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

标签:#ev

找到 5163 篇相关文章

AI 资讯

Stop Leaking API Keys: The Backend for Frontend (BFF) Pattern Explained

👉 TL;DR: Frontend applications (SPAs, mobile apps, desktop clients) cannot securely store secrets: any embedded API key is extractable by users and attackers. The Backend for Frontend (BFF) pattern solves this by placing a server-side layer between your frontend and third-party APIs. The BFF holds the secrets; the frontend never sees them. For production deployments, use a secrets manager (AWS Secrets Manager, HashiCorp Vault) rather than environment variables to enable rotation and auditing. A BFF adds infrastructure complexity, but for any API key with financial or administrative implications, the tradeoff is worth it. Frontends are notoriously leaky environments. Cybernews found in 2022 that 56% of Android apps on the Google Play Store contained hardcoded secrets extractable through basic automation. A similar study in 2025 concluded that iOS apps are not better, with over 815,000 secrets harvested from 156,000+ apps (71% leaking at least one credential). These studies plainly expose the widespread issue of hard-coding secrets in production-deployed frontend code. This article aims to warn developers about this risk and present a simple, reusable pattern for safeguarding their applications: the Backend for Frontend (BFF) pattern. Before we start, let's be clear on the crucial point: Whether you are building a React Single Page Application (SPA), a mobile app, or a desktop client, if the code runs on the user's device, the user (and potential attackers) can always inspect it. The solution isn't to try and hide the keys better ; it's to move them somewhere safe. "Public Clients" vs. "Confidential Clients" In OAuth terminology, there are two types of clients, with completely different security models : Confidential Clients : Applications running on a secure server (e.g., a Node.js backend, Python API) that can securely store secrets (like a CLIENT_SECRET) because end-users don't have access to the server's file system or memory. Public Clients : Applications running

2026-08-13 原文 →
AI 资讯

MCP 2026-07-28 from the server side: Codex already speaks it, Claude doesn't yet

On July 28, the Model Context Protocol project shipped a new spec revision, 2026-07-28 . I run backend engineering at GoodBarber, and our public MCP server is a live production surface: real apps, real content, real push notifications. So for us a new revision is not a changelog to skim on a Friday. It is a migration with our name on it. We have just brought the server up to the new revision. This post is three things: the operator's cut of what changed, what upgrading a public server actually involves, and the thing we found in our logs while checking the work. The last one is the reason I'm writing. The operator's cut of 2026-07-28 The headline is the stateless core. MCP grew up as a stateful, bidirectional protocol: an initialize handshake, a negotiated session, an Mcp-Session-Id header to carry it all. The new revision retires that entirely. Every request now self-describes in _meta : protocol version, client identity, capabilities. The practical consequence is the one server operators have wanted since day one: you can put an MCP server behind a plain round-robin load balancer with no shared session storage. If you have ever kept session affinity alive with duct tape, you know exactly which muscle just relaxed. The rest, fast: Method and tool names now also travel in Mcp-Method and Mcp-Name HTTP headers, so gateways can route and meter without parsing JSON bodies. Multi Round-Trip Requests: a call can come back with resultType: "input_required" and continue over stateless connections. Mid-call questions no longer need a held-open stream. List results (tools, prompts, resources) carry ttlMs and cacheScope , so clients can finally cache your inventory honestly instead of guessing. Authorization hardening: RFC 9207 issuer validation, and Client ID Metadata Documents replacing Dynamic Client Registration. Tasks, MCP Apps, and Enterprise Managed Authorization become formal extensions instead of core features. Roots, Sampling, and Logging are deprecated, with a minim

2026-08-13 原文 →
AI 资讯

A static site that collects form submissions, in one HTML attribute

