AI 资讯
AI Code Tools for Legacy System Modernization (2026 Guide)
Originally published at nlocoding.com 92%of IT leaders say legacy systems slow digital transformation (IBM, 2026) Every minute, a bank somewhere spends $1,200 just keeping 1970s code alive. Not replacing it, just making sure it doesn’t explode. A senior developer at Citi told McKinsey in January 2026: “We spend 53% of our engineering budget patching COBOL.” Legacy code isn’t a quirky artifact anymore. It’s a financial anchor chained to your cloud ambitions... Why AI Code Tools for Legacy System Modernization Matter in 2026 AI code tools have redefined how companies approach system upgrades. In 2026, 61% of modernization projects fail due to manual errors or missed dependencies (Gartner, 2026). You can’t afford human error when one typo in ancient assembler code can cost $500,000 in downtime. The rise of generative AI for code refactoring is the only thing standing between you and a multi-million dollar rewrite. AI Code Tools Are Slashing Modernization Timelines by 63% AI code tools for legacy system modernization have cut modernization project timelines by 63% on average (Accenture, 2026). Manual migration can take 18 months—AI-powered tools like IBM watsonx Code Assistant and Google Gemini Advanced do it in under 7 months. This isn’t a hypothetical. Banco do Brasil migrated 2.8 million lines of COBOL to Java in 2025 with Cognizant’s AI tool; downtime: 14 hours. Average cost per line dropped from $3.60 (human) to $1.15 (AI-assisted). 💡 Pro Tip: Start with small pilot modules (1000-5000 lines). Measure defect rates before scaling. AI-Assisted Code Understanding Reduces Failure Rates Code comprehension is the single biggest risk in legacy system modernization. 47% of failures in 2026 were due to “unknown dependencies” (Forrester, 2026). AI code tools now map data flows, detect dead code, and generate architectural diagrams from raw source. Microsoft’s Copilot for Azure can parse 1.5 million lines in two days and flag 96% of “code rot” blocks. One insurance company in
AI 资讯
Refactoring Safely: A Step-by-Step Guide
Refactoring Safely: A Step-by-Step Guide We all know that feeling: a function that's 200 lines long, a class that does too many things, or a variable named data2 . Refactoring is the cure, but doing it recklessly can break your app and your confidence. Here's how I approach refactoring safely, step by step. 1. Start with a Safety Net Before touching any code, make sure you have tests. If your project lacks tests, write a few key ones first. Focus on the behavior you're about to change. The goal is to have a safety net that tells you when you've broken something. # example test for a function we'll refactor import unittest from mymodule import calculate_total class TestCalculateTotal ( unittest . TestCase ): def test_with_discount ( self ): self . assertEqual ( calculate_total ( 100 , discount = 0.1 ), 90 ) If tests aren't feasible, at least have a manual checklist. But automated tests are worth the effort. 2. Make Small, Atomic Changes Don't try to refactor everything at once. Pick one logical change. For instance, extract a method or rename a variable. Each change should be small enough that if it breaks, you know exactly what caused it. // before function processOrder ( order ) { const total = order . items . reduce (( sum , item ) => sum + item . price , 0 ); const tax = total * 0.08 ; const final = total + tax ; return final ; } // after step 1: extract tax calculation function processOrder ( order ) { const total = order . items . reduce (( sum , item ) => sum + item . price , 0 ); const final = total + calculateTax ( total ); return final ; } function calculateTax ( amount ) { return amount * 0.08 ; } Run your tests after each tiny step. If they pass, move on. If they fail, you know the last change caused it. 3. Use Your IDE's Refactoring Tools Modern IDEs can rename variables, extract methods, and change signatures safely. They update all references automatically. This reduces human error. For example, in VS Code, right-click a function and choose "Extract to
AI 资讯
The Moto Watch Ultra is a return to Wear OS
Just a few months after getting back into the smartwatch game, Motorola is targeting the premium end of the market with the first Moto Watch Ultra. It comes with some upgrades from the recent Moto Watch - most obviously the jump to Wear OS - but lacks the outdoorsy features that would make it a […]
AI 资讯
FSCSS Component Architecture: A Modular, Composition-First Approach to CSS
FSCSS component architecture is built around a modular, composition-first model that compiles to plain CSS. It emphasizes reusable style units, design tokens, conditional logic, and selective imports—with almost no runtime JavaScript required for the final output. Components in FSCSS are treated as pure style definitions rather than framework-specific widgets, keeping stylesheets readable, highly reusable, and free of classic “mega-stylesheet” problems while still producing standard CSS that any browser understands. Core Building Blocks FSCSS provides a focused set of primitives for defining and composing styles: Primitive Purpose Best for Introduced / Key version str(name, "…") Named blocks of CSS declarations Simple reusable style snippets Core @fun(name){…} Key-value stores (design tokens) Spacing scales, color palettes, property groups Core @define name(params) Parameterized mixins Themed components, variants, full structures 1.1.15+ pattern(threshold: "desc", "…") Semantic / fuzzy matching Natural-language style injection 1.1.25+ @event name(param) Conditional value functions Themes, states, calculations Core @arr(name[…]) Arrays + iteration Generated classes, loops, scales Core @import Selective / wildcard module loading Modular architecture & ecosystem modules Core How Components Are Structured 1. Atomic / Token Layer ( @fun + variables) Design tokens sit at the foundation so every component draws from a single source of truth: @fun(tokens) { primary: #2563eb; radius-md: 8px; space-4: 1rem; shadow-sm: 0 1px 3px rgba(0,0,0,.1); } 2. Base Style Blocks ( str() or @fun full-block) Related declarations are grouped into reusable blocks that can be dropped into any selector: str(card-base, " padding: @fun.tokens.space-4.value; border-radius: @fun.tokens.radius-md.value; box-shadow: @fun.tokens.shadow-sm.value; background: white; ") 3. Parameterized Components ( @define ) True mixins accept arguments and can be composed freely: @define button(bg: #2563eb, fg: white,
AI 资讯
How to Handle Anti-Bot Measures When Taking Screenshots Programmatically
How to Handle Anti-Bot Measures When Taking Screenshots Programmatically You send a request. The page loads. The screenshot comes back blank, or shows a CAPTCHA, or captures a "Please verify you're human" wall. This is one of the most common problems when building any screenshot pipeline. Here's what's actually happening and how to deal with it. Why headless browsers get flagged Bot detection works by looking for patterns that differ from real users. Headless Chrome has several tells: navigator.webdriver returns true by default Missing Chrome-specific properties like window.chrome Inconsistent screen dimensions (no monitor attached means no GPU info) Mouse events fire at pixel-perfect coordinates with no jitter Font fingerprints differ from headed browsers Modern detection services (Cloudflare, Akamai, Datadome) look for combinations of these signals, not individual flags. Spoofing one without the others often makes the fingerprint more suspicious, not less. The practical spectrum of detection Most sites fall into one of three categories: No active detection — a basic bot check via User-Agent string at most. Simple fix: set a realistic UA. Passive fingerprinting — loads a detection script, collects signals, blocks on second or third visit. You'll see this on news sites, e-commerce, media platforms. Active challenges — Cloudflare Turnstile, hCaptcha, reCAPTCHA v3 score-based. These require real interaction or a solving service. Know which category your target falls into before spending time on it. Fixes that work for most cases 1. Use a stealth plugin For Playwright, playwright-extra with puppeteer-extra-plugin-stealth patches the most common fingerprinting vectors: npm install playwright-extra puppeteer-extra-plugin-stealth import { chromium } from 'playwright-extra'; import StealthPlugin from 'puppeteer-extra-plugin-stealth'; chromium.use(StealthPlugin()); const browser = await chromium.launch(); This handles navigator.webdriver , window.chrome , and several other
AI 资讯
Namaste JavaScript — Complete Notes
Full interview-prep notes, ##Episode 1 through 29. Episode 1 : Execution Context ============================== Everything in JS happens inside the execution context. Imagine a sealed-off container inside which JS runs. It is an abstract concept that hold info about the env. within the current code is being executed. In the container the first component is memory component and the 2nd one is code component Memory component has all the variables and functions in key value pairs. It is also called Variable environment. Code component is the place where code is executed one line at a time. It is also called the Thread of Execution. JS is a synchronous, single-threaded language Synchronous:- In a specific synchronous order. Single-threaded:- One command at a time. Episode 2 : How JS is executed & Call Stack ============================================= When a JS program is ran, a global execution context is created. The execution context is created in two phases. Memory creation phase - JS will allocate memory to variables and functions. Code execution phase Let's consider the below example and its code execution steps: var n = 2 ; function square ( num ) { var ans = num * num ; return ans ; } var square2 = square ( n ); var square4 = square ( 4 ); The very first thing which JS does is memory creation phase, so it goes to line one of above code snippet, and allocates a memory space for variable 'n' and then goes to line two, and allocates a memory space for function 'square'. When allocating memory for n it stores 'undefined', a special value for 'n'. For 'square', it stores the whole code of the function inside its memory space. Then, as square2 and square4 are variables as well, it allocates memory and stores 'undefined' for them, and this is the end of first phase i.e. memory creation phase. Now, in 2nd phase i.e. code execution phase, it starts going through the whole code line by line. As it encounters var n = 2 , it assigns 2 to 'n'. Until now, the value of 'n' wa
AI 资讯
Workshop: Gate Retrieved Context With a Cheap Scoring Pass in 70 Minutes
Untrusted retrieval is now a more common production failure than a weak prompt, because agents ingest memory they never score. A seventy-minute workshop can add a cheap scoring gate, a replayable log, and a reject path before generation. Students leave with a runnable Python harness, a four-row decision table, and a timing plan they can repeat. The method stays useful if every product name is removed and the scoring host is only a free server. What you will build This workshop treats retrieved snippets as untrusted input, not as ground truth the model should quote. You will capture a retrieval batch, score each chunk against a written rubric, and allow only passing chunks into the prompt. A JSONL replay log records the fingerprint, score, and decision so later failures can be diffed. The generation model never sees dropped text, which keeps stale or planted memory out of the answer. Timing box 00:00–00:10 — install dependencies, copy the harness, and load the sample corpus 00:10–00:30 — Exercise 1: capture retrieval payloads and stable fingerprints 00:30–00:50 — Exercise 2: score chunks with a rubric and an optional free model 00:50–00:65 — Exercise 3: gate the prompt and replay one rejected case 00:65–00:70 — debrief against the decision table and list remaining holes The schedule is a teaching box, not a production SLA, and it assumes one laptop plus one HTTP scoring endpoint. If the endpoint is slow, freeze Exercise 2 after five scored chunks and continue with the logged samples. Do not expand the window to chase a perfect judge; the learning goal is a gate you can rerun. Why a scoring pass belongs in front of generation Cheap code generation has made it easy to wire a retriever into a chat loop in an afternoon. The failure mode that follows is quieter than a crash: the model answers fluently from a chunk that is expired, off-topic, or injected. Architecture diagrams rarely show that hop as a trust boundary, so teams skip scoring and jump to a larger generator. A
AI 资讯
Der Alarm, der niemanden weckt, ist schlimmer als keiner
In meinem ersten größeren Projekt waren wir stolz auf unsere Alarme. Wir hatten Hunderte davon. Jede Metrik, die uns eingefallen ist, bekam einen Schwellwert und eine Benachrichtigung. Wir dachten, das sei gründlich. In Wahrheit hatten wir uns eine Umgebung gebaut, in der niemand mehr hinschaute. Der Effekt kam schleichend. Erst überflog man die Alarme noch. Dann klickte man sie weg. Am Ende gab es einen eigenen Ordner, in den die ganzen Mails wanderten, ungelesen. Als dann ein echter Ausfall kam, ging seine Meldung im Rauschen unter, genau wie alle anderen. Wir hatten Alarme, aber wir hatten keine Alarmierung mehr. Das ist ein Unterschied. Seitdem behandle ich Alarme mit einer fast strengen Sparsamkeit. Meine Leitfrage lautet: Wenn dieser Alarm nachts losgeht, würde ich wollen, dass jemand aufsteht? Wenn die Antwort nein ist, dann ist es kein Alarm. Dann ist es höchstens ein Eintrag in einem Dashboard, das man morgens anschaut. Diese Trennung zwischen „jemand muss jetzt handeln" und „das ist zur Kenntnis" hat unsere Bereitschaft gerettet. Der zweite Grundsatz ist, auf Symptome zu alarmieren, nicht auf Ursachen. Nutzer merken nicht, dass eine CPU bei achtzig Prozent liegt. Sie merken, dass die Seite langsam ist oder Fehler zurückkommt. Also alarmiere ich auf das, was der Nutzer spürt: Latenz, Fehlerrate, gescheiterte Anfragen. Die hohe CPU ist dann eine Information für die Diagnose, kein Grund, jemanden zu wecken. Oft löst sich eine hohe Auslastung von selbst, ohne dass ein Mensch je etwas tun müsste. Am meisten hat mir geholfen, jeden Alarm mit einer ehrlichen Verfallsprüfung zu versehen. Alle paar Wochen gehe ich die Liste durch und frage bei jedem einzelnen: Ist der schon mal losgegangen? Hat daraufhin jemand etwas getan? Wenn ein Alarm seit Monaten nur Lärm macht und nie zu einer Handlung geführt hat, dann löse ich ihn auf. Das fühlt sich mutig an, ist aber das Gegenteil. Ein Alarm, dem niemand traut, schützt niemanden. Gutes Monitoring erkennt man nicht an der
创业投融资
X shifts US creator payouts from Stripe to X Money
X says U.S. creator payouts will now be handled through its X Money payments service, a change that appears to replace the previous Stripe-powered payout system.
AI 资讯
Google is sending MrBeast into the wilderness, armed with AI
MrBeast will feature Gemini, Google Health, and the Fitbit Air in upcoming videos as part of a multi-year partnership with Google. The deal will kick off with a video featuring Jimmy "MrBeast" Donaldson turning to Gemini for wilderness survival advice: First up on September 5 is a new MrBeast video following Jimmy and his crew […]
AI 资讯
Percentiles, the IQR and the 1.5 Outlier Rule: How to Flag a Bad Row
By Michael Nocito , data analyst · Published August 9, 2026 By the end of this page you can compute quartiles by hand, build the standard outlier fence from them, and run that fence over any column to get back a short list of rows worth looking at. On the sixteen orders below, one mistyped quantity gets flagged automatically while every honest large order stays inside the fence. Here is what to actually do today. On the column you care about most, get four numbers: the 25th percentile, the 75th, their difference, and 1.5 times that difference added to the 75th. Anything above that last number is a row to open and read. It is one query, and it turns "is this data clean" into a list of specific rows. The short version: a percentile is a value with a known share of the data below it. The interquartile range is the width of the middle half. Values more than one and a half of those widths beyond the middle half get flagged. The fence is easier to see than to read, so it gets the picture. The original carries a diagram here. In words: A horizontal line with a row of small filled dots along it, spaced unevenly and thinning out towards the right. A tall rectangle is drawn around the dots in the middle of the row, covering the central half of them, with a thick vertical bar inside it. The rectangle's left edge is labelled Q1, its right edge Q3, and the bar inside it median. From each edge of the rectangle a horizontal whisker line runs outward to a short vertical cap, reaching the furthest dot on that side that still lies within range. To the right of the right-hand cap stands a tall dashed vertical line labelled fence, drawn one and a half rectangle-widths beyond the rectangle's right edge, with a small double-headed measuring arrow underneath showing that distance against the rectangle's own width. One lone dot sits well to the right of that dashed line, drawn as a hollow ring instead of a filled dot, so it reads as picked out rather than belonging with the rest. Every oth
AI 资讯
What Is a P-Value? Worked by Shuffling Nine Real Orders 126 Ways
By Michael Nocito , data analyst · Published August 9, 2026 By the end of this page you can say what a p-value measures in one sentence, compute one by hand with no distribution theory at all, and name the four things people routinely claim a p-value says that it does not. The worked example is nine real orders where two regions differ by 168.50, and the answer comes out of counting rather than out of a table. Here is what to actually do today. Any time you are about to report that two groups differ, write down the two group sizes first. If either is under about twenty, a p-value will almost certainly come back large no matter how real the difference is, and the honest report is the difference, the sizes, and an interval, not a verdict. The short version: a p-value is the share of results at least as extreme as yours that you would get if the thing you are testing had no effect at all. Small means your result would be unusual under nothing-happening. It does not mean the effect is large, and it does not mean the effect is real. That definition is doing a lot of work in one sentence, so it gets the picture. The original carries a diagram here. In words: A histogram built from small dots, one dot per outcome, arranged in ten vertical columns of different heights standing on a horizontal baseline. The columns rise from one dot at the far left to a peak of twenty-seven dots just right of centre, then fall away to a single dot at the far right, giving the whole shape a rounded hump centred slightly left of the middle of the picture. Two vertical dashed lines cut down through the shape, one on the left of the hump and one on the right, placed symmetrically about the hump's centre. The dots lying in the two tails beyond those lines are drawn in a darker, warmer shade, and the dots in the bulk between them are drawn in a lighter blue, so the tails stand out from the middle. In the two columns the dashed lines pass through, the darker dots are stacked at the bottom of the co
AI 资讯
Moving Averages: Smoothing a Series Without Smoothing Away the Truth
By Michael Nocito , data analyst · Published August 9, 2026 By the end of this page you can build a moving average by hand, choose a window length on purpose rather than by habit, explain the lag it introduces, and spot the one data problem that silently corrupts every moving average you will ever compute. On the sixteen weeks below, the raw series wobbles with a standard deviation of 237. A three-week average brings that to 101, and a five-week to 62. Here is what to actually do today. Add a moving average to your busiest weekly or daily chart, using a window that matches your cycle: 7 for daily data with a weekday pattern, 4 or 13 for weekly, 12 for monthly. Then plot it on top of the raw series rather than instead of it, so the reader can see both the noise and the trend. The short version: a moving average replaces each point with the average of it and the points around it. Wobble cancels out, trend survives, and the price is that the smoothed line reacts late. The effect is easier to see than to describe, so it gets the picture. The original carries a diagram here. In words: A line chart with two lines drawn on the same axes over sixteen evenly spaced points. The first line is thin and violently jagged, swinging from near the top of the chart down to near the bottom and back again several times, with one especially deep trough about a quarter of the way along and one especially tall spike just past the middle. The second line is thicker and starts two points later than the first. It stays in a narrow band through the middle of the chart, rising and falling only gently, and never comes close to either the peaks or the troughs of the jagged line. Where the jagged line spikes to its highest point, the thick line rises only slightly and does so one point later than the spike. The word actual labels the jagged line near its left end, and the phrase three-week average labels the thick line. Every number on this page is real. The same sixteen orders used across these
AI 资讯
Mean vs Median: When to Use Each, and When the Answer Changes
By Michael Nocito , data analyst · Published August 9, 2026 By the end of this page you can compute both averages by hand, say in one sentence which one your question needs, and show what each does when one number in your data is wrong. On the sixteen orders below, a single mistyped quantity moves the mean by 337.50 and the median by 30. That gap is the whole reason both words exist. Here is what to actually do today. Next to every average you report, put the median beside it and look at the two numbers together. If they are close, report the mean and move on. If they are far apart, you have found something worth saying out loud, and this page is about what. The short version: the mean shares the total out equally, so every value pulls on it. The median is the middle value once you sort, so only the position of a value matters, not its size. That difference is easiest to see rather than read, so it gets the picture. The original carries a diagram here. In words: Two horizontal number lines, one above the other, drawn on the same scale. On the top line, sixteen small filled dots sit in a loose cluster across the left and middle of the line. Two markers sit almost on top of each other just past the centre of that cluster: a solid triangle pointing up, labelled mean, and a short vertical bar, labelled median. On the bottom line the same sixteen dots appear, except one dot from the middle of the cluster has moved far to the right and off the end of the line, shown by an arrow leaving the line through a small break mark at the right edge. The median bar on the bottom line has barely shifted from where it was on the top line, moving only a hair to the right. The mean triangle on the bottom line has slid a long way to the right, roughly ten times further than the median moved, and a horizontal dotted guide connects its old position on the top line to its new position on the bottom line to show the size of the slide. Every number on this page is real. One sixteen-row order
AI 资讯
Pangram Has Emerged as the Gold Standard of AI Detection. Should You Trust It?
Meet the AI police who can make or break careers—in publishing and beyond.
AI 资讯
A Small Transformer Trained in 1.5 Hours Beat Many LLMs on ARC
Mithil Vakde trained a small transformer from scratch in 1.5 hours on a rented RTX 5090. The compute cost came to about 67 cents. The model scored 44% on the ARC-AGI-1 public eval, one point behind TRM's 45% and in the same band that left DeepSeek R1, o3-mini, and Gemini 2.5 Pro behind when the TRM paper ran the comparison ( arXiv 2510.04871 ). This is the third post in his ARC series. The previous result went viral and drew public scrutiny from researchers including Lucas Beyer, Jeremy Howard, and Rohan Anil. The new one raises the score, cuts the cost, and answers every objection in a long appendix. What the model does Each puzzle becomes a sequence of tokenized grids. The transformer trains on those sequences from scratch at test time, using both the train puzzles and the eval puzzle inputs, with the eval labels hidden. Test-time training is not a loophole here. ARC is a meta-learning benchmark built around skill acquisition, and Chollet's original paper frames the whole thing as a measure of how efficiently a system gains new skills ( arXiv 1911.01547 ). The architecture got a 2026 refresh: 8 layers, SwiGLU, RMSNorm, the NorMuon optimizer, 3D RoPE, and per-task embeddings. Inference augments each test input with color and dihedral permutations, inverts the augmentations, and submits the two most common outputs. The biggest change is the loss function. Previous versions trained on input and output tokens. This one trains on output tokens only, which makes the approach supervised. Score went from 40% to 44%. Vakde admits he does not fully understand why, and the training loss got worse even as scores improved. His guess is finite model capacity. The ablations back him up on what carries the result: drop the 3D RoPE and the score falls to about 24%, drop the per-task embeddings and it falls to about 24%. Representations, not compute, are doing the work. The lineage it joins None of these systems pretrain. They learn from the benchmark's roughly thousand examples: S
AI 资讯
How the WordPress transient API works, and when `wp transient delete` actually helps
WordPress ships with a built-in way to store data temporarily — save something for a fixed window of time, and it stops being valid once that window closes. This is the transient API, and both WordPress core and countless plugins lean on it to cache things like external API responses or the results of expensive calculations. It's a genuinely useful mechanism, but used without understanding how it actually behaves, expired entries can pile up and quietly bloat the database. Note: the transient API is WordPress core's name for a small set of PHP functions — set_transient() , get_transient() , delete_transient() — built around the idea of a cache entry with an expiration. How a transient actually works Saving a transient means specifying three things: a value, a key, and an expiration in seconds. set_transient ( 'weather_data' , $api_response , 3600 ); // cache for one hour Where that value actually gets stored depends on the site's setup: Default setup (most shared hosting environments): it lands in the wp_options table as a row named _transient_<key> , with a matching _transient_timeout_<key> row holding the expiration With a persistent object cache (a plugin backed by Redis or Memcached): the value goes to that cache layer instead of wp_options When get_transient() is called, WordPress compares the timeout value against the current time and returns false if the entry has expired. At that point, the design intends for the stale row to be cleaned up automatically — but that cleanup isn't as reliable as it sounds. Why expired entries stick around In theory, an expired transient should disappear. In practice, wp_options can accumulate a large number of long-expired rows. Two things typically cause this: get_transient() is never called again for that key. The automatic cleanup described above is passive — it only fires when something actually tries to read the value and finds it expired. It isn't an active sweep. If a plugin sets a value once and never checks it again, t
AI 资讯
FBI Probes Service Selling 153M+ Drivers Licenses
A new identity theft service launched on the dark web this week is selling digital scans of more than 153 million drivers licenses from people in the United States and Canada. Based on interviews with individuals whose licenses are available for purchase on this service, it appears to be siphoning images collected by a widely-used identity verification company based in Louisiana. KrebsOnSecurity also has learned that the New Orleans field office of the Federal Bureau of Investigation (FBI) today launched an official inquiry into the source of the images.
AI 资讯
AfterQuery reportedly becomes Y Combinator’s fastest-ever unicorn, now valued at $3.2B
AI model-training startup AfterQuery has reportedly raised a round that valued it at $3.2 billion, just five months after announcing its $30 million Series A at a $300 million valuation in April.
AI 资讯
Split PDF Pages in the Browser with pdf-lib — No Uploads, No Server
A few weeks ago I built a free online Merge PDF tool that runs 100% in the browser. Today I'm sharing its sibling: a Split PDF tool using the same library — pdf-lib — with zero file uploads, zero watermark, and zero server code. You can try it live here: https://yourutilityhub.com/pdf/split-pdf Why split PDFs in the browser? Most online PDF tools upload your file to a server — which means your document is never truly private. Splitting pages locally means: No uploads — nothing leaves your device No watermark or signup Free — no per-page charges Works offline, fast, for files of any size (limited by your browser's memory) The plan We'll load the PDF, pick a page range (or specific pages), copy those pages into a fresh PDFDocument , and save the result — all with pdf-lib . Let's walk through the full working component . 1. Install and import npm install pdf-lib import { PDFDocument } from " pdf-lib " ; 2. Load the uploaded file const arrayBuffer = await file . arrayBuffer (); const pdf = await PDFDocument . load ( arrayBuffer ); const totalPages = pdf . getPageCount (); PDFDocument.load() accepts an ArrayBuffer . We read it straight from the File object — no server involved. 3. Split by page range (e.g. 1-5 or 3- ) const parts = pageRange . split ( " - " ); const startRaw = parseInt ( parts [ 0 ]. trim (), 10 ); const endRaw = parts [ 1 ]. trim () === "" ? totalPages : parseInt ( parts [ 1 ]. trim (), 10 ); // validate 1..totalPages const startPage = Math . min ( startRaw , endRaw ) - 1 ; // 0-based const endPage = Math . max ( startRaw , endRaw ) - 1 ; const newPdf = await PDFDocument . create (); const pageIndices = []; for ( let i = startPage ; i <= endPage ; i ++ ) { pageIndices . push ( i ); } const copiedPages = await newPdf . copyPages ( pdf , pageIndices ); copiedPages . forEach ( page => newPdf . addPage ( page )); The trick: copyPages() wants 0-based indices , but users type 1-based page numbers, so we subtract 1. "3-" with an empty end means "to the last pa