AI 资讯
The Midnight wallet SDK changed its npm scope. Here is what to update.
If you installed the Midnight wallet SDK a while back and pinned the package names, your imports are now pointing at a deprecated scope. Nothing is broken yet. But the packages you depend on moved, and the old names are living on borrowed time. Here is what changed, why it matters, and the one gotcha that trips people up. The short version The wallet SDK packages moved from the @midnight-ntwrk scope (with a dash) to @midnightntwrk (no dash). @midnight-ntwrk/wallet-sdk-facade -> @midnightntwrk/wallet-sdk-facade The old dashed packages still install, so your build keeps working for now. They are published as a transitional alias. But the dashed scope is deprecated, and the newest releases only show up on the new no-dash scope. So you want to move over. There is one exception. @midnight-ntwrk/ledger-v8 stays on the dashed scope. Do not rename that one. More on that below. What actually changed Straight from the wallet SDK v1.2.0 release notes: the npm scope has changed from @midnight-ntwrk to @midnightntwrk (no dash). New installs should depend on @midnightntwrk/* . The old @midnight-ntwrk/* packages continue to be published as a transitional alias during the migration window, so existing consumers keep working, but the dashed scope is deprecated. So both scopes exist on npm right now. That is why nothing breaks. But they are not equal. The no-dash scope is where the active releases land, and the dashed scope lags behind. You can see it yourself. Here are the current latest versions, dashed vs no-dash: Package Dashed (old) No-dash (new) wallet-sdk-facade 4.0.1 4.1.0 wallet-sdk-hd 3.0.2 3.0.3 wallet-sdk-shielded 3.0.1 3.0.2 wallet-sdk-dust-wallet 4.1.0 4.2.0 If you stay on the dashed names, you quietly get the older packages. The version fixes and new features go to the no-dash scope first. The gotcha: ledger-v8 does not move This is the part that catches people. When you do a find and replace across your project, it is tempting to swap every @midnight-ntwrk for @midnig
开源项目
🔥 chuspeeism / dashi-ppt-skill - An AI-agent skill that generates browser-editable presentati
GitHub热门项目 | An AI-agent skill that generates browser-editable presentations from multiple visual themes, exportable to HTML, PDF, and PPTX. | Stars: 5,764 | 298 stars today | 语言: JavaScript
AI 资讯
The day I asked three LLM agents to rewrite legacy Java for me — and what actually happened
1. The question that started everything Three weeks into my internship, my supervisor sat down across from me and asked, very casually: "OK your NLP pipeline extracts intentions and rules from legacy Java. Nice. And then what? " I looked at him. I looked at my laptop. I looked back at him. The whole project — Pulsar Modernizer — was supposed to eventually turn legacy Java into modern Spring Boot code. My part was the "understand the old code" part. F1 = 0.857 on the annotated corpus, a shiny React UI, everything humming in Docker. But the "and then?" was doing a lot of work in that sentence. That evening I wrote in my notes: "Nobody has actually tried the generation part. Everyone assumes it'll be easy because LLMs. That is very obviously wrong." So I decided to try. 2. Why "just prompt an LLM to rewrite it" doesn't work The naive move — feed the old code and the extracted rules to an LLM and say "please modernize this" — has three problems and I hit all of them in the first hour: The model hallucinates. It happily invents helper classes that don't exist and calls methods with the wrong signature. You have no criterion for stopping. The model tells you "it's done ". OK. Is it? By what test? You have no criterion for equivalence. Even if it compiles, how do you know the new code actually does what the old one did? I needed something more constrained than "prompt it and pray". 3. The setup — a chain, not a monolith I ended up building three specialized agents in sequence: IntentCard + RuleCards │ ▼ [APIDesigner] ──► JSON contract (class, methods, DTOs, throws) │ ├───────────────┐ ▼ ▼ [CodeGenerator] [TestGenerator] │ │ ▼ ▼ .java *Test.java │ │ └────► verifier (mvn test) The key insight: each rule extracted from the legacy code should become a test that the generated code has to pass. This flips the whole thing. I don't trust the LLM. I trust javac and JUnit. I did all of this on a local model — Qwen 2.5 Coder 3B via Ollama. No cloud APIs, no data leaving my Mac. On a
开发者
Making a screenshot PDF searchable — no OCR, because we rendered the page
We archive whole web pages as PDFs. Under the hood each page is a full-height screenshot dropped onto a PDF page — which looks perfect and is completely useless the moment you want to use the text. Ctrl+F finds nothing. You can't copy a sentence. A screen reader opens the document and sees… an empty page with one big image. The fix is the same trick a "searchable scan" uses: draw the real text invisibly , on top of the image, at the exact coordinates where each word appears. The difference is that a scanner needs OCR to guess the text — we rendered the page ourselves , so we already have the ground truth. No OCR, no guessing. Here's how we built it with pdf-lib and @pdf-lib/fontkit , and the one part that turned out to be genuinely hard. The shape of it While the page is still open in the headless browser, ask the DOM where every word is. Assemble the PDF: embed the screenshot as the page background. For each word, drawText it at its coordinates with opacity: 0 . Steps 1 and 3 are easy. The trap is in which words you're allowed to draw. Step 1 — ask the browser where the words are Running inside the page (Puppeteer's page.evaluate ), we walk every text node and measure each word with a Range : const walker = document . createTreeWalker ( document . body , NodeFilter . SHOW_TEXT ); // ...for each word in each text node: const range = document . createRange (); range . setStart ( node , start ); range . setEnd ( node , end ); const rects = range . getClientRects (); if ( ! rects . length ) continue ; // display:none or empty line box const b = rects [ 0 ]; // first rect = where the word starts out . push ({ t : word , x : b . left + window . scrollX , // document coordinates, not viewport y : b . top + window . scrollY , w : b . width , h : b . height , fs : parseFloat ( getComputedStyle ( el ). fontSize ) || 12 , }); getClientRects() gives viewport coordinates, so we add scrollX/scrollY to get document coordinates — the ones that line up with a full-page screenshot.
AI 资讯
Pi4J LED Playground: A Community Resource for Learning Hardware Programming with Java
One of the best moments when learning electronics is seeing your first LED blink. It's a simple experiment, but it represents the bridge between software and the physical world. With Java and Pi4J, that first step is already well documented. But what happens after the first LED? How do you experiment with different animations, colours, brightness levels, or GPIO configurations without repeatedly rewriting the same code? That question led to the creation of the Pi4J LED Playground . 👉 https://igfasouza.github.io/pi4j-led-playground/ Why another example? Pi4J already provides excellent examples and documentation for getting started with Raspberry Pi hardware. The project itself encourages community-driven examples and implementations, recognising that the ecosystem grows through shared contributions. The goal of the LED Playground is not to replace those examples. Instead, it provides an interactive environment where developers can quickly experiment with LED behaviours while learning how Pi4J works. Think of it as a sandbox where changing a few lines of code immediately produces visible results. Built by the community, for the community This project started as a personal experiment while exploring Pi4J. Very quickly it became clear that the playground could be useful for others who are starting their journey with Java on Raspberry Pi. Instead of keeping it as a private repository, it was published as an open community resource where anyone can: 1. learn from the source code; 2. suggest improvements; 3. report issues; 4. contribute new LED effects; 5. help improve the documentation; Open source projects become stronger when many people contribute different ideas, and Pi4J itself has grown thanks to this collaborative model. What can you do? The playground demonstrates common LED operations such as: turning LEDs on and off; blinking patterns; brightness control (where supported); experimenting with different GPIO configurations; creating reusable animations; Because th
AI 资讯
Academic social network developed to connect students through knowledge exchange.
SkillShare is an academic social network developed to connect students through knowledge exchange, informal tutoring, and collaboration among users with different skills. The project aims to facilitate collective learning through a modern, dynamic, and responsive web platform. The project was developed as a Course Completion Project (TCC) for the Technical Course in Information Technology at the Escola Técnica de Brasilia (ETB).
AI 资讯
How to Convert PDF to Word in the Browser with Vue 3 and pdf-lib
Converting PDF to Word seems straightforward, but the reality is more complex. PDF stores text as character coordinates, while Word uses structured paragraphs. Bridging this gap requires careful text extraction and order reconstruction. Here's how to build a browser-based PDF to Word converter with Vue 3 and pdf-lib . The challenge: PDF vs Word PDF is a presentation format — text is positioned precisely on the page. Word is an editing format — text flows in paragraphs with styles. Converting between them means: Extracting text from PDF coordinates Reconstructing reading order Generating structured DOCX output The stack Vue 3 with Composition API pdf-lib for PDF parsing docx for Word document generation Vite for bundling The core implementation < script setup lang= "ts" > import { ref } from ' vue ' import { PDFDocument } from ' pdf-lib ' import { Document , Paragraph , TextRun } from ' docx ' const file = ref < File | null > ( null ) const processing = ref ( false ) const result = ref < Blob | null > ( null ) async function convertPdfToWord () { if ( ! file . value ) return processing . value = true const arrayBuffer = await file . value . arrayBuffer () const pdf = await PDFDocument . load ( arrayBuffer ) const pages = pdf . getPages () const allChunks : TextChunk [] = [] for ( const page of pages ) { const textContent = await page . getTextContent () for ( const item of textContent . items ) { allChunks . push ({ text : item . text , x : item . transform [ 4 ], y : item . transform [ 5 ], size : item . size }) } } // Sort by reading order const sorted = sortByReadingOrder ( allChunks ) // Generate DOCX const doc = new Document ({ sections : [{ properties : {}, children : sorted . map ( chunk => new Paragraph ({ children : [ new TextRun ( chunk . text )] }) ) }] }) const blob = await doc . pack () result . value = blob processing . value = false } interface TextChunk { text : string x : number y : number size : number } function sortByReadingOrder ( chunks : TextCh
AI 资讯
Next.js 16.3: Instant Navigations, Up to 90% Less Dev Memory and Faster Builds
Vercel has released Next.js 16.3, featuring significant updates since version 16.0. Enhancements include reduced memory usage during development, accelerated build times, and improved type checking. Instant Navigations introduces faster, client-like responses while maintaining server-rendered architecture. Developers are advised to gradually adopt new features due to noted caveats. By Daniel Curtis
开发者
React useEventListener Hook: Type-Safe DOM Events (2026)
Here's a modal close-on-Escape that quietly does the wrong thing: function Modal ({ onClose }: { onClose : () => void }) { useEffect (() => { const onKey = ( e : KeyboardEvent ) => { if ( e . key === " Escape " ) onClose (); }; window . addEventListener ( " keydown " , onKey ); return () => window . removeEventListener ( " keydown " , onKey ); }, [ onClose ]); return < div role = "dialog" > … </ div >; } If the parent passes an inline onClose={() => setOpen(false)} — and it almost always does — onClose is a new function on every render, so this effect tears the listener down and adds a fresh one on every single render of the parent. Drop onClose from the deps to stop the churn and you get the other bug: the listener now holds the first render's onClose forever, and closing the modal calls a stale closure. You can't win this with a dependency array, because the two things you want are in direct conflict: subscribe once , but always run the newest handler . The fix is to separate them — register the listener on a stable identity, and call through a ref that's kept current. useEventListener from @reactuses/core is that split, packaged. This post covers what it actually does under the hood, the four ways to name a target, exactly what TypeScript infers for each one (this part surprises people), the options that don't retrigger, and the two gotchas worth knowing before you ship it. Quick Start npm install @reactuses/core import { useEventListener } from " @reactuses/core " ; function Modal ({ onClose }: { onClose : () => void }) { useEventListener ( " keydown " , ( e ) => { if ( e . key === " Escape " ) onClose (); }); return < div role = "dialog" > … </ div >; } That's the whole fix. No dependency array, no useCallback on the parent, no cleanup to remember. The listener is added to window once when the component mounts and removed when it unmounts; the arrow function you passed is re-created on every render and it doesn't matter, because the listener never re-registers
AI 资讯
Stop Writing Regex to Match URLs — The Browser Already Can
Priya was three paragraphs into rewriting a support ticket when the page flashed and her draft reverted to what it had looked like an hour earlier. She hadn't refreshed. Nobody had. The service worker had. It was running a cache-first strategy for ticket pages — fetch once, serve from cache after that, so the dashboard felt instant on a flaky connection. The intent was to cache /tickets/482 , the read-only view, and leave /tickets/482/edit alone, since an edit form is exactly the page you never want served stale. Here's the line that decided which was which: const isTicketView = /^ \/ tickets \/\d +/ . test ( pathname ); Spot it yet? Read it once more before you scroll. The missing character was $ /^\/tickets\/\d+/ anchors the start of the string — ^ — but never anchors the end. So it matches /tickets/482 . It also matches /tickets/482/edit , /tickets/482/history , and /tickets/482-anything-at-all , because "one or more digits after /tickets/ " is true of all of them. The regex was never wrong about what it checked. It just never checked enough. The one-character fix is obvious once you see it: const isTicketView = /^ \/ tickets \/\d +$/ . test ( pathname ); Ship that and you'll hit the next edge case within a week: a trailing slash ( /tickets/482/ ) now fails to match, because $ demands nothing comes after the digits — not even a slash. Add \/? before the $ and you've fixed that one. Then someone deep-links to /tickets/482?tab=history and the query string breaks the anchor again, because pathname on some code paths actually holds the full URL. Each fix is a patch on the last, and every patch is a chance to reintroduce the first bug in a new shape. This is the part nobody tells you about hand-rolled URL matching: it isn't hard because regex is hard. It's hard because "does this path match this shape" has a dozen boundary conditions, and a hand-written pattern only encodes the ones you happened to think of on the day you wrote it. The API built for exactly this job T
AI 资讯
Fix Next.js "params should be awaited" Error in Next.js 15+
Fix Next.js "params should be awaited" Error in Next.js 15+ If you are seeing the params should be awaited Next.js error after upgrading to Next.js 15 or following an older App Router tutorial, you are not alone. The error usually looks something like this: Route "/blog/[slug]" used params.slug. params should be awaited before using its properties. Sometimes it appears with searchParams . Sometimes it appears with cookies() or headers() . And sometimes the page still seems to work, but your terminal keeps shouting at you. This article will slow it down and explain the fix in a beginner-friendly way. No deep framework lecture first. Just the actual problem, the broken code, the fixed code, and the reason it works. What This Error Means in Plain English In older Next.js code, you may have treated params like a normal JavaScript object. Something like this: const slug = params . slug ; That used to feel natural. If your route was: /blog/[slug] and the user opened: /blog/my-first-post you expected: params . slug ; // "my-first-post" In newer Next.js versions, especially Next.js 15+, some request-based values became asynchronous. That means you should treat them like values that need to be waited for before you read from them. So instead of reading params.slug directly, you do this: const { slug } = await params ; That is the heart of the fix. The error is not saying your route is missing. It is not saying your [slug] folder is wrong. It is saying: You are trying to read route data before awaiting it. The common flow: the page loads, the code reads params.slug directly, Next.js expects params to be awaited, and the error appears. Why This Changed Next.js has a group of features called Dynamic APIs . That sounds more complicated than it is. In simple terms, Dynamic APIs are values that depend on the current request. For example: What route did the user open? What query string is in the URL? What cookies came with this request? What headers came with this request? Is draft
AI 资讯
Run Local LLMs with Ollama and Spring AI
In the previous parts, we connected Spring AI with cloud-based AI models. But there is one important question: What if you don't want to send your data to an external AI provider? What if you want to: Run an LLM on your own machine Develop AI applications without API costs Work without an internet connection Keep sensitive company data private Experiment with different open-source models Build AI features locally before moving them to production This is where Ollama becomes very useful. In this article, we will learn how to run a local LLM using Ollama and connect it with Spring AI . We will build a simple real-world AI Customer Support Assistant using Java, Spring Boot, Spring AI, and Ollama. What We Are Building Our application will look like this: User | | HTTP Request v +---------------------+ | Spring Boot API | +---------------------+ | v +-------------+ | Spring AI | | ChatClient | +-------------+ | v +--------+ | Ollama | +--------+ | v Local LLM (Llama/Qwen) | v AI Response | v User The important part is that the LLM is running locally . There is no need to send every prompt to OpenAI, Anthropic, or another cloud provider. 1. What Is Ollama? Ollama makes it easy to run open-source LLMs locally. Instead of calling a remote API like: Spring Boot | v OpenAI API | v Cloud LLM we can run: Spring Boot | v Spring AI | v Ollama | v Local LLM Ollama can run models such as: Llama Qwen Gemma Mistral DeepSeek and many other compatible models The exact models available change over time, so always check the Ollama model library before choosing one. 2. Why Run an LLM Locally? Imagine you are building an internal HR application. Employees may send questions such as: What is our maternity leave policy? or: What is the process for requesting annual leave? You may not want internal company information leaving your infrastructure. A local LLM can help: Employee | v Spring Boot | v RAG / Business Logic | v Ollama | v Local LLM This can provide a useful privacy boundary. However
AI 资讯
How to Stop Your Discord Bot From Sleeping on Render's Free Tier
A step-by-step tutorial to stop a discord bot from sleeping on Render's free tier — the real cause, the fix, and a working code example. How to Stop Your Discord Bot From Sleeping on Render's Free Tier You've deployed your Discord bot to Render's free tier, it worked for a bit, and now it's going offline — sometimes after a few minutes, sometimes randomly. This is one of the most common issues developers hit deploying a bot for the first time, and it has a specific, well-understood cause and a fix you can ship in under ten minutes. Table of Contents Why This Happens on Render Specifically Confirming This Is Your Actual Problem Step 1: Install StayPresent Step 2: Wrap Your Bot's Entry Point Step 3: Read Render's Assigned Port Step 4: Set Your Render Start Command Step 5 (Optional): Prevent Inactivity Sleep Specifically Verifying It Worked FAQs Conclusion Why This Happens on Render Specifically Render's free-tier web services are checked for health over HTTP, and free services also spin down after a period without incoming traffic. A discord.py bot connects outward to Discord's gateway — it never opens an HTTP port of its own, which is completely normal bot behavior. Render's health checker, seeing nothing respond on the expected port, has no way to know the bot is actually working fine internally. It just sees silence, and reacts accordingly. Confirming This Is Your Actual Problem If your bot's entry point goes straight into bot.run(TOKEN) with nothing else, and Render's dashboard shows the deployment as unhealthy or repeatedly restarting with no matching error in your bot's own logs, this is almost certainly it. Step 1: Install StayPresent pip install staypresent[prod] Add it to your requirements.txt as well: staypresent[prod] discord.py Step 2: Wrap Your Bot's Entry Point Keep your existing bot code in bot.py completely unchanged. Create a new main.py : import os import staypresent staypresent . web . json ({ " status " : " running " }) staypresent . run ( " bot.py
AI 资讯
Perry Mason in: The Case of the Drifting Timer
Perry Mason in: The Case of the Drifting Timer Opening Statement You need a reactive "current time" in your Vue 3 app. A schedule grid with a red line showing "now." A live clock. A dashboard that updates every minute. Every Vue developer reaches for setInterval first. It works. But "works" and "works well" are different things. This is the story of taking a naive timer from "it ticks" to production-grade — and the four iterations it took to get there. The prosecution calls four exhibits. Let's begin. Exhibit A: The Memory Leak const currentTime = ref ( new Date ()) onMounted (() => { setInterval (() => { currentTime . value = new Date () }, 60000 ) }) It works. Sort of. The defense rests — but the prosecution is just getting started. Exhibits of negligence: The interval is never cleared. When the component unmounts, the timer keeps firing every 60 seconds forever — updating a ref nothing reads anymore, and holding its closure (and everything the ref references) in memory for the lifetime of the page. Silent. Invisible. The kind of leak that shows up in production after a user navigates around your app for 20 minutes. Exhibit B: Component-Only Cleanup const currentTime = ref ( new Date ()) let timeInterval = null onMounted (() => { currentTime . value = new Date () timeInterval = setInterval (() => { currentTime . value = new Date () }, 60000 ) }) onUnmounted (() => { if ( timeInterval ) clearInterval ( timeInterval ) }) Now we clean up. The interval is stored in a variable, cleared on unmount. A step forward. But onUnmounted has a scope limitation worth understanding: The limitation: onUnmounted only works inside components. If someone calls this logic from a Pinia store or outside a component's setup() context, onUnmounted never fires. The timer leaks silently. (Composables called synchronously during setup() are fine — Vue's docs recommend exactly that. The problem is when there's no component instance at all.) The timer fires 60 seconds after load , not at the t
开发者
Implementing IN statements using JooqTemplate
@Service public class SimpleUserService { @Autowired private JooqTemplate jt ; public List < user > selectUserInDept ( UserParam param ) { //If deptIDs==null or deptIDs. isEmpty automatically ignores this query condition // SELECT * FROM user_table WHERE name LIKE '%?%' AND dept_id IN (?,?...); return jt . queryv ( "user_table" , User . class , "name%" , param . getName (), "dept_id:in" , param . getDeptIds ()); } public List < user > selectUserNotInDept ( UserParam param ) { // SELECT * FROM user_table WHERE name LIKE '%?%' AND dept_id NOT IN (?,?...); return jt . queryv ( "user_table" , User . class , "name%" , param . getName (), "dept_id:notin" , param . getDeptIds ()); } }
AI 资讯
Part 1 — What Actually Happens When Code Runs
When we write: const result = add ( 10 , 20 ); it feels like the computer simply "runs the code." But the CPU doesn't understand JavaScript. There are several layers between the code we write and the hardware actually executing instructions. That's what I wanted to understand first. From JavaScript to the CPU In Node.js, JavaScript is handled by V8 , the JavaScript engine. A simplified view looks like this: JavaScript ↓ V8 ↓ Bytecode ↓ JIT compilation ↓ Machine instructions ↓ CPU V8 doesn't simply "interpret JavaScript" or "compile JavaScript" once and forget about it. It can start with bytecode and progressively compile frequently executed ("hot") code into more optimized machine code. Eventually, the CPU is executing instructions that operate at a much lower level than the JavaScript we originally wrote. What does the CPU actually do? At its core, a CPU repeatedly executes instructions. A simplified mental model is: Fetch → Decode → Execute → Repeat The CPU has several important pieces involved in this process. Registers are tiny, extremely fast storage locations inside the CPU. They're used to hold values the CPU is actively working with. The ALU (Arithmetic Logic Unit) performs many arithmetic and logical operations. The Program Counter (PC) keeps track of where the next instruction comes from. And the CPU runs according to a clock, measured in GHz. A 3 GHz CPU has roughly 3 billion clock cycles per second, but that does not mean it executes 3 billion instructions per second. Different instructions and architectures have different costs. Modern CPUs are far more sophisticated than this simplified model, using pipelining, multiple execution units, branch prediction, out-of-order execution, and more. But the basic model is enough to start reasoning about performance. The CPU doesn't get everything from RAM One of the most important things I learned here is that where data lives matters . A simplified hierarchy looks like: Registers ↓ L1 Cache ↓ L2 Cache ↓ L3 Cache
AI 资讯
Computer Fundamentals: No BS
I've been working with software for a few years now, and I've noticed something uncomfortable. I can build things. I can work with React, Node.js, databases, APIs, cloud services, and all the usual stuff that comes with being a software engineer. But if I stop and ask: "What is the computer actually doing underneath all of this?" My mental model gets surprisingly fuzzy. I know the concepts. I've used them. I've probably explained some of them before. But knowing how to use something and understanding what is actually happening underneath it are two very different things. And I want to fix that. Why I'm writing this This isn't a course, and I'm not writing this as an expert teaching computer science. These are essentially my notes while rebuilding my computer fundamentals from the ground up . I'm trying to connect the things I use every day as a software engineer with what is actually happening inside the machine. Instead of learning concepts because they're on a traditional CS syllabus, I'm starting with a question: What do I actually need to understand to reason about a production system? For me, that means being able to look at a system and understand what's happening underneath my code. Why is something slow? Where is the bottleneck? What happens when something fails? Why does adding more memory help in one situation but not another? What actually happens when two things execute concurrently? Why does a database query become slow? What happens to a simple HTTP request between two machines? I don't want to just know the answer. I want the mental model that lets me reason about the answer . The path I'm taking I'm roughly following the layers that a typical request passes through: CPU → Memory → OS → Network → Storage → Concurrency → Distributed Systems So the series will go through: 1. What Actually Happens When Code Runs Starting from the bottom: CPU mental model, memory hierarchy, and number representation . 2. Operating Systems Then moving up into processes, th
AI 资讯
Replaying real-time telemetry through a live rendering pipeline, without touching the components
I have a set of React components that render live telemetry: an attitude indicator, a moving map, tapes and gauges, a scrolling event log. They take a data source, subscribe to it, and paint whatever numbers arrive. That works for a live feed. The obvious next thing you want is replay: load a recorded session, scrub a timeline, watch the same instruments play it back. The naive version of this is a trap, and it took me a wrong turn to see why. My first instinct was that replay is a data problem, load the samples, push them into the components in order, done. It compiled, it ran, and the charts were empty. Not broken, not erroring. Empty. The instruments that show a single current value worked fine. The time-series charts sat blank while correct data flowed into them. That empty chart is the whole story of this post, because the reason it's empty is the reason replay is more interesting than it looks. The components are watching a clock you forgot about Here's the data source interface these components consume. It's small on purpose: interface TelemetryValue { timestamp : number ; // wall-clock, unix ms value : number ; channel ?: string ; } interface AltaraDataSource { subscribe ( callback : ( value : TelemetryValue ) => void ): () => void ; getHistory (): TelemetryValue []; readonly status : ConnectionStatus ; destroy (): void ; } A live source stamps each sample with Date.now() as it arrives. A time-series chart, reasonably, assumes that's what timestamps mean: it anchors its x-axis to Date.now() and draws a moving window of the last few seconds, discarding anything older than windowMs because that's off the left edge of the view. Now replay a session recorded an hour ago. Every sample carries its original timestamp, an hour in the past. The chart buffers them correctly, then asks "is this within the last few seconds of now?", the answer is no for every single sample, and it draws nothing. The data is all there. It's just an hour to the left of the visible window,
开发者
I Built 132 Free Online Tools Because I Kept Searching for Them
As a developer, I constantly end up searching for small tools to do random things. Format JSON, decode JWTs, generate UUIDs, encode URLs, compare text, convert data, and so on. I got tired of opening a different website every time, so I started building my own collection. That's CtrlTool. It currently has 132 free tools for developers and everyday tasks, with a focus on keeping them fast, simple, and easy to use. A lot of the tools process data directly in the browser when possible. https://ctrltool.wtf It's still very new, so I'd love to hear what tools you think are missing.
AI 资讯
React State Management in 2026 — Context API vs Redux Toolkit vs Zustand vs Jotai (Same Cart, Real Code + Benchmarks)
The React state-management debate has produced more bad takes than any other frontend topic. "Just use Context." "Redux is dead." "Zustand for everything." "Jotai is the future." All four are partially right and partially dangerous, depending on what you're building. So instead of arguing, I built the same shopping cart — derived totals, async fetch, localStorage persistence, three subscribing components — in all four libraries , and benchmarked it. This is the condensed version; the full guide (all four implementations with real code, the complete matrix, and the decision flow) is on my site 👇 Full guide: https://prepstack.co.in/blog/react-state-management-context-redux-toolkit-zustand-jotai-comparison-guide The one benchmark that reframes everything 1,000 components subscribed to one store. Update one value. How many re-render? Library Components re-rendered Wall-clock Context (single value) 1,000 (all) 42 ms Context (split into 5) ~200 12 ms Redux Toolkit (selectors) 1 2.1 ms Zustand (selector) 1 1.8 ms Jotai (atom) 1 1.5 ms Context without splitting re-renders the world. The other three are within margin of each other — meaning the real differences are boilerplate and DX , not render speed. The four, in one line each Context API — built-in, 0 KB, but every consumer re-renders on any change. Right for theme/auth/locale; wrong for anything busy or with many subscribers. Redux Toolkit — ~22 KB, most boilerplate, but RTK Query (caching, dedupe, invalidation), middleware, and time-travel DevTools are best-in-class. Payoff scales with app complexity. Zustand — ~3 KB, no provider, selectors built in, a full store (state + async + persistence) in ~25 lines. The modern default for most 2026 apps. Jotai — state is many small atoms, each with its own subscriber list. Smallest blast radius per update; ideal for forms and derived graphs. Real production migration (same e-commerce app) Metric Context-everywhere Redux Toolkit Zustand Initial JS (gzipped) 412 KB 438 KB 390 KB A