A static site has no backend. That is the point of one — and it is also why the contact form is the first thing that breaks. The usual answers are a third-party form service with its own signup, a serverless function you now maintain, or a mailto: link nobody clicks. There is a third option that falls out of how static hosting already works: the host is in the path of every HTML response it serves. It can collect the form itself. On harvis.dev that is one attribute: <form harvis-form= "contact" > <input name= "email" type= "email" required > <textarea name= "message" ></textarea> <button> Send </button> </form> Deploy, and submissions show up in the dashboard. No script tag, no API key in the page, no fetch() , no JavaScript at all — the form works with JS disabled, because it is a plain HTML form doing what plain HTML forms have always done. The page I am describing is live at harvis-forms-example.harvis.dev — submit the form and see where you land. Everything below is what makes that page work. What actually happens The rewrite happens on the way out, while the HTML is being served: action and method are replaced with /__harvis/form/contact on your site's own subdomain. Same origin, so there is no CORS, no preflight, and nothing in the page has to know a project id. A honeypot field is inserted. It is positioned off-screen rather than display: none , because a bot that skips hidden inputs is a bot that would otherwise get through. Anything that fills it in gets the success page and is stored nowhere — a bot that can tell it was caught is a bot that tries again differently. data-harvis-redirect="/thanks.html" becomes a hidden field, since the handler never sees your HTML — only what the browser posts. It is re-validated on arrival, and a protocol-relative //somewhere-else is refused. The reply is a 303 , so the browser follows it with a GET and a refresh on the thank-you page cannot post the form twice. The form name is part of a URL and a dashboard heading, so it

2026-08-13 原文 →
AI 资讯

Persisting Claude CLI Login Between Container Builds

Goal Keep Claude Code's account/session login ( ~/.claude.json ) alive across devcontainer rebuilds, instead of having to re-authenticate every time the image is rebuilt. The problem Claude Code keeps two things on disk: ~/.claude/ — a directory, already persisted via a named Docker volume ( claude-playwright-setup ). ~/.claude.json — a single file holding account/session state, which was not persisted. Every container rebuild wiped it, forcing a fresh login. Normally you'd just mount a named volume onto the whole folder the state lives in, the same way .claude/ , .copilot/ , and .continue/ are already handled. That's not an option here: .claude.json isn't inside its own subfolder, it sits directly in $HOME alongside everything else ( .bashrc , .ssh/ , .profile , ...). Mounting a volume onto $HOME itself to catch one file would shadow all of that, so the file has to be persisted on its own. Mounting a named volume straight onto the file path ( claude-json-...:/home/container-user/.claude.json ) seems like the next-simplest option, but it breaks on this Docker Desktop setup: mount ... not a directory: Are you trying to mount a directory onto a file A named volume's backing store is always a directory. Docker is supposed to detect that the mount target is a single file and copy the image's file into the volume so it ends up binding file-to-file. On this Docker Desktop that detection fails — the volume comes up as an empty directory, and runc then tries to bind that directory onto the file path and crashes at container start. This was confirmed by deleting the volume and rebuilding the image from scratch, so it isn't a stale-cache artifact. The fix Never mount a volume directly onto a single file. Instead, mount it onto a directory — the same shape already used for .claude / .copilot / .continue — and symlink the dotfile into that directory from the Dockerfile. Dockerfile.debian : USER container-user .... RUN mkdir -p /home/container-user/.claude-json && \ touch /home/

2026-08-13 原文 →
AI 资讯

I need one picture that shows where the money goes

Someone in every company eventually says this out loud. Usually it's the CFO. Sometimes it's a VP of engineering, or the unlucky engineer who got handed "own our cloud costs" on top of their actual job. The bill comes in, it's up again, the spreadsheet has eleven tabs, and someone finally says: "Stop. I don't want another spreadsheet. I need one picture that shows where the money goes." It's a completely reasonable request. It's also strangely hard to satisfy with the tools most teams already have. This post is about why, where the money usually turns out to be going, and what that one picture actually looks like. The bill answers "how much". The question is "where" A cloud bill is a flat table — a very big one. An AWS Cost and Usage Report can run to millions of rows, and every row is precise: this resource, this hour, this rate. If your question is "how much did we spend on EC2 in July", the tools answer instantly. But "where does the money go" is a different kind of question. A dollar enters the company as one line on an invoice and then travels: through a provider, into an account, into some kind of resource, and finally — ideally — onto somebody's team. It's a path, not a number. Flat tables don't show paths. Native tools slice one dimension at a time. Cost Explorer will show you spend by service. Or by linked account. Or by one tag. Each view is true, and each view is a dead end, because the question in the meeting is always a path through several dimensions at once: which team's non-prod environments, in which account, are driving the compute growth? Answering that with one-dimensional views means six tabs and a join you perform in your head. The join in your head is where the meeting dies. So people fall back to the spreadsheet. Someone brave builds a pivot table; it's accurate for a week, then a re-org or a new account lands and it quietly becomes fiction that everyone still forwards. Where the money usually goes We look at a lot of cloud bills. The leaks a

