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

标签:#p

找到 15201 篇相关文章

AI 资讯

What I keep seeing working with crypto companies under MiCA

I run brand and product work for crypto and fintech companies, and this year the same request keeps landing on my desk, worded slightly differently each time: we don't want to look like crypto anymore. It comes from payment companies, exchanges, stablecoin startups — the ones that spent years looking like "the future" and now want to look like a bank. Or rather, a neobank. The first thing they ask to kill is the gradient. This isn't taste finally maturing. It's regulation. Under MiCA you can't operate in European crypto without a license, and a licensed company that still looks like a 2021 DeFi protocol has a problem its lawyers can't fix. So the whole industry is quietly repainting itself toward "trustworthy." Here's the trap I keep watching people walk into. The gradient everyone's fleeing is already being replaced by a new monoculture — the same off-white, the same restrained type, the same calm. Swapping a gradient for clean sans-serif feels like progress because it looks like the companies that already won (Stripe, Coinbase). But you're not them, and wearing the surface of a trusted brand doesn't make you inherit the trust. It's just a different uniform. The escape route became a traffic jam. The deeper issue: the audience flipped. For 15 years crypto brands were built for insiders who chose crypto because it wasn't a bank. The dark dashboard and the "to the moon" energy were tribe signals. But a licensed company now answers to regulators, banks, institutions, and normal people moving their salary — none of whom read a glowing gradient as "innovative." They read it as "unregulated." Same brand, overnight liability. And the part most people skip: trust isn't a color. It's spread across every surface you own, all the way down to the transaction detail nobody thinks about. A clean homepage in front of a 2021 dashboard isn't progress — it's a tell. The repackaging that works goes all the way down: the same restraint and clarity from the cold email to the onboarding

2026-06-26 原文 →
AI 资讯

1,200 Applications. 4 Offers. Here's What Actually Got Me the Product-Based Role

I am going to start with a number most people will not say out loud. 1,200 applications. That is how many jobs I applied to over 3 to 4 months trying to switch from a service-based company to a product-based one. I had spreadsheets, saved searches, and browser tabs I kept telling myself I would close tomorrow. Some nights I was applying at 11pm just to hit my self-imposed daily quota. Out of 1,200, I got around 10 interview calls. Out of 10, I got 4 offers. The applications got me in the room. What happened inside the room is what this post is actually about. The One Thing That Followed Me Into Every Interview At my previous company I worked on a lot of things, but one project came up in literally every single interview. We had a Python module that parsed ASAM MDF files. Binary log files from vehicles and sensors, often gigabytes in size. The parser was painfully slow. Around 8 minutes to load a single file. The kind of slow where you start it, go get lunch, and hope it is done when you come back. I rewrote it in Rust. Load time dropped from 8 minutes to 12 seconds. 40x improvement on GB-scale files. Every interviewer stopped me the moment I mentioned it. The questions were real engineering questions, not generic resume stuff. "Why Rust over Go or C++?" "How did you profile the bottleneck first?" "What was your testing strategy when rewriting something this critical?" "What would you do differently now?" I would spend 20 to 30 minutes just on this one project. Not because they were grilling me. Because it was a genuine conversation between two people who cared about the problem. Here is why it worked: I had lived with it. I hit walls in the rewrite that took days to figure out. The context, the wrong turns, the eventual solution were all stored in my head. When a follow-up question came, the answer was just there. You cannot fake that. A first follow-up question exposes a tutorial project immediately. Real work under real constraints creates a depth that no amount o

2026-06-26 原文 →
AI 资讯

Record of Site Issues #2 - Playback / GOP

