AI 资讯
Laravel Middleware Execution Order Explained: Why Your Middleware Runs in the Wrong Order
Laravel middleware can be perfectly written and still behave unexpectedly. You may notice authentication running too late, permission checks failing, tenant initialization not working, logging middleware missing important data, or custom middleware executing in an order you didn't expect. In many cases, the middleware code itself is not the problem. The real issue is middleware execution order. Understanding how Laravel executes middleware is critical when building secure and scalable applications because every request passes through multiple layers before reaching your controller. Common Symptoms You may encounter problems such as: Authenticated users being treated as guests Permission middleware failing unexpectedly Tenant information not being available Request logging missing user details Rate limiting triggering before authentication Redirect loops after login Middleware appearing to be ignored completely These issues are often caused by middleware running in the wrong sequence. How Laravel Processes a Request A typical Laravel request follows this flow: Browser ↓ Global Middleware ↓ Middleware Group (Web/API) ↓ Route Middleware ↓ Controller ↓ Response ↓ Browser Each middleware layer can inspect, modify, allow, or block the request before it reaches your application logic. Because of this, execution order matters. Example Problem #1 Suppose you have two middleware: Authenticate User Log User Activity Your logging middleware expects an authenticated user. $user = auth()->user(); However, the log always shows null. Why? Because the logging middleware executes before authentication. The solution is ensuring authentication middleware runs first so user information is available when logging occurs. Example Problem #2 Multi-tenant applications often initialize tenant information through middleware. TenantMiddleware If another middleware accesses the database before tenant initialization, queries may use the wrong database connection. This can lead to: Incorrect data
AI 资讯
How I Built a Free AI Image Tool That Runs 100% in the Browser (No Server Needed)
I recently built a free online image processing tool that runs entirely in the browser. No uploads, no servers, no sign-ups. Here's how it works under the hood. https://img.aixiaot.com The Problem Most online image tools require uploading your photos to someone else's server. This raises privacy concerns and limits file sizes. I wanted to build something that processes everything locally. Tech Stack - Next.js for the frontend - TensorFlow.js + Real-ESRGAN for AI upscaling - @imgly/background-removal for AI background removal - Tesseract.js for OCR - Canvas API for compression, resizing, format conversion Features • AI Background Removal - one click, works for portraits, products, animals • Image Compression - reduce file size up to 96% • Format Conversion - JPG, PNG, WebP • ID Photo Maker - passport and visa photos with customizable backgrounds • AI Image Upscaler - 2x to 8x with Real-ESRGAN • OCR - extract text from images, 20+ languages • Image Resizer - enlarge or shrink Architecture All processing happens client-side using WebAssembly and the Canvas API. When you upload an image, it never leaves your device. The AI models (background removal, upscaling) run locally in your browser using TensorFlow.js and ONNX Runtime Web. Open Source The entire project is open source under AGPL v3. You can find it on GitHub: https://github.com/haizeigh/ai-image-tools Try It https://img.aixiaot.com I'd love to hear your feedback! What features would you add?
AI 资讯
How I Organize 10,000+ Prompts Across Projects
One question I get surprisingly often is: "How do you manage thousands of AI prompts without losing track of them?" The answer is simple. I don't treat prompts as conversations. I treat them as reusable software assets. Over the years, I've created prompt libraries across multiple AI projects, books, research initiatives, and client work. That means managing well over 10,000 prompts covering everything from Python development and AI agents to content generation and workflow automation. If you're still storing prompts in random ChatGPT conversations, you're making life much harder than it needs to be. Here's the system that works for me. Stop Thinking of Prompts as Temporary Most people write a prompt, get an answer, and move on. That's fine for casual use. But builders rarely solve the same problem only once. If you find yourself writing: API documentation SQL queries FastAPI endpoints Docker configurations Code reviews Git commit messages ...you're probably solving recurring problems. Recurring problems deserve reusable prompts. My Folder Structure Instead of organizing prompts by AI tool, I organize them by purpose. For example: AI-Prompts/ │ ├── Python/ │ ├── FastAPI │ ├── Django │ ├── Flask │ └── Automation │ ├── JavaScript/ │ ├── React │ ├── Node.js │ └── TypeScript │ ├── DevOps/ │ ├── Docker │ ├── Kubernetes │ └── GitHub Actions │ ├── AI/ │ ├── RAG │ ├── Agents │ ├── MCP │ └── Prompt Engineering │ └── Documentation/ This mirrors how software projects are organized. Finding a prompt takes seconds. Every Prompt Has Metadata A prompt isn't just text. It's documentation. Each prompt in my library includes: Category: Purpose: Model: Input: Expected Output: Version: Last Updated: For example: Category: FastAPI Purpose: Generate CRUD endpoints Model: GPT-4o Expected Output: Production-ready FastAPI code Six months later, I know exactly why that prompt exists. I Version My Prompts Developers version code. Why not prompts? For example: FastAPI_CRUD_v1.md FastAPI_CRUD_v
AI 资讯
When (and when not) to inline images as Base64
Base64 image data URIs are one of those web techniques that look like a magic shortcut the first time you use them. Instead of referencing an external file: <img src= "/logo.png" alt= "Logo" > you can put the image bytes directly in the document as text: <img src= "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..." alt= "Logo" > That can be useful. It can also make a page slower, harder to cache, and more annoying to maintain. Here is the practical rule: inline images as Base64 when self-containment matters more than caching. Keep normal image files when the browser should be able to cache, resize, lazy-load, or optimize them independently. What a Base64 image actually is An image file is binary data. Base64 rewrites that binary data as plain text using a limited character set. To make the browser treat the text as an image, you wrap it in a data URI: data:image/png;base64,iVBORw0KGgoAAAANSUhEUg... The first part tells the browser the MIME type. The second part tells it the data is Base64 encoded. The long tail is the image itself. Base64 is not compression. It is not encryption. It is just a text representation of the same bytes. When inlining an image is worth it 1. Tiny icons and UI assets For very small images, removing an extra HTTP request can be worth the extra bytes. This is especially true for small icons, logos, placeholders, simple UI sprites, or tiny transparent PNGs. Modern HTTP/2 and HTTP/3 make extra requests cheaper than they used to be, so this is not an automatic win. But for a one-off tiny asset inside a small page or widget, a data URI can still be a clean choice. 2. Single-file deliverables Sometimes the point is not raw page speed. Sometimes you need one file that carries everything with it: an HTML report an email template a CodePen or demo snippet a CMS block where you cannot upload assets a test fixture that should not depend on external hosting In those cases, Base64 is useful because the image travels with the HTML, CSS, JSON, or JavaScript.
AI 资讯
The same root cause keeps coming back because nobody tracks it. I built a zero-dep CLI that does.
You write the postmortem. You file the action items. Everyone nods, the doc gets archived, and life moves on. Six months later, the exact same root cause takes down the exact same service — and nobody in the room remembers the first incident, let alone that its fix never actually shipped. "We use rootly to track this automatically. It flags when incidents have the same root cause as previous ones." That's a real answer from an SRE thread about this exact problem — and it's a paid, hosted feature of a full incident-management platform. Most teams don't have rootly or incident.io. What they have is a folder of markdown postmortems that nobody diffs against each other. So I built rootecho : a zero-dependency CLI that does the one useful thing those platforms do for this — flag when a new incident's root cause echoes a past one, and show you whether that past incident's action items ever actually got finished. How it works Each postmortem is one JSON record — free-text root_cause and/or curated root_cause_tags , plus action_items with a status: { "id" : "INC-2026-014" , "title" : "Payment webhook retries exhausted" , "root_cause" : "webhook retry queue misconfigured to drop after 3 attempts, no dead-letter fallback" , "root_cause_tags" : [ "webhook" , "retry-queue" , "dead-letter" , "config" ], "action_items" : [ { "id" : "AI-1" , "description" : "Add dead-letter queue for webhook retries" , "owner" : "alice" , "status" : "open" } ] } rootecho add records it and compares against your history: $ rootecho add inc-2026-014.json ⚠ root cause echo detected for "INC-2026-014": INC-2026-003 (2026-03-15) — 100% similar root cause Payment webhook retries exhausted ✓ Add retry backoff [done] ✗ Add monitoring alert for queue depth [open] — 93d overdue → 1 action item(s) from this past incident were never finished. recorded to .rootecho/history.jsonl That's the whole point of the tool in one output: not just "you've seen this before," but "and here's the fix that never happened." r
AI 资讯
LINQ and ZLinq in the Unity 6 Era: Avoiding GC Allocations in Large-Scale Projects
Introduction In large-scale Unity development, GC Alloc can quietly become a real problem. At first, nothing looks wrong. But as the project grows and you add more enemies, UI, master data, events, states, notifications, logs, and other systems, small allocations that happen every frame begin to pile up. LINQ is especially convenient. var aliveEnemies = enemies . Where ( x => x . IsAlive ) . OrderBy ( x => x . DistanceToPlayer ) . ToList (); It is readable. But if this kind of code runs every frame, it can become a source of both GC Alloc and CPU overhead. Unity's official documentation also recommends reducing frequent managed heap allocations as much as possible, ideally getting close to 0 bytes per frame. https://docs.unity3d.com/2022.3/Documentation/Manual/performance-garbage-collection-best-practices.html For general GC Alloc best practices, this article refers to the Unity 2022.3 documentation, because the general guidance still applies. Unity 6-specific GC behavior is covered later using the Unity 6.0 documentation. This article assumes Unity 6.0 as the minimum Unity version and explains how to choose between regular LINQ and ZLinq in production code. Unity 6.0 uses the Roslyn C# compiler, and its C# language version is C# 9.0. However, some C# 9 features, such as init-only setters, are not supported. https://docs.unity3d.com/6000.0/Documentation/Manual/csharp-compiler.html The short version The point of this article is not to ban LINQ completely. Do not use LINQ in hot paths just because it is readable. Do not assume ZLinq solves everything just because you introduced it. Those are the two main ideas. A rough guideline looks like this: Area Guideline Editor extensions, build scripts, debug code Regular LINQ is usually fine Startup, loading, initialization LINQ can be fine, but measure when data size is large Update / LateUpdate / FixedUpdate Avoid LINQ by default Code that is not per-frame but still called frequently Consider ZLinq Code that materializes res
AI 资讯
5 things that surprised me building on HMRC's Making Tax Digital API
I spent the last while building the HMRC integration for TapTax , a Making Tax Digital (MTD) app for UK sole traders. MTD is the UK government's programme that pushes tax filing out of paper and spreadsheets and into software talking directly to HMRC's APIs. I have integrated with a fair few third-party APIs. Stripe, Plaid-style banking, the usual. HMRC is its own animal. Some of it is genuinely well designed, some of it caught me completely off guard, and a couple of things cost me a full day each before the penny dropped. So here are the five things that surprised me most. Each one is the surprise, then the fix, with a short snippet from our actual TypeScript backend. Not tax advice, just engineering notes from someone who has now stepped on the rakes so you do not have to. 1. The API version lives in the Accept header, and getting it wrong is a 406 Most APIs version in the URL: /v2/thing . HMRC versions through content negotiation. You ask for a version in the Accept header, like application/vnd.hmrc.5.0+json , and if you ask for a version that endpoint does not serve, you get a 406 Not Acceptable . No helpful "did you mean v3" message. Just 406. The part that bit me: different endpoints are on completely different versions at the same time. Obligations is on v3.0, the self-employment cumulative summary is on v5.0, calculations are on v8.0, ITSA status is on v2.0. There is no single "current" version to pin. The fix was to make the version a required argument on the request wrapper so you can never forget it, and set it per call: // src/services/hmrcApi.ts const headers = { Authorization : `Bearer ${ accessToken } ` , Accept : `application/vnd.hmrc. ${ apiVersion } +json` , // e.g. "5.0" ... hmrcConfig . getFraudHeaders ( req ), }; One more trap: versions get withdrawn. Obligations used to answer on v2.0; that now returns a 404, not a 406, so it looks like a missing resource rather than a stale version. When an HMRC call 404s, check the version before you go hunt
AI 资讯
Anatomy of an API scrape: reading 251 requests like a crime scene
Last week someone tried to copy my visa API's database. They didn't succeed — they got 0.6% of it before I cut the key — but the 251 requests they left behind are a near-perfect teaching case for what targeted API extraction actually looks like from the defender's side. Here's the forensic walkthrough. The target One endpoint: GET /api/v1/visa?from={passport}&to={destination} It returns the visa rule for a passport→destination pair — visa type, allowed stay, conditions. The full matrix is ~39,585 pairs . That matrix is the product. The evidence The attacker's requests weren't spread across the map. They were a sweep, one passport at a time: Passport Destinations pulled Coverage 🇦🇪 UAE (ARE) 195 ~100% of that passport's matrix 🇦🇺 Australia (AUS) 53 ~1/4, interrupted 🇨🇳 China (CHN) 2 test calls 249 unique pairs, near-zero duplicates. Whoever wrote this was methodical: validate that one full passport comes out cleanly, then move to the next. Reading the cadence The timestamps are where a scrape gives itself away. Minute by minute: 11:56 2 ← test phase (incl. the one failure) 11:57 1 11:58 25 ┐ 11:59 26 │ 12:00 20 │ ~25 req/min, dead regular … │ = one request every ~2.4s 12:07 21 ┘ No human reads visa rules on a 2.4-second metronome for 11 minutes. This is a loop. The fingerprint Four signals — and the point isn't nationality, it's that the request parameters themselves leaked the intent: Handle: visadb_scraper . It signed its own work. Email: throwaway @temp.com . No intention of receiving anything. Languages: en + zh , on a product with no Chinese-market surface yet. Error signature: the very first call (CHN→THA, in Chinese, 11:56:45) failed, then everything ran clean. Classic "calibrating the script" tell. The math 250 records is 0.6% of the base. At 25 req/min, a full dump would've taken ~26 hours . This wasn't a dump — it was a feasibility test . They proved a whole passport comes out easily, then stopped, nowhere near the 3,000/month free-tier ceiling. What I coul
AI 资讯
Como implementar OTP (código de confirmação) por WhatsApp no Brasil
Guia prático para adicionar verificação por código OTP via WhatsApp oficial no seu sistema, com exemplos em Node.js, PHP e Python — e comparação honesta de custos entre WhatsApp, SMS e e-mail Como implementar OTP (código de confirmação) por WhatsApp no Brasil Se você tem um cadastro, login ou checkout, em algum momento vai precisar confirmar que o usuário realmente controla o número de telefone que informou. Esse é o trabalho do OTP ( One-Time Password , ou senha de uso único): você envia um código, o usuário digita, você confere. No Brasil, mandar esse código por WhatsApp costuma ser melhor que por SMS — mais gente lê, entrega mais e custa menos. Neste post eu mostro como implementar isso na prática, com código que roda, e comparo os canais de forma honesta (inclusive citando alternativas pagas). Por que WhatsApp e não SMS? Critério WhatsApp oficial SMS E-mail Entregabilidade Alta Média Baixa (cai em spam) Taxa de leitura ~98% ~90% ~20% Custo por envio ~R$ 0,03 R$ 0,08–0,15 Baixo, mas pouco lido Copiar código Botão nativo Manual Manual O SMS ainda é um bom fallback para quem não usa WhatsApp, mas como canal principal de OTP no Brasil, o WhatsApp ganha na maioria dos casos. ⚠️ Use sempre a API oficial do WhatsApp (WhatsApp Business Platform) , não automação de WhatsApp Web. Automação não oficial derruba a entrega e corre risco de bloqueio pela Meta. O fluxo em 2 passos Toda implementação de OTP tem a mesma forma: Enviar o código ( send ) → você gera um código e manda pelo canal. Verificar o código ( verify ) → o usuário digita e você confere. O detalhe importante: a resposta do send confirma que a mensagem foi aceita , mas a entrega no aparelho é assíncrona. Para OTP isso não é problema — a própria verificação já é a prova de entrega . Se o usuário digitou o código certo, chegou. Você não precisa de webhook nem de polling de status. Implementando com uma API pronta Você pode falar direto com a WhatsApp Business Platform, mas isso exige aprovação de template, gestão
AI 资讯
Stop pasting JWTs into random websites
A JWT isn't just JSON you can inspect. It's a live bearer token. Here's a safer way to decode one. A few days ago I was reviewing a bug with a teammate. They wanted to see what was inside an access token, so they copied it into the first JWT decoder Google returned. It wasn't a dummy token. It was a production access token with almost an hour left before it expired. Nobody was trying to do anything risky—it was just the quickest way to inspect a JWT. That's exactly why this keeps happening. The thing people forget A JWT looks like this: header.payload.signature The payload isn't encrypted. It's just Base64URL-encoded JSON. Because of that, people often think: "The payload isn't secret, so the token is probably safe to paste." Those aren't the same thing. The payload may be readable, but the token itself is still your credential . Anyone holding it can usually authenticate as you until it expires. Why online decoders make me nervous Some JWT tools only decode locally in your browser. Others offer things like signature verification, claim validation, or key management. Features like those often require talking to a backend, which means the token gets sent somewhere else. Maybe the site is trustworthy. Maybe it isn't. From the UI alone, you usually can't tell. Even if a decoder claims everything runs client-side, I don't like assuming that's true when I'm holding a production credential. You don't need a website to inspect a JWT Most of the time I'm only interested in the payload anyway. echo " $TOKEN " \ | cut -d '.' -f2 \ | base64 --decode \ | jq Because JWTs use Base64URL encoding, you may need to translate the alphabet and add padding first: decode_jwt () { local payload = $( echo -n " $1 " | cut -d . -f2 | tr '_-' '/+' ) while [ $(( ${# payload } % 4 )) -ne 0 ] ; do payload = " ${ payload } =" done echo " $payload " | base64 --decode | jq } decode_jwt " $TOKEN " That gives you the claims, expiration time, issuer, audience—everything most people open a decoder for.
AI 资讯
Diff from the live server, not from your git history — when a local repo has drifted from production
An investigation agent flagged "the license API PHP returns Japanese-hardcoded messages" and we sat down to fix it. But something felt off the moment we opened the file — the version running on the production server didn't match the latest commit in the local repo . Stranger still, production had more recent features than our local checkout . A bit of digging turned up the truth: months earlier, someone had hot-patched the production file in response to a different user issue, and that change had never been committed back to git . This post walks through how we detected that drift, and the two-stage strategy we used to merge production back into the local repo safely. How this regression silently slips in If we'd written the fix on top of our local repo and uploaded it to production, here's what would have happened: all the production-only improvements get overwritten and quietly disappear . In our case, the production file had a half-year-old language-handling addition for the "Early Bird Bonus" feature — when a USD customer buys, client_name is set to 'Early Bird Bonus' ; for JPY customers it's '早期利用特典' . None of that existed in our local git. A normal PR-merge-and-deploy cycle would have silently rolled back the Early Bird i18n logic , regressing English users' display back to Japanese. Catching this was half luck. Opening the file to start the fix, I noticed code I didn't recognize, ran git blame , and the lines were nowhere in git history . That's when alarm bells went off. Two-stage rollforward — make production the source of truth first The strategy we landed on was a two-stage merge. Stage 1 (rollforward sync) : Pull the production file straight into the local repo. Apply the diff in the "production → local" direction, not the other way . After this, the local repo's HEAD matches what's actually running on production. # Pull the production file into the local repo scp -i ~/.ssh/key layer2024@host:wpmm.jp/public_html/license/api/register_free.php \ /tmp/regis
AI 资讯
Ng-News 26/16: OpenNG Foundation, spartan/ui
OpenNG Foundation and spartan/ui 1.0 are the headline topics this week: a new home for libraries like Spectator and Elf, and spartan/ui, a stable shadcn-inspired component library for Angular. Also in brief: Storybook's Angular modernization through AnalogJS, the end of ng-conf, and AI Dev Craft in Las Vegas. OpenNG Foundation Maintaining open-source libraries is hard work. Developers often do it in their spare time, committing to years of maintenance, adding new features, and responding to user requests. Last episode, we reported that the ngneat organization was taken down for unknown reasons. While we still don't know why it happened, a new home has emerged for its popular libraries like Spectator and Elf: the OpenNG Foundation. Gerome Grignon, known for CanIUseAngular and as the organizer of Ng-Baguette, announced the foundation, which is already hosting these libraries. Alongside Gerome, the current OpenNG team also includes Dominic Bachmann, organizer of Angular Lucerne and author of the angular-typed-router library. OpenNG Foundation · GitHub OpenNG Foundation has 8 repositories available. Follow their code on GitHub. github.com spartan/ui 1.0 spartan/ui has officially released its 1.0 version. It provides an "accessible, production-ready library of more than 55 components" with fully customizable styling. After debuting in August 2023 with 30 primitives, it now reaches stable in 2026 with a modern architecture built around signals, standalone components, zoneless change detection, and SSR. Originally initiated by Robin Götz, a full team quickly formed around the project. spartan/ui can be seen as the Angular equivalent to shadcn/ui, famous for its customizability. While similar open-source alternatives exist, spartan/ui was the pioneer and has a proven track record of active maintenance over the years. Announcing spartan/ui 1.0 Robin Goetz Robin Goetz Robin Goetz Follow for Playful Programming Angular Jun 24 Announcing spartan/ui 1.0 # angular # webdev 8 reac
AI 资讯
Fable 5 got jailbroken again
Fable 5 got jailbroken again Researcher Vitto Rivabella tested Fable 5’s defenses and managed to find a bypass. According to him, most attempts failed. The protection is multi-layered: the model checks the prompt, conversation history, system context, and its own response. Some filters run during generation and can stop the answer halfway through. The checks are not based on keywords. The system looks at meaning, intent, language, wording, and suspicious chains of requests. The bypass took around 20 hours. It required rare languages, academic framing, long build-ups, Unicode, breaking the task into parts, and working with the chain of thought. The author did not get a stable bypass for long tasks. According to him, regular search is faster and cheaper.
AI 资讯
Block Google's AI Overviews at the Network Layer, Not the DOM
TL;DR: Most extensions block Google's AI Overviews by hiding the panel with a content script after it renders — fragile, flickery, and always a step behind Google's markup changes. A better approach: force udm=14 at the network layer with declarativeNetRequest , so the AI Overview never loads. The content script becomes a backstop, not the main mechanism. One Chrome API mystery — AI Mode being invisible to four different extension APIs — shows why the DOM was never the right layer. Google puts an AI Overview at the top of most search results now, and a lot of people would rather it didn't. So there's a whole shelf of Chrome extensions that remove it. Almost all of them work the same way, and I think that way is a mistake. The obvious approach, and why it's a trap The default move is DOM-hiding: inject a content script, wait for the AI Overview panel to render, find it by class name or attribute, and set display: none . It's the first thing anyone reaches for, and it works — until it doesn't. The problems are all baked into the approach. You're reacting after the render, so there's a flash of AI content before your script catches it. You're matching against Google's markup, which is obfuscated and reshuffled constantly, so every layout change is a silent breakage. And you're paying for DOM churn on a page you don't control. You end up in a permanent game of catch-up against a page that changes whenever Google feels like it. The deeper issue is that you're operating one layer too high. The panel is a symptom . By the time it's in the DOM, the work is already done — the server decided to send it, the page rendered it, and now you're scrambling to un-render it. If you can move the decision earlier, none of that scramble has to happen. The thesis: prevent it at the network layer Google Search takes a parameter, udm , that selects which result vertical you get. udm=14 is the plain "Web" results view — the classic list of links, no AI Overview, no AI Mode. It's Google's ow
AI 资讯
The Hugging Face Hub Is a Free JSON API: Rank Trending AI Models Without a Key
Everyone reads the Hugging Face trending page in a browser. Almost nobody knows the whole Hub sits behind a plain JSON API with no key, no login, and cursor pagination. If you want a weekly report of what the AI community is actually adopting, you can build it with fetch . The endpoints GET https://huggingface.co/api/models GET https://huggingface.co/api/datasets GET https://huggingface.co/api/spaces Useful parameters, same across all three: sort ranks results: trendingScore , downloads , likes , createdAt , lastModified direction=-1 for descending search matches names, author restricts to one org like meta-llama filter matches Hub tags: text-generation , license:mit , even arxiv:2606.23050 limit up to 100 per page So the top trending models right now: https://huggingface.co/api/models?sort=trendingScore&direction=-1&limit=100 trendingScore is the interesting one. Downloads and likes rank all time popularity, which is dominated by the same old models. Trending score is Hugging Face's own measure of current momentum, and it moves daily. Today it puts a four day old OCR model from Baidu at the top, which no downloads sort would surface for weeks. Slim payloads with expand By default the models endpoint returns a siblings array listing every file in the repo, which bloats a 100 item page. Ask for exactly the fields you want instead: const fields = [ ' downloads ' , ' likes ' , ' trendingScore ' , ' pipeline_tag ' , ' tags ' , ' createdAt ' ]; const params = new URLSearchParams ({ sort : ' trendingScore ' , direction : ' -1 ' , limit : ' 100 ' }); for ( const f of fields ) params . append ( ' expand[] ' , f ); const res = await fetch ( `https://huggingface.co/api/models? ${ params } ` ); const models = await res . json (); Pagination is a Link header There is no page parameter. Each response carries a Link header with a cursor for the next page, GitHub style: function nextUrl ( res ) { const m = ( res . headers . get ( ' link ' ) || '' ). match ( /< ([^ > ] + ) >; \s *r
AI 资讯
I Launched an AI-Built Board Game — Here's What Happened Next
Not long ago I wrote about how I built a browser-based board game called "Growing City" in three days using AI — and how the hardest part wasn't the code at all. Some time has passed, and I wanted to share what happened next. Layout Bugs While vibe-coding solo, I only tested on my own screen, resolution, and browser. The problem surfaced as soon as real users joined with different setups: some people saw everything misaligned, some things got clipped, some cards overlapped each other. This is how it looked on some screens I had to rewrite the layout to use adaptive sizing so the game looks correct regardless of screen resolution. It should work now — but if something still looks off on your end, let me know and I'll fix it. Bots Started Talking Another change, unrelated to bugs. The service started feeling more alive. Previously, bots just played: rolled dice, bought cards, said nothing. Now they react in the chat to what's happening in the game — if someone's building gets taken, if someone buys an expensive card or runs out of money. It's a small thing, but the game feels noticeably more lively. An empty game with silent bots versus a session where someone's commenting on what's happening in chat — it's a meaningfully different experience, even though the game itself is the same. Thank You to Early Players A special thanks to everyone who tried the game after my first article. And extra thanks to a user with the nickname SHAM, who pointed out that the game rules never said you can't buy multiple purple cards in a row — even though the game itself has that restriction. Fixed! What's Next The project is still going. I'm thinking about ads and other ways to bring in players. Without new users, it's hard to get feedback — and without feedback, it's hard to know what to fix or improve first. The unit economics don't quite work out yet: paid acquisition costs more than I'm willing to invest at this stage. I'll keep figuring it out. If you have ideas on how to find playe
AI 资讯
I Cut My LLM Bill 40x and Rewrote Nothing: A CTO's Migration Story
Here's the thing: i Cut My LLM Bill 40x and Rewrote Nothing: A CTO's Migration Story Six months ago my CFO slid a single line item across the table. OpenAI: $4,800 for the month. I'd like to say I was surprised, but I'd been watching the number climb for two quarters. What actually surprised me was how little it took to bring that number down to under $200 without anyone on my engineering team writing new code, without a single regression, and without telling my customers anything had changed. This is the story of how we did it, what we evaluated, what broke, and what I'd tell any other CTO walking into the same conversation with their finance lead. The Real Cost of Vendor Lock-In I've been a CTO long enough to recognize the pattern. You pick a vendor. The vendor becomes the default. Procurement assumes you're locked. Your engineers build abstractions around their quirks. Six months later nobody can tell you what it would actually cost to switch because the switching cost has become invisible. It's just "how we do things." OpenAI was that vendor for us. GPT-4o handled our summarization pipeline, our customer support copilot, and a few internal tools I'd hacked together on a Saturday. We were paying $2.50 per million input tokens and $10.00 per million output tokens. At our volume, those numbers add up faster than you'd think because the output side balloons in conversational workloads. Here's the arithmetic that should scare every CTO: at $10/M output, every million tokens of generated text costs a dime on the dollar. If your product generates a 1,000-token response for 100,000 users a day, that's 100 million tokens a day, which is $1,000 a day in output alone. That's $30,000 a month. Just for one feature. The 40x claim I keep seeing isn't marketing spin. DeepSeek V4 Flash charges $0.18/M input and $0.25/M output. Do that math against GPT-4o and the comparison is brutal. Multiply your current OpenAI output spend by 0.025 and you'll get the rough number you'd pay for
AI 资讯
I Built a Board Game in 3 Days with AI — and Realized Code Was the Easiest Part
I love board games — especially the kind you can play without leaving home. You just call your friends, drop a link, and you're playing in minutes. At some point, I caught myself wondering: how realistic is it to build a complete game almost entirely with AI? Not a prototype, but something actually playable. I decided to find out. Three days later, I had a working browser-based board game: rooms, multiplayer, bots, chat, full game sessions. But the most interesting thing turned out to have nothing to do with AI writing code. What's the Game? The game is called "Growing City" (Растущий город). It's an economic board game about developing your own city. Each turn, players roll a die, buildings activate, income flows in, and you earn money to buy new structures. Gradually you build up enterprises, construct your economic engine, and race to complete all the key buildings before your opponents. You can play directly in the browser with no registration. I wanted the simplest possible entry: open the site, enter a nickname, create or join a room. If the mechanics seem familiar — you're not imagining it. I was inspired by a well-known city-building board game. Day 1: AI Really Can Write Games I'm not a developer. I work in tech, but I don't code professionally. Over the past few months I've been experimenting heavily with vibe coding, so I decided to build this project the same way. I didn't start with code at all. First, I wrote out the mechanics in detail: what cards exist, how a turn plays out, what should happen in each situation. Once the logic settled, I started gradually converting the description into code using AI. Day 2: Writing the Game Was Just the Beginning When the first playable version appeared, it quickly became clear that the code was far from the hardest part. The biggest problem was balance . If you leave everything as-is, players find the single most profitable strategy within a few games and repeat it endlessly. I had to manually tweak card costs, adj
AI 资讯
How I designed a Premium Dark Mode Hotel PMS Dashboard (HTML/CSS)
When looking for a Property Management System (PMS) dashboard for a hotel project, I noticed most existing solutions look like they were built in 1998. I decided to code a modern, premium dashboard from scratch using pure HTML and vanilla CSS. I focused on two main design trends: Dark Mode and Glassmorphism. Here is a breakdown of how I approached the design, along with some CSS snippets you can use in your own projects. The Dark Mode Color Palette Instead of using pure black (#000000), I used a deep slate blue for the background. This reduces eye strain for hotel staff working night shifts and feels much more premium. `css :root { --bg-dark: #0f172a; /* Deep slate / --surface-dark: #1e293b; / Slightly lighter surface / --accent-gold: #facc15; / Premium gold for CTAs */ --text-main: #f8fafc; } body { background-color: var(--bg-dark); color: var(--text-main); }` The Glassmorphism Effect For the statistics cards (like Revenue and Occupancy Rate), I used a subtle glass effect to make them pop off the dark background without looking flat. `css .stat-card { background: rgba(30, 41, 59, 0.7); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 16px; padding: 24px; transition: transform 0.3s ease; } .stat-card:hover { transform: translateY(-5px); }` The Result By combining these modern design tokens with a clean CSS Grid layout, the dashboard feels incredibly sleek. It tracks live bookings, room statuses, and RevPAR seamlessly. Want the full code? If you are a developer, agency, or freelancer building a SaaS or a booking system, you don't have to start from scratch. I've packaged the complete, fully responsive HTML/CSS template. You can see the design and grab the source code here to save yourself 20 hours of coding: 👉 Download the Lumina PMS Template Happy coding! Let me know if you have any questions about the CSS architecture in the comments.
AI 资讯
The Hidden Cost of Unplanned Work (And How to Protect Your Sprint)
Every sprint starts with optimism. The board is clean, the story points are perfectly balanced, and the team is ready to ship. Then, Tuesday happens. The CEO wants a "quick favor." A major client finds a critical bug in production. The marketing team urgently needs a landing page tweak. By Thursday, your pristine sprint board is buried under a mountain of "urgent" tickets that were never discussed in planning. This is Unplanned Work , and it is the silent killer of engineering velocity. Why Unplanned Work is So Dangerous It’s not just that unplanned work takes time. The real damage comes from context switching . When a developer is deeply focused on building a new feature, forcing them to stop, spin up a local environment for a different repository, debug a legacy issue, and then try to return to their original task destroys their flow state. A "10-minute quick fix" actually costs the company an hour of lost productivity. When this happens multiple times a week: Deadlines Slip: The tasks you actually committed to get pushed back. Burnout Increases: Developers feel like they are working hard but accomplishing nothing. Trust Erodes: Management wonders why the team can't stick to a timeline. How to Protect Your Team You cannot eliminate unplanned work completely. Bugs will happen, and production will break. But you can manage it. 1. The "Firefighter" Rotation Instead of letting unplanned work disrupt the entire team, assign one developer per sprint to be the "Firefighter" (or Batman/Support). Their only job for that sprint is to handle urgent bugs, ad-hoc requests, and unblock others. The rest of the team is completely shielded. 2. The 20% Buffer Rule If you have 100 hours of developer capacity, never plan 100 hours of feature work. Always leave a 20% buffer specifically for unplanned tasks. If no fires start, you can pull from the backlog. If fires do start, your deadline isn't destroyed. 3. Track the "Ghost" Tickets The worst kind of unplanned work is the kind that h