2026-08-13 原文 →
AI 资讯

You know what's worse than not being able to log in?

This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry . You Know What's Worse Than Not Being Able to Log In? Being told everything worked right up until you try to actually use your account. Yes, that was a real bug. And, somehow, I ended up being pulled into another authentication mystery. At this point, I’m starting to think authentication bugs have a personal grudge against me. 😅 In my previous Smash Story , I wrote about a bug where users simply couldn't log in. This time, the problem was sneakier because most of the flow looked completely healthy. The user was approved, the background task ran, the email and SMS arrived, and Cognito had a user. Then the user actually tried to use their account. And everything fell apart. It Started With Two User Pools The authentication setup was fairly large and had evolved over time, so there wasn't one shiny User Pool doing everything. We had an older Cognito User Pool supporting existing authentication flows, including mobile-based signup, while a newer User Pool handled a newer flow where users received an email containing their PIN. Both pools were intentional because they supported different parts of the authentication journey. That wasn't the problem. The interesting part was that the application database had its own representation of a user, while Cognito had another. On top of that, some of the work connecting those two systems happened asynchronously. As long as everyone agreed about who the user was, nobody cared. The moment they disagreed, authentication became very interested. The Tiny Timing Window The problem appeared in the partner and dependant journey. A member could create a partner or dependant during signup or later from the member details area. A relevant non-member user would then approve the account, which scheduled an asynchronous task called SendingEmailsAfterApprovalBot in a TaskList database table. That task ran every 15 minutes, and once it executed, the partner or depend

2026-08-13 原文 →
AI 资讯

I built a free, no-signup AI text toolkit - here's the stack and why

I kept hitting the same small friction: I'd want to quickly rewrite an email, clean up some text, or summarize a long thread — and every tool wanted me to sign up, pick a plan, or watch an ad first. For a ten-second task, that's absurd. So I built the thing I wanted: a set of free, no-signup AI text tools , each doing one job well. This is a quick write-up of the stack and the decisions behind it. 👉 Live: https://www.texttoolsai.app The core idea: one tool, one job, zero friction Instead of a single mega-app, it's a collection of single-purpose tools — rewrite, tone change, summarize, prompt generation — each on its own page. You land, paste, get output. No account, no modal, no paywall. The "no signup" rule forced good constraints: everything has to work instantly and statelessly, which kept the whole thing simple. The stack Next.js (App Router) — server components for the content/SEO pages, client components only where the tool actually needs interactivity. Vercel for hosting — the deploy story is boringly good, which is what you want. An LLM API on the backend — the browser never sees a key; requests go through a Next.js route handler that owns the prompt and the provider call. Tailwind for styling — fast to iterate, easy to keep consistent across dozens of tool pages. One decision that paid off: data-driven pages Every tool is defined as a config object (label, placeholder, system prompt, endpoint) rather than a hand-built page. Adding a new tool is mostly adding data, not wiring up new routing. That's what made it realistic to ship a lot of tools without the codebase turning into spaghetti. // simplified shape { slug: 'rewrite', label: 'Paste your text', endpoint: '/api/tools/rewriter', systemPrompt: '...' } The route handler resolves the endpoint key against a map of system prompts, so the API surface stays tiny even as the tool count grows. What I'd tell anyone building something similar Keep the API key server-side. Obvious, but easy to leak through a miscon

2026-08-13 原文 →
AI 资讯

KEDA 3.0 Scale-to-Zero: How We Cut Intermittent Kubernetes Workload Costs to Almost Nothing

KEDA 3.0 just landed, and the headline feature is the one I care about most as someone who watches a cloud bill: event-driven autoscaling now covers 80+ event sources (Kafka, RabbitMQ, and a long list more) with proper scale-to-zero. If you run workloads that sit idle most of the day and spike when work arrives, this is the difference between paying for capacity you use and paying for capacity that waits. I have been moving our intermittent workloads onto this pattern, so here is what scale-to-zero actually does to the bill, where it helps, and the sharp edges nobody mentions. The problem: HPA scales to one, not to zero Standard Horizontal Pod Autoscaler has a floor. minReplicas cannot be zero, so a workload that processes a queue twice a day still keeps at least one pod (and often the node under it) running 24/7. For a consumer that is busy 2 hours a day, you are paying for 22 hours of nothing. KEDA changes the shape of the question. Instead of "how many replicas does current CPU justify," it asks "are there events waiting." No events, zero pods. Events arrive, it scales from zero up to whatever the load needs. That floor of zero is the whole game for intermittent work. Where scale-to-zero actually pays off Not every workload benefits. The ones that do share a profile: bursty, event-triggered, and tolerant of a short cold start. In our environment the clear wins were: Queue consumers. A worker draining an SQS or RabbitMQ queue that fills a few times a day. Idle 80%+ of the time, now scales to zero between bursts. Kafka stream processors for low-volume topics that only see traffic during business hours. Scheduled batch jobs dressed up as long-running services because nobody wanted to re-architect them. Scale-to-zero gets most of the savings without the rewrite. Dev and staging consumers that had no reason to run overnight and did anyway. A rough sizing rule I use: if a workload is idle more than half the day and an extra few seconds of latency on the first event is