Environment And Situation Control room of an apartment Number of installed product : 3 (PC-based NVR, dual-LAN supported) Remote support : X (I actually went to the site and diagnosed) Reported Issue In viewer, when user changes play speed while playing back the recorded data, it randomly plays the data in hyper speed(almost 30x~60x) For example: 4x play means 4 seconds in video per a second. But in the site, it played 30~60 seconds per a seconds, showing the video stutturing. Diagnosis Checked the overall environment. System(CPU / RAM usage), network environment(bandwidth), resoulution, stream configurations, etc. -> Nothing suspicious. Some of the installed cameras had unusual fps and gop values Normally, fps and gop values are set to be equal(for exmaple, if fps is 30 then gop is also 30 so that iframe can appear every second) But the cameras' set up values were fps 15, gop 60(iframe per 4 seconds) Assumption Somehow the viewer keeps failing to find iframe to play. And it's maybe because iframe appears with a long gap. Quick note: iframe is kind of a key-frame. Since the viewer starts decoding from an iframe, it's necessary when it comes to playback. What I Tried Set all the cameras' gop value to 15(same as fps) Result Ran a test with data before changing the gop values and after. During interval before changing the gop, the issue occurred almost every time I tried. But after chaning the gop, the issue no longer occurred. Concolusion The issue was triggered by large GOP value (GOP 60 with FPS 15). With only one iframe every four seconds, the viewer sometimes failed to find an appropriate iframe after changing the playback speed, causing abnormal playback behavior. According to the viewer developer, this is likely related to the viewer's iframe searching logic, which is still under investigation. Keep This In Mind Check camera settings(especially gop and fps) first when it comes to playback issue. Always check before/after data to confirm assumption.

2026-06-26 原文 →
AI 资讯

Java LLD: Designing Snakes and Ladders with O(1) Move Resolution