2026-08-13 原文 →
AI 资讯

How Artificial Intelligence Disrupts Engineering Progression

AI is disrupting career progression by eliminating the learning opportunities at each rung while simultaneously enabling people to perform above their experience level, Alasdair Allan explained in his talk Engineering Progression When AI Ate the Middle at QCon London. Fewer junior developers join the industry, and AI slows hiring at the entry level. By Ben Linders

2026-08-13 原文 →
AI 资讯

One Prompt Can Make a Game Demo. That Is Not the Same as Making a Game.

A playable first-person shooter generated from one prompt would have sounded absurd not long ago. Now, videos of AI-built browser games that resemble Call of Duty and Counter-Strike are spreading across social media. On August 10, Axios reported on the rise of “one-shot” AI game prompting : give a model one detailed instruction, let it produce the code, and receive something you can play. This is a real milestone. It is also easy to misunderstand. A one-prompt game can prove that a model knows how to assemble controls, graphics, physics, enemies, and a recognizable game loop. It cannot prove that the result will stay interesting after the first few minutes. The first prompt creates the demo. The decisions after that create the game. Why These Demos Feel So Important Game ideas used to face a large gap between imagination and interaction. You could describe a mechanic, draw a map, or write a design document. But discovering whether the idea actually felt good required code, assets, an engine, and enough technical work to reach a playable build. Prompt-to-game tools are shrinking that gap. This change is not limited to experimental AI demos. Roblox recently announced mobile-first creation tools that turn text prompts into basic games , giving creators a starting point they can playtest, change, share, and publish. That starting point matters. A playable failure teaches you more than a beautiful design document. You can immediately discover that the movement is slow, the arena is empty, the objective is confusing, or the central mechanic is less interesting than it sounded. The value of one-shot generation is not that the first result is finished. It is that the first result arrives early enough to challenge your assumptions. A Recognizable Game Is Not Necessarily a Good Game A model can generate the visible parts of a familiar genre surprisingly well. Ask for a browser FPS and it may produce: First-person movement Weapons and ammunition Enemies that chase or shoot Hea

2026-08-13 原文 →
AI 资讯

Building epilot Apps from your terminal, with a little help from AI agents

A few months ago we shipped the epilot CLI , and it quietly became one of my favorite tools. One command, npx epilot , gives you every single epilot API operation in your terminal: entities, journeys, workflows, pricing, files, permissions, 50+ APIs. Interactive pickers if you're exploring, --json and --no-interactive if you're scripting. It also turned out to be a perfect match for AI agents like Claude. Agents are great at driving CLIs: they discover operations, read the help, make calls, parse the JSON. No custom integration or MCP server needed, the CLI is the integration. And because handing an agent live CRM access is a scary idea, the CLI ships with two safety nets, both enforced server-side: # A session that physically cannot write. The restriction is baked # into the token, so the bearer can't turn it off. epilot auth login --readonly # A token that additionally gets all PII anonymized in every response epilot access-token createAccessToken -d '{ "name": "AI agent token", "read_only": true, "anonymize": true }' Read-only plus anonymized means an agent can explore, analyze and report on your real org all day, and the worst it can do is read data it can't even de-anonymize. Now we've made the CLI even better. On top of the raw API commands, we added app facades : a set of high-level epilot app commands that take you from an empty folder to a working app installed in your org. And that's what this post is really about, because apps are where the fun is. What are epilot Apps? epilot is very configurable out of the box: journeys, workflows, automations, pricing. But at some point every team hits a wall, something the UI simply doesn't offer. A custom tab on the contact or opportunity page showing data from your own systems A whole custom page in the epilot navigation Your own block in the journey builder A widget in the end-customer portal A flow action that calls your API when a workflow step runs An external product catalog or an API proxy to your backend That

2026-08-13 原文 →
AI 资讯

Your rate limiter is broken behind a tunnel — the X-Forwarded-For problem

You put your app behind a tunnel (or any reverse proxy) to test webhooks. Everything works. Then you notice something odd in your logs: every single request comes from the same IP address. Congratulations, you've met the X-Forwarded-For problem. What actually happens When a request flows through a tunnel, the TCP connection to your app comes from the relay, not the real client. So request.remote_addr — the value your framework uses for rate limiting, IP logging, geo-blocking, brute-force detection — is the relay's address. For every request. From every user. The consequences are quiet and nasty: Your rate limiter now rate-limits the relay, not the client. One aggressive user trips the limit and everyone gets blocked. Or worse, the limit is per-IP and effectively unlimited, because each relay node looks like one "user." * Your access logs are fiction. Security review of an incident? Every entry says the same address. * IP allowlists silently break. "Only allow my office IP" now allows nothing, or everything, depending on how it's wired. The fix (and its trap) The proxy already tells you the real client IP — in the X-Forwarded-For header. Every framework has a setting to trust it. Flask: ProxyFix . Express: app.set('trust proxy', ...) . Rails, Django, Laravel: equivalents exist. Here's the trap: trust that header blindly and anyone can spoof it. A client can send X-Forwarded-For: 1.2.3.4 directly, and if your app believes headers from anyone, your rate limiter is bypassed with a curl flag. The correct setup has two halves: 1. Trust `X-Forwarded-For` only when the immediate connection comes from a proxy you control (your tunnel relay, your load balancer). 2. Strip or ignore the header on direct connections. Most frameworks express this as "trusted proxies" — a list of proxy IPs whose forwarded headers you believe. Set it. It's five minutes of config that determines whether your security features are real or decorative. Why this matters more in the tunnel era Tunnels us

2026-08-13 原文 →
AI 资讯

I Built HackForPinas to Make Philippine Hackathons Easier to Discover

In my previous article, I talked about Train Track, the transit app I built around Metro Manila's railway systems. This project started with a completely different problem. I kept thinking about how difficult it can be to discover hackathons and coding competitions. Not because they don't exist. They do. The problem is that they're scattered everywhere. A university might announce one. A government agency might host another. A private company might run one. A developer community might post another. And suddenly you're checking multiple websites just to figure out: What can I actually join? So I built HackForPinas. What is HackForPinas? HackForPinas is a free, public, and open-source directory for Philippine: Hackathons Coding challenges Technology competitions The idea is pretty straightforward: Make opportunities easier to discover. Events can be filtered by: Region Format Organizer type Status Organizers are categorized as: Government University Private Instead of browsing through unrelated websites, users can explore opportunities in one place. But the more I worked on it, the more I realized that the directory itself wasn't the hardest part. The data was. The Data Problem Imagine trying to collect hackathons from different websites. One might have an RSS feed. Another might use WordPress. Another might expose an API. Another might have an ordinary HTML page. And another might not have anything structured at all. So HackForPinas uses multiple scraping strategies: WordPress REST API RSS GDG Community Eventbrite HTML + Cheerio The scraper runs through a background endpoint and collects events from different Philippine technology sources. The interesting part wasn't: "Can I scrape a website?" It was: Can I turn information from completely different sources into one consistent dataset? That became a much more interesting engineering problem. I Didn't Want Anyone to Publish Directly There's another problem with a public directory. If anyone can submit an event, what s

2026-08-13 原文 →
AI 资讯

# I Built My Developer Portfolio as Peter Parker's Lab 🕷️