Java LLD: Designing Snakes and Ladders with O(1) Move Resolution Designing Snakes and Ladders is a classic LLD (Low-Level Design) interview question that tests your ability to write clean, maintainable, and highly performant code. While the rules are simple, naive implementations quickly fall apart under scale, concurrency, or changing business requirements. Want to go deeper? javalld.com — machine coding interview problems with working Java code and full execution traces. The Mistake Most Candidates Make Expensive Runtime Scans : Iterating through lists of snakes and ladders on every single move, turning an $O(1)$ lookup into a slow $O(N)$ search. Violating SRP : Hardcoding board mechanics, game loops, and dice rolling logic inside a single monolithic class. Tight Coupling : Binding player movement directly to the dice, making it incredibly difficult to introduce custom game rules (e.g., crooked dice or extra turns). The Right Approach Core mental model : Treat the board as a flat, pre-computed $O(1)$ lookup array where each index represents a cell and its value represents the final destination. Key entities/classes : Board , Jump (representing Snakes/Ladders), Player , Dice , Game , and MovementStrategy . Why it beats the naive approach : It decouples board setup from game loop execution, turning expensive runtime lookups into instantaneous array access. The Key Insight (Code) public class Board { private final int [] board ; // Pre-computed jump destinations public Board ( int size , List < Jump > jumps ) { this . board = IntStream . range ( 0 , size + 1 ). toArray (); jumps . forEach ( j -> board [ j . start ()] = j . end ()); // Precompute O(1) lookups } public int resolvePosition ( int current , int roll ) { int next = current + roll ; return next < board . length ? board [ next ] : current ; } } Key Takeaways DP-Style Precomputation : Pre-populating a lookup array transforms runtime search complexity from $O(N)$ to $O(1)$ time complexity per turn. Open-Closed

2026-06-26 原文 →
AI 资讯

tgo Devlog #3: Taming Context Windows, Compiling Lodash, and the Repetitive Reality of True Ownership

I’ve been making massive headway on tgo, my TypeScript to Go compiler library, but it is forcing me to confront some hard realities about how I manage systems, AI, and even people. The Cost of Scaling Complexity Since the last devlog, I’ve added full support for Node libraries— fs , path , process , and a few others. Right now, I’m in the trenches trying to compile Lodash, argparse , and date-fns . I pushed date-fns to the side for a minute because Lodash is proving to be the perfect stress test. It is, frankly, obnoxious. In some cases, the code is just very poorly written. Lodash has 316 different entry points. Right now, 122 are failing. But dealing with this massive, complex library has forced me to completely overhaul my test runner. I’ve built it so that you can choose specific entry points and compile only what you need—similar to how ES bundle works. I’ve also implemented heavy caching. If you are continually rebuilding, it won't re-compile the source to Go every single time; it just handles the binary compilation unless something actually changed. It’s significantly faster. But as this project scales, the sheer complexity is threatening to break the system—and by the system, I mean the AI I am using to build it. Process is Survival I do most of this development through AI, and getting an LLM to consistently output good software engineering without breaking existing features is incredibly difficult. I was constantly blowing out the context window. Even at 200,000 tokens, it wasn't enough. By the time the AI figured out what to do, it would start summarizing the context and immediately start doing a terrible job. This forced me to narrow down all possibilities. I realized there are really only four things I am ever asking the AI to do: Update the test runner. Fix a bug. Implement a new feature. Work on a library. That’s it. I defined strict workflows for those four pathways. If I ask it to fix a bug, it has to run the specific test, read the JavaScript, read

2026-06-26 原文 →
开发者

Array Methods in JS - Part 2

JavaScript Array Search Methods What are Array Search Methods? Array Search Methods are used to: Find the position (index) of an element. Check whether an element exists. Retrieve an element that satisfies a condition. Find the index of an element that matches a condition. Search from the beginning or the end of an array. Common Array Search Methods Method Purpose Returns indexOf() Finds the first occurrence of a value Index or -1 lastIndexOf() Finds the last occurrence of a value Index or -1 includes() Checks whether a value exists true / false find() Finds the first matching element Element or undefined findIndex() Finds the index of the first matching element Index or -1 findLast() (ES2023) Finds the last matching element Element or undefined findLastIndex() (ES2023) Finds the last matching index Index or -1 1. Array.indexOf() Definition The indexOf() method searches an array for a specified value and returns the index of its first occurrence . If the value is not found, it returns -1 . Syntax array . indexOf ( searchElement ) array . indexOf ( searchElement , startIndex ) Parameters Parameter Description searchElement Value to search for startIndex (optional) Index where the search starts Returns Index of the first matching element. -1 if not found. Internal Working Suppose: let fruits = [ " Apple " , " Orange " , " Mango " , " Orange " ]; Memory: Index 0 → Apple 1 → Orange 2 → Mango 3 → Orange When: fruits . indexOf ( " Orange " ); JavaScript starts from index 0 : Apple ❌ Orange ✅ Found Stops immediately and returns: 1 Example let fruits = [ " Apple " , " Orange " , " Banana " ]; console . log ( fruits . indexOf ( " Orange " )); Output 1 Example - Not Found let fruits = [ " Apple " , " Orange " ]; console . log ( fruits . indexOf ( " Mango " )); Output -1 Example - Start Position let fruits = [ " Apple " , " Orange " , " Banana " , " Orange " ]; console . log ( fruits . indexOf ( " Orange " , 2 )); Output 3 Real-Time Example Suppose an e-commerce site wants to

2026-06-26 原文 →
AI 资讯

Can We Talk About the "AI/ML Engineer" Shortcut for a Second?

Lately, it feels like my feed is completely flooded with "Become an AI/ML Engineer in 2 Hours!" crash courses and quick certificates promising a golden fast-track into machine learning roles. But let’s be completely real for a second: there are no tutorial shortcuts here. The more I dive into actual system architecture and cloud infrastructure, the more obvious it becomes: machine learning isn't a standalone magic trick. It's built entirely on rock-solid Computer Science, efficient data structures, and heavy-duty software engineering. Software Engineering First, AI Second If you can’t build or scale a reliable backend, manage data pipelines, or understand low-level underlying system logic, you simply cannot scale an AI model in production. Prompt engineering is cool for prototyping, but production-level ML requires real, foundational engineering skills. You have to learn how to be a great software engineer first. Looking Past the Hype (A Solid Structural Roadmap) If you actually want to look past the superficial fluff and understand how real data workloads, model deployments, and ML infrastructure fit into a cloud environment, I found an incredibly solid, structured resource. Instead of hand-waving past the hard parts, Microsoft Learn has an official, step-by-step breakdown on Azure AI and Machine Learning Fundamentals. It actually goes into the core architectural principles and shows you what real cloud-scale infrastructure looks like. Whether you are trying to map out your summer learning roadmap or just want to understand the actual systems backing these models, I highly recommend checking it out. Here is the structured entry point if you want to skip the shortcuts and dive into the real infrastructure: 🔗 Official Azure Machine Learning Technical Hub What are your thoughts? Are you seeing the same "AI shortcut" hype on your feeds, or are people finally starting to focus back on core system fundamentals? Let's discuss in the comments!

2026-06-26 原文 →
AI 资讯

JavaScript Arrays Methods - Part 1

What is an Array? An Array is a special object in JavaScript used to store multiple values in a single variable. Instead of creating separate variables, let student1 = " John " ; let student2 = " David " ; let student3 = " Alex " ; we can use an array: let students = [ " John " , " David " , " Alex " ]; Each value inside the array is called an element , and every element has an index starting from 0 . Index : 0 1 2 ------------------------- Array : | John | David | Alex | ------------------------- 1. Array length Definition The length property returns the total number of elements present in an array. It is not a function . It is a property of an array object. It is also writable, meaning you can change the length to increase or decrease the array size. Syntax array . length To modify the array length: array . length = newLength ; Parameters None. Returns Returns a number representing the total number of elements in the array. Internal Working Consider this array: let fruits = [ " Apple " , " Orange " , " Mango " ]; Memory representation: Index 0 → Apple 1 → Orange 2 → Mango length = 3 When JavaScript creates the array, it internally stores a special property: { 0 : "Apple" , 1 : "Orange" , 2 : "Mango" , length: 3 } Whenever you access: fruits . length JavaScript simply returns the value stored in the length property. It does not count the elements every time. This makes length very fast. Example 1 let fruits = [ " Apple " , " Orange " , " Banana " ]; console . log ( fruits . length ); Output 3 Example 2 - Updating Length let numbers = [ 10 , 20 , 30 , 40 ]; numbers . length = 2 ; console . log ( numbers ); Output [ 10 , 20 ] JavaScript removes the remaining elements. Example 3 - Increasing Length let colors = [ " Red " , " Blue " ]; colors . length = 5 ; console . log ( colors ); Output [ "Red" , "Blue" , empty × 3 ] The new positions become empty slots . Real-Time Example Imagine an E-commerce Shopping Cart . let cart = [ " Laptop " , " Mouse " , " Keyboard " ]; co

2026-06-26 原文 →
AI 资讯

MCP Is More Useful as Context Distribution Than as RPC

Most discussions around MCP focus on tool calling. That is natural. When people first see MCP, the obvious use case is simple: Let the AI call external tools. A model can read a GitHub issue. A model can query a database. A model can update a file. A model can call an API. In that sense, MCP looks like an RPC layer for AI agents. That is useful. But I think it may not be the most important use of MCP. The more interesting use is this: MCP can distribute context, rules, skills, and operating contracts to AI clients. In other words, MCP is not only a way for AI to call tools during work. It can also be a way to define the working environment before the work starts. The problem with RAG RAG is usually used to answer this question: What information might be relevant to this request? The system searches documents, retrieves chunks, and gives them to the model. This works well for many cases. But it has structural limits. RAG retrieves likely relevant information. It does not necessarily define how the work should be done. For team-level AI work, this is a problem. A team does not only need information. A team also needs shared rules. For example: What is the authoritative source? What should be treated as unknown? When should the AI stop? When is human confirmation required? What is the closure condition? Which workflow should be used? Which domain skill applies? What evidence must be recorded? RAG can retrieve documents that describe these rules. But retrieval is not the same as governance. A retrieved chunk is just context. It is not necessarily an operating contract. The problem with local prompts Many teams try to solve this with prompts. They write instructions like: Follow our coding rules. Use this design document. Ask questions when unclear. Do not make risky changes. This helps, but it does not scale well. Each developer may have a different local prompt. Each AI client may load a different file. Each repository may contain a slightly different version of the ru

2026-06-26 原文 →
AI 资讯

MCP Server Auth: The API Is the Real Boundary

A single shared API key is fine right up until a second person uses it. intent-brain — the system, repo qmd-team-intent-kb , renamed to the intent-brain plugin v0.4.0 this day — is a team knowledge base. A Fastify HTTP API sits over a governed memory corpus. In front of that API is an MCP server named teamkb , so a teammate doesn't open a dashboard or learn an endpoint. They ask in Claude Code and get a cited answer back with qmd:// citations. That's the whole pitch: institutional memory you query in the same place you write code. Up to this day it authenticated with one shared TEAMKB_API_KEY . The shared key has two failures that only show up once the tool has more than one user. First, every request looks identical, so the audit log can't say who asked. Second, revoking one person means rotating the key for everyone — there's no per-person handle to drop. Both are structural, not bugs you patch. You fix them by giving each person their own credential. The work closed that gap with three things, in this order: per-user tokens (identity), a server-side write gate (authorization), and a per-read access log (audit). The through-line: the API is the real boundary. The MCP client-side tool gate is UX, not security. And the per-read access log stays separate from the governance audit trail — separate log, not no log. Identity: per-user tokens replace the shared key apps/api/src/auth/token-registry.ts . Each token resolves to a record: { actor, role } , where role is 'admin' | 'member' . The shared key's two failures both dissolve here — every request now carries an actor , and revoking one person is dropping one record, not a team-wide rotation. Tokens come from layered sources, in precedence order: explicit records → a TEAMKB_TOKENS JSON env → a TEAMKB_TOKENS_FILE (default ~/.teamkb/tokens.json ) → the legacy single TEAMKB_API_KEY , which becomes one admin token with actor "shared" for back-compat. Each entry is a bearer token resolved to an identity at request time. Ma

2026-06-26 原文 →
AI 资讯

When --cap-drop ALL Broke the Gate Socket

The dogfood run went green. The gate had governed zero calls. That is the agent-governance-plane's entire job: run an AI coding agent inside a sandbox, route every tool call through a Unix-domain-socket gateway, and write a signed, hash-chained journal of every allow/deny. A green run that gated nothing isn't a pass. It's a governance plane governing air. The gate that catches its own hollowness AGP's CI dogfood doesn't just check that the harness exits 0. evidence-bundle.sh fails on a 0-gated run — if the journal shows no decisions, the build is red regardless of process exit status. That guard is what surfaced this at all: the agent process came up, the harness reported success, but the bundle had no verdicts to verify. Red. That's the last I'll say about hollow-green detection here. It's the door, not the room. The room is why zero calls reached the gate, and the answer turned out to be a collision between two things that look unrelated until you trace the syscall: Linux capabilities and a Unix socket's permission bits. The wrong theory The first hypothesis blamed the execution path. AGP has a dev-sandbox mode where the agent and the gate share a process, and a docker mode where the agent runs in a container talking to a host daemon over a bind-mounted socket. The theory was that the same-process path was short-circuiting the gate — agent and gate in one address space, the socket round-trip optimized away, decisions never journaled. Plausible. Wrong. The dev-sandbox path journaled fine in isolation. The failure only appeared in docker mode, and the moment that became clear the investigation moved from "which code path" to "what's different about the container." What's different about the container is the security posture. The real root cause: caps meet a missing write bit The short version: connecting to a Unix domain socket needs write permission on the socket file. --cap-drop ALL strips CAP_DAC_OVERRIDE — the capability that lets root ignore permission bits — s

2026-06-26 原文 →
AI 资讯

2026 PDF Generation API Comprehensive Comparison Review: 13 Mainstream Solutions Benchmarked (HTML to PDF)

By 2026, the PDF generation API market has evolved from "can it generate" to "does it generate well, fast, and securely." There are over 20 solutions on the market, ranging from a few euros per month for lightweight APIs to enterprise-grade SDKs, with price differences exceeding 100x. This article provides a horizontal comparison of 13 mainstream PDF Generation APIs across six core dimensions — rendering quality, developer experience, performance & stability, data security, pricing, and additional features — to help technical teams make optimal selections. 💡 If you're evaluating PDF generation solutions, check out ComPDF Generation API for an enterprise-grade PDF SDK that integrates viewing, editing, generation, and conversion in one package. Participating Products Overview Product Company/Background Core Positioning Starting Price (Official) ComPDF Generation API PDF Technologies (KDAN) Enterprise PDF Generation SDK + API Free 200 requests/month PDFGeneratorAPI Actual Reports (Estonia) Enterprise Document Automation €80/year (50 credits) CraftMyPDF Independent Team (Singapore) Drag-and-Drop Template Editor $0/month (50 PDFs) DocRaptor Expected Behavior (USA) Highest CSS Fidelity (PrinceXML) Free (5 watermarked docs/month), $15/month Orshot Independent Team Templates + API, supports images & video 30 free, $39/month APITemplate.io Independent Team Visual + HTML Dual Editor $0/month (50 PDFs), $19/month PDFMonkey Independent Team (France) Lightweight HTML Templates €0/month (20 docs), €5/month PDFShift Independent Team Minimalist HTML-to-PDF 50 free requests/month Api2Pdf Independent Team Pay-per-use, no monthly fee $1/month + usage IronPDF Iron Software (USA) .NET Ecosystem PDF Library $749/year Nutrient DWS Nutrient (formerly PSPDFKit) PDF Generation API 50 free requests/month Apryse Apryse (formerly PDFTron) Enterprise PDF SDK Contact sales (starting from $1,500) Adobe Document Generation API Adobe Cloud Document Generation Usage-based pricing Six-Dimension In-Dep

2026-06-26 原文 →
AI 资讯

🚀 Join the Omnia Community — Contributors Wanted

🚀 Join the Omnia Community — Contributors Wanted Hello everyone, I'm building Omnia , an open-source, privacy-first productivity workspace designed to combine notes, tasks, calendars, habits, goals, reminders, and AI assistance into a single desktop application. The vision is simple: Create the productivity app we all wish existed — fast, beautiful, extensible, local-first, and truly owned by its users. Current Stack React 19 TypeScript Tauri v2 SQLite Zustand Tailwind CSS v4 Tiptap Editor OpenRouter / OpenAI / Ollama What We're Building Omnia aims to become a serious alternative to tools like Notion, Obsidian, and other productivity platforms while remaining: Free and open source Privacy-focused Local-first Highly customizable Community-driven Looking For Contributors Everyone is welcome, regardless of experience level. Frontend Developers Help improve: UI/UX Editor experience Dashboard widgets Accessibility Responsive layouts Rust Developers Help with: Tauri backend Native integrations Performance optimization Security improvements Designers Help create: Themes Icons Illustrations User experience improvements Documentation Writers Help build: Wiki pages Tutorials Guides Developer documentation Open Source Enthusiasts Help by: Testing releases Reporting bugs Suggesting features Participating in discussions Current Priorities Stabilizing the first release Performance improvements Windows support Linux support Plugin architecture Theme ecosystem Export & backup tools Why Contribute? Because this is an opportunity to help shape an ambitious open-source project from the very beginning. Every contribution matters, whether it's a bug report, documentation improvement, design suggestion, or a major feature implementation. If you're interested in building the future of personal productivity software with us, we'd love to have you on board. Let's build something amazing together. 🚀 See you in the repository!

2026-06-26 原文 →
AI 资讯

JavaScript String Methods

A String in JavaScript is a sequence of characters used to store text. let course = " JavaScript " ; 1. String length Purpose Returns the total number of characters in a string. Syntax string . length Example let company = " OpenAI " ; console . log ( company . length ); Output 6 Real-Time Example Checking password length before registration. 2. String charAt() Purpose Returns the character at a specified index. Syntax string . charAt ( index ) Example let city = " Madurai " ; console . log ( city . charAt ( 3 )); Output u Internal Logic M a d u r a i 0 1 2 3 4 5 6 Index 3 contains "u". 3. String charCodeAt() Purpose Returns the Unicode value (UTF-16 code) of a character. Example let letter = " A " ; console . log ( letter . charCodeAt ( 0 )); Output 65 More Examples console . log ( " a " . charCodeAt ( 0 )); Output: 97 4. String codePointAt() Purpose Returns the Unicode code point of a character. Useful for emojis and special symbols. Example let emoji = " 😊 " ; console . log ( emoji . codePointAt ( 0 )); Output 128522 Difference console . log ( " 😊 " . charCodeAt ( 0 )); console . log ( " 😊 " . codePointAt ( 0 )); codePointAt() gives the actual Unicode value. 5. String concat() Purpose Combines two or more strings. Example let firstName = " Annapoorani " ; let lastName = " Kadhiravan " ; let fullName = firstName . concat ( lastName ); console . log ( fullName ); Output Annapoorani Kadhiravan Alternative console . log ( firstName + lastName ); 6. String at() Purpose Returns character at a specific position. Supports negative indexing. Example let language = " JavaScript " ; console . log ( language . at ( 0 )); console . log ( language . at ( - 1 )); Output J t 7. String [ ] Purpose Access characters using bracket notation. Example let laptop = " Dell " ; console . log ( laptop [ 0 ]); console . log ( laptop [ 2 ]); Output D l Difference console . log ( laptop . charAt ( 0 )); console . log ( laptop [ 0 ]); Both return same result. 8. String slice() Purpose Extract

2026-06-26 原文 →
AI 资讯

Why HTML-to-PDF Breaks in Production (and What to Use Instead)

Almost every "generate a PDF" feature starts the same way. You already have HTML. You already have CSS. So you reach for the obvious move: render the page, screenshot it to PDF, ship it. Puppeteer, Playwright, wkhtmltopdf, a hosted "HTML to PDF API" — pick your flavor. In an afternoon you have an invoice coming out the other end and it looks fine. Then it goes to production. And "fine" slowly turns into a backlog of weird, hard-to-reproduce bugs. This is not an argument that HTML-to-PDF is useless. For a one-off export or an internal report, it's great. The argument is narrower: the moment PDF generation becomes a real, automated, customer-facing part of your product, "screenshot a web page" is the wrong abstraction — and the failure modes are predictable enough to list in advance. The core problem: a PDF is not a web page A browser renders for an infinite, scrollable, single-width viewport. A PDF is a stack of fixed, finite, printable pages. Those are different physics. HTML-to-PDF works by rendering your page in a headless browser and then slicing that continuous render into page-sized pieces. Everything that's hard about it comes from that one mismatch: you designed for a stream, and now you're forcing it into pages. Most of the bugs below are just that mismatch showing up in different costumes. Failure mode 1: pagination This is the big one. A browser has no concept of "page 2." So when your content is taller than one page, the engine has to guess where to cut — and it cuts wherever the pixel ruler lands. That means: a table row sliced in half across the page break a heading stranded alone at the bottom of a page, its content on the next a total row that floats away from the table it belongs to a signature block split from the line above it CSS has break-inside: avoid , break-before , and friends — and they help. But support is uneven across engines, they interact badly with flex/grid, and you end up hand-tuning rules per document until it looks right for the da

2026-06-26 原文 →