I could have built another developer portfolio. You know the one. Dark background. Glowing buttons. "Full Stack Developer | AI | Cloud | DevOps" Six project cards. GitHub link. Done. But honestly, that doesn't feel like me. Before I was interested in AI, software engineering, cloud, automation and all the other things I keep breaking and rebuilding, I was just a kid who loved Spider-Man. And the older I got, the more I realized that I didn't actually relate to Spider-Man because he was a superhero. I related to Peter Parker . The curious kid. The awkward kid. The kid who builds things. The kid who experiments. The kid who fails and somehow keeps going. That felt familiar. So when I started building my portfolio, I wanted it to represent that. I called it: 🧪 Peter Parker's Lab The idea is that my portfolio is basically my digital lab. A place where I can show what I'm building, what I'm learning and what I'm experimenting with. 🕷️ Peter Parker → curiosity 🕸️ Spider-Man → persistence 💻 Developer → everything I'm building today And honestly, "lab" describes my development journey pretty well. I build something. It breaks. I investigate why. I fix it. Then I get another idea and break something else. 😂 That's the fun part. I'm currently interested in building things around: AI AI agents automation full-stack applications developer tools cloud infrastructure DevOps local-first software I'm not trying to pretend I've mastered all of it. I'm trying to keep learning by building real things . That's what I want this portfolio to show. Not just a list of technologies. Not just a list of GitHub repositories. But the problems I'm curious about and the things I'm actually trying to create. 🌐 Peter Parker's Lab https://peterparker-lab.vercel.app/ This is version one. I'll keep changing it as I change. New projects. New experiments. New ideas. Probably new bugs too. Because maybe the best portfolio isn't one that says: "Look how much I know." Maybe it's one that says: "Look what I

2026-08-13 原文 →
AI 资讯

CSS Just Got a Parent Selector. Your Forms Will Never Look the Same

For as long as I've been writing CSS, there's been one direction it refused to look: up. You could style a child based on its parent all day long, but the second you wanted a parent to react to something happening inside it — a checked checkbox, an invalid field, a filled-in input — you were reaching for JavaScript. Every time. It didn't matter how small the interaction was. :has() breaks that rule on purpose, and it's been safe to use in production for a while now — it's supported across Chrome, Edge, Firefox, Safari, and Opera, no polyfill required. I didn't fully appreciate what that meant until I rebuilt a form I'd been maintaining for two years and deleted most of the JavaScript in it. Not all of it — I'll get to where it still earns its place — but most. The rule CSS used to have /* This has always worked: style a child based on the parent */ .card.featured .title { color : gold ; } /* This has never worked, until :has(): style the parent based on a child */ .card :has ( .badge--sold-out ) { opacity : 0.6 ; } :has() reads as "select this element, if it contains a match for whatever's inside the parentheses." Once that clicks, a huge category of things people were writing classList.toggle() calls for turns into a single selector. Styling a label when its input is focused This used to mean a focus and blur listener on the input, toggling a class on the label. Now: .field :has ( input :focus ) { border-color : var ( --accent-color ); box-shadow : 0 0 0 3px color-mix ( in srgb , var ( --accent-color ) 25% , transparent ); } Wrap the label and input in a .field container, and the whole field lights up the moment the input inside it gets focus — no listener, no class toggle, and it can never drift out of sync with the actual focus state, because it is the actual focus state. Required-field indicators that can't go stale I've fixed this bug more times than I want to admit: a form gets a field added, and someone forgets to also add the little red asterisk that's suppo

2026-08-13 原文 →
AI 资讯

Astro 7: Rust Compiler, Rust Markdown Pipeline and Vite 8 for Builds Up to 61% Faster

Astro 7 focuses on build performance, utilizing native tooling and a rewritten compiler in Rust. The new version includes faster Markdown processing and stricter HTML rules. Recent updates introduced advanced routing and incremental builds, while issues around legacy file compatibility and dependency counts were raised in feedback. Astro targets content-driven sites with minimal JavaScript. By Daniel Curtis

2026-08-13 原文 →
AI 资讯

Stop Comparing AI Coding Tools by Autocomplete Quality

The biggest mistake in choosing an AI coding tool is comparing autocomplete latency. Cursor and Windsurf are editors with agent abilities. Claude Code works mostly through a terminal on your local repository. GitHub Copilot spans IDEs, GitHub, code review, and a cloud agent. Replit Agent connects generation to a hosted environment where the app actually runs. CodeGeeX provides affordable IDE help for Chinese-language development. They execute in different places. That means a single "best AI coding tool" ranking is a category error — the right question is where the AI should run your work. The four execution models IDE assistants and agentic editors (Cursor, Windsurf, Copilot IDE features, CodeGeeX) stay close to your current edits. Feedback is immediate, and you stay in control of scope. The cost is that complex work still consumes your attention, and two overlapping AI editor subscriptions rarely make sense — run a two-week crossover pilot and keep one. Local terminal agents (Claude Code) read repositories, edit files, and run commands on your machine. This fits debugging, dependency migrations, and test loops. The security docs describe a read-only default with permission requests, and you should keep that default: start read-only, smallest directory, no broad allowlists for network, deletion, or deployment commands. Cloud coding agents (GitHub Copilot cloud agent) work in an ephemeral Actions-powered environment and come back with commits or a pull request. Good for bounded issues, tests, and docs. Budget is not just the seat — AI credits and Actions minutes are separate. Hosted application environments (Replit Agent) go from natural language to a running prototype in the browser. Great for education and proof-of-concept. Test git import/export, database migration, and code export before you depend on it. Quick decision table Primary workflow Evaluate first Main risk Frequent coding inside one AI editor Cursor Editor migration; broad changes still need review Cr

2026-08-13 原文 →
AI 资讯

The Case of the Lying Clock: 5 Vue Mysteries Solved

Every detective has their cold cases. These are mine — five Vue concepts that confused me until I investigated them properly. Grab your magnifying glass. Case #1: The Lying Clock Imagine you set an alarm to go off every 60 seconds. You press start at 10:00:00. First alarm: 10:01:00 — perfect Second alarm: 10:02:00 — still good But your phone is also doing other things: checking email, refreshing weather, running background tasks. Sometimes it fires the alarm a tiny bit late. Third alarm: 10:03:01 (1 second late) Fourth alarm: 10:04:02 (2 seconds off now) Five hours later: your alarm fires at 15:05:12 when it should fire at 15:05:00 That gap growing bigger over time — that's drift . The timer slowly slides away from where it should be. Why Does This Happen? JavaScript runs on a single thread — it can only do one thing at a time. When a timer is supposed to fire, the browser puts it in a queue. But if the thread is busy doing something else, the timer waits. The MDN documentation for setTimeout lists several reasons timers fire late: Nested timeouts are throttled to a minimum of 4ms after 5 levels of nesting (per the HTML5 spec ) Background tabs are throttled to a maximum of once per second ( MDN : "timeouts are throttled to firing no more often than once per second (1000 ms) in inactive tabs") Chrome 88+ introduced intensive throttling for hidden pages: timers that have been hidden for more than 5 minutes are checked only once per minute Tracking scripts in Firefox get even more aggressive throttling: 10 second minimum in background tabs Does This Happen on New Devices Too? Yes, but less. Modern devices are faster, so the delay per tick is smaller — maybe 1-2 milliseconds instead of 10-20. But over hours, even 1ms per tick adds up. And background tab throttling happens on every device, no matter how fast — it's a browser policy, not a hardware limitation. Is This Common Knowledge? It's the kind of thing you learn when your boss says "why does the clock on our dashboa

2026-08-13 原文 →
产品设计

Font Preview Is a Classification Problem, Not a Beauty Contest

An Arabic type preview becomes more useful when it helps a designer classify intent. “Which one looks best?” is too vague. The better question is whether the phrase needs readability, geometry, ceremony, ornament, handwriting energy, or poetic flow. Six familiar script directions make that distinction visible: Naskh, Kufic, Thuluth, Diwani, Ruq'ah, and Nastaliq. They should not be treated as interchangeable decorations. Each changes the apparent density, rhythm, and purpose of the same phrase. Define the intent before rendering A preview UI can ask for a short design intent alongside the text: type PreviewIntent = | ' readable ' | ' structural ' | ' ceremonial ' | ' ornamental ' | ' informal ' | ' poetic ' The mapping is not a legal or historical verdict, but it is a practical starting point. Naskh commonly supports readable, balanced text. Kufic emphasizes geometry and strong outlines. Thuluth benefits from display scale and ceremonial space. Diwani creates dense, flowing ornament. Ruq'ah feels direct and handwritten. Nastaliq's descending rhythm is especially relevant to Persian and Urdu presentation. Preview the real phrase Specimen text hides problems. A designer should render the actual name, quotation, or headline because letter combinations alter width, joins, baseline rhythm, and negative space. The same style can feel excellent for a short name and crowded for a longer sentence. The comparison surface should keep content constant while changing one variable at a time. That makes differences attributable to the font direction rather than color, size, or wording. Respect right-to-left layout Font selection and layout cannot be separated. A preview needs explicit RTL direction, sensible alignment, and enough room for vertical and descending forms. Nastaliq in particular may need more line height than a compact Latin-oriented component assumes. .preview { direction : rtl ; text-align : right ; overflow : visible ; } This looks elementary, but many design tools

2026-08-13 原文 →