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

标签:#java

找到 1182 篇相关文章

AI 资讯

Building Local-First Web Apps: Parsing HTML and PDFs to Markdown in the Browser

Local-first and privacy-focused web utilities are having a massive comeback. With browser engines becoming faster and WebAssembly/Web Workers maturing, there is rarely a reason to push sensitive user documents to an external backend for simple conversions. While building MD-Convert (a zero-upload document to Markdown converter), I explored how to parse real-world documents into clean Markdown entirely on the client side. Here is a breakdown of the core architecture and libraries that make purely in-browser document processing possible. 1. Converting Web Articles with Readability + Turndown Converting messy web markup into clean Markdown involves two distinct steps: Content Extraction: Stripping ads, navbars, sidebars, and trackers. HTML-to-Markdown Transformation: Translating semantic DOM nodes into markdown tokens. Mozilla’s @mozilla/readability paired with turndown is an incredible combination for this: import { Readability } from ' @mozilla/readability ' ; import TurndownService from ' turndown ' ; function htmlToCleanMarkdown ( rawHtmlDocument , sourceUrl ) { // 1. Extract pure article content const reader = new Readability ( rawHtmlDocument ); const article = reader . parse (); if ( ! article || ! article . content ) { throw new Error ( ' Unable to extract main content ' ); } // 2. Initialize Turndown const turndownService = new TurndownService ({ headingStyle : ' atx ' , codeBlockStyle : ' fenced ' }); // Ensure image URLs remain absolute turndownService . addRule ( ' absoluteImages ' , { filter : ' img ' , replacement : ( content , node ) => { const src = node . getAttribute ( ' src ' ); const alt = node . getAttribute ( ' alt ' ) || '' ; if ( ! src ) return '' ; try { const absoluteUrl = new URL ( src , sourceUrl ). href ; return `![ ${ alt } ]( ${ absoluteUrl } )\n\n` ; } catch { return `![ ${ alt } ]( ${ src } )\n\n` ; } } }); return turndownService . turndown ( article . content ); } Offloading Heavy PDF Parsing to Web Workers Parsing large PDFs using pdf

2026-08-27 原文 →
AI 资讯

The Bug Class AI Coding Agents Keep Introducing (and How We Started Catching It in CI)

The pattern AI coding agents are good at producing a diff that works in the narrowest sense — the function still returns what the test expects. What they're not reliably good at is preserving properties nobody wrote a test for in the first place. The two we kept running into: an authorization check quietly dropped during an agent-driven refactor (nothing failed, because no test covered who was allowed to call the route — only that the route worked), and a rewritten query that behaved fine against a small dev dataset and full-table-scanned the moment it hit production data. Neither shows up in CI as it exists today. Both show up in code review only if the reviewer happens to look at exactly the right five lines out of a few hundred. What we built Agent Code Merge Gate is a free GitHub Action, now live on the GitHub Marketplace , that runs on every pull request and scans the diff specifically for those two regression classes. It runs an offline heuristic pass (fast, no external call) plus one AI-backed pass for a short Executive Summary, and posts a single comment back to the PR that updates on every push rather than piling up duplicates. Deliberately narrow scope — it's not trying to be a general linter. It covers the two failure modes we found ourselves manually re-checking for once AI-generated PRs became the majority of our merge volume. Wiring it into CI Three lines in a workflow file: ​ - name : Agent Code Merge Gate uses : avalonlabs-platform/agent-code-merge-gate@v1.0.0 ​``` { % endraw % } No signup and no config needed for the default behavior. Two inputs worth knowing about : { % raw % } `fail-on-critical : true ` turns a CRITICAL finding into an actual failed check instead of just a comment, and `comment-on-pr : false ` if you'd rather build your own notification from the raw `status` output. ## What's next Right now it's diff-scoped — it sees what changed in this PR, not the whole repo's history of how that code got there, which limits how much context it

2026-08-27 原文 →
AI 资讯

What Changes When Converting SVG to React Components (JSX & TSX)

TL;DR SVG attributes like stroke-width become strokeWidth in JSX. class → className . Numeric values become {expressions} . Inline styles become objects. xmlns and XML comments are removed. The converter outputs either JSX or TSX with SVGProps . Use automation (SVGR or SVGCode) for large icon sets. Import only what you need to keep bundle sizes small. Converting an SVG file into a React component is more than just pasting markup into a .jsx or .tsx file. React uses JSX, which is stricter than HTML/XML and requires specific changes to ensure your SVG renders correctly and remains maintainable. In this post, we’ll explore every transformation that takes place—from attribute casing to TypeScript typing—so you understand exactly what our free SVG to React converter does under the hood. What Actually Changes? Kebab‑case Attributes Become camelCase SVG uses attributes like stroke-width , fill-rule , and clip-path . JSX requires property names that are valid JavaScript identifiers, so these become: SVG Attribute React JSX stroke-width strokeWidth stroke-linecap strokeLinecap stroke-linejoin strokeLinejoin fill-rule fillRule clip-path clipPath font-size fontSize stroke-dasharray strokeDasharray class Becomes className In SVG you write class="icon" , but in JSX you must use className="icon" because class is a reserved word in JavaScript. Numeric Attributes Are Converted to Expressions React treats string values differently from numbers. For numeric SVG attributes like width , height , x , y , cx , r , etc., the converter outputs {value} instead of "value" . <circle cx="12" cy="12" r="10" /> becomes: < circle cx = { 12 } cy = { 12 } r = { 10 } /> Inline Styles Become Objects If your SVG uses style="fill: red; stroke: blue;" , it must be converted to a JavaScript object: style = {{ fill : ' red ' , stroke : ' blue ' }} xmlns and Namespace Declarations Are Removed React automatically uses the correct SVG namespace, so xmlns and other XML namespace declarations are unnecessary a

2026-08-26 原文 →
AI 资讯

Writing QUIC in Pure Java

I maintain gumdrop , an async, non-blocking Java server framework. Last year I wanted to add HTTP/3 support, and ran into a wall: the Java ecosystem essentially doesn't have QUIC. The JDK's own experimental support (JEP 517) is client-only. Netty gets HTTP/3 by shelling out to quiche + BoringSSL over JNI — which works, but you're back to native builds, platform-specific binaries, and a C library sitting underneath your "pure Java" framework. I used that approach first. It was clumsy enough that I went looking for a pure-Java alternative. There's exactly one: Kwik. But Kwik is blocking per connection — one thread per QUIC connection. That's a non-starter for a framework built around single-threaded selector loops handling tens of thousands of concurrent connections. So I wrote a QUIC implementation from scratch: packet protection, loss detection and NewReno congestion control, connection migration, 0-RTT, QPACK, an HTTP/3 client and server — all driven by the same non-blocking event loop as everything else in gumdrop. Collaboration note: TLS 1.3 comes from Agent15 — also from Kwik's author, Peter Doornbosch, but just the handshake layer, not the connection model. We're currently working together on making PQC — hybrid key exchange and signatures — the default there. Why the thread model matters The reason this mattered beyond HTTP/3: gumdrop isn't a web framework with QUIC bolted on, it's a general async I/O framework, and QUIC is just a transport. One thread per connection is exactly the model gumdrop exists to avoid — it caps concurrency at your thread pool, not your file descriptors, and it's the reason a "just use Kwik" fix was never really on the table. The same QUIC stack backs DNS-over-QUIC (DoQ) as a first-class DNS transport alongside DoT, DoH, UDP, and TCP — and the DNS resolver itself is fully async, with no blocking InetAddress.getByName() anywhere in the I/O path, which is its own small miracle in Java. HTTP, SMTP, IMAP, POP3, FTP, MQTT, SOCKS — it's the

2026-08-26 原文 →
AI 资讯

A Unified KPI Framework for Automation Testing with Playwright & JavaScript

Measuring the impact of test automation goes beyond simple pass/fail ratios. To demonstrate real engineering excellence and business value, automation metrics must capture execution speed, suite stability, test coverage, maintenance cost, and CI/CD integration. Here is a comprehensive, unified KPI framework designed specifically for Playwright & JavaScript automation suites. 📊 Executive KPI Targets Category Metric Target Execution Speed Runtime Reduction 50% ↓ Efficiency Throughput +40% ↑ Stability Flaky Tests < 3% Reliability Retry Dependency < 5% Coverage Automation Coverage 80%+ Quality Defect Leakage 20–30% ↓ Productivity Script Dev Time 30% ↓ CI/CD Pipeline Time 40% ↓ ROI Automation ROI Positive (3–6 months) Cost Manual Effort Reduction 30–50% ↓ 1. Execution Efficiency & Speed Test Execution Time Reduction: Target 40–60% reduction vs legacy frameworks like Selenium. $$\text{Reduction \%} = \frac{\text{Old Time} - \text{New Time}}{\text{Old Time}} \times 100$$ Parallel Execution Efficiency: Measure tests executed per hour and parallel thread utilization. $$\text{Efficiency \%} = \frac{\text{Sequential Time} - \text{Parallel Time}}{\text{Sequential Time}} \times 100$$ Test Throughput: Maximize total test cases executed per CI window. CI/CD Pipeline Cycle Time: Aim for a 30–40% total reduction in build + test execution duration. 2. Stability & Reliability Flaky Test Rate: Keep flaky tests under 2–3% by leveraging Playwright's native auto-waiting and resilient locators. $$\text{Flakiness \%} = \frac{\text{Flaky Tests}}{\text{Total Tests}} \times 100$$ Retry Dependency Ratio: Track the percentage of tests passing only after retries to minimize false positives. Failure Root Cause Accuracy: Target >90% of test failures pointing directly to genuine application defects rather than script instability. 3. Coverage Metrics Automation Coverage: Maintain 80%+ regression coverage across all functional scenarios. Cross-Browser & Device Coverage: Measure test runs across Chromi

2026-08-26 原文 →
AI 资讯

Building a Client-Side N-gram Utility for Text Structure and Phrase Audit

Hey DEV community! 👋 When writing technical documentation, user guides, or long-form informational articles, maintaining a clear and engaging reading style is highly important. However, as writers, we often fall into repetitive phrasing habits without realizing it. Traditional word counters only track isolated, single words. To evaluate multi-word phrases and understand the flow of our writing, we need a different approach. This is where an N-gram analysis becomes highly useful. To provide a safe and private solution for content editors, I built a lightweight, entirely client-side N-gram Analyzer . In this post, we will explore the technical implementation of this utility, how to handle text segmentation in JavaScript, and why local browser processing is a reliable choice for data privacy. What is an N-gram? In computational linguistics and text processing, an N-gram is a contiguous sequence of $n$ items (usually words) from a given sample of text. A Unigram represents single words ($n=1$). A Bigram represents two-word phrases ($n=2$). A Trigram represents three-word phrases ($n=3$). A 4-gram represents four-word phrases ($n=4$). Analyzing these combinations helps developers and content creators identify repetitive phrases, evaluate vocabulary diversity, and check if the thematic distribution of a document aligns with its target focus. The Client-Side Approach: Privacy and Data Isolation Many online text tools process user inputs on backend servers. This setup introduces a significant privacy risk if you are analyzing sensitive internal documentation, unpublished drafts, or proprietary code comments. By executing the lexical parsing entirely within the user's browser, we keep the processing local. The text never travels across the network, and there are no external database logs. The local device handles the entire operation. Implementing the N-gram Extraction in JavaScript Let's look at the core logic. To build an N-gram extractor, the utility must perform three ke

2026-08-26 原文 →
AI 资讯

Building an Automated QA KPI Dashboard for Playwright & BDD Pipelines

Tracking test automation metrics manually often leads to outdated figures and missed engineering gaps. To solve this, automated reporting directly from your test suites—such as Playwright and Cucumber—provides clear visibility into health, execution speed, and coverage. Below is a breakdown of how to structure an Automation KPI Dashboard to streamline test metrics, track trends, and establish actionable engineering goals. Executive Summary Dashboard KPI Metric Target Current Value Status Trend Total Test Cases 100% coverage 85% 🟡 Partial ↗️ Up Automated Test Coverage 90%+ 78% 🟡 Partial ↗️ Up Pass Rate (Last Run) 95%+ 92% 🟡 Partial ↔️ Stable Avg. Execution Time < 30 min 28 min 🟢 Good ↘️ Down Flaky Test Rate < 2% 1.5% 🟢 Good ↔️ Stable Defects Detected — 3 🟡 Review ↔️ Stable CI/CD Pipeline Success 100% 98% 🟡 Partial ↗️ Up Key Metric Breakdowns 1. Coverage & Execution Total Test Suite: 120 tests (94 Automated, 26 Manual). Latest Run (2026-05-29): 94 executed — 87 passed, 7 failed, 0 skipped. 2. Flakiness Tracking Flaky Tests (Last 10 Runs): 2 scenarios identified. Top Offenders: Scenario A: UI timeout issues. Scenario B: Data synchronization lag. 3. Defect Detection & CI/CD Performance Defect Lifecycle: 3 opened, 1 closed (Avg. resolution time: 2 days). Pipeline Health: 98% success rate, 12 min average build time. Primary Cause of Pipeline Failure: Dependency resolution errors. Execution & Pass Rate Trends (Last 6 Runs) Run Date Pass % Fail % Flaky % Duration (min) 2026-05-29 92% 8% 2% 28 2026-05-28 91% 9% 2% 29 2026-05-27 90% 10% 3% 30 2026-05-26 89% 11% 3% 31 2026-05-25 88% 12% 4% 32 2026-05-24 87% 13% 4% 33 Next Engineering Action Items Automation Expansion: Push total automated coverage past 90%. Flakiness Mitigation: Refactor explicit waits and isolation for UI timeout and data sync scenarios. Pipeline Stability: Resolve dependency caching errors to bring CI/CD success to 100%. Optimization: Lower execution suite duration below 25 minutes using parallel run setups.

2026-08-26 原文 →
AI 资讯

Portfolio Update, I Guess

This isn't my main piece for the week, it's more of a "contributes nothing to knowledge" kind of post. Last week I took another look at my portfolio and thought, "Hey, why not make this feel a bit more like me?" So I set out to give it a makeover, stuffed as much of my personality into it as I could, and et voilà, done. The old one was kinda too formal. TL;DR: I gave my portfolio a personality transplant. If you'd rather just look than read: a-thedeveloper.vercel.app Vibe / Tone Option By default, the professional option is enabled. But if you're not too sensitive and want to have a little fun, try toggling over to the unfiltered version of me, lol. I don't actually talk like that in real life anymore, but having grown up speaking English, that's pretty much how I sounded back in my teenage years. I was a grumpy teenager like everyone else, the difference is I was extra grumpy compared to most. 😭 I also lost access to my Instagram account, so all of it is still sitting there, public, for anyone to see. Every day I hope that account just quietly gets deleted. And if you're wondering whether that same energy has been erased, nope, it's still very much here. I just keep it contained to appropriate contexts now, lol. I also found these while digging through my old microsoft drive, weird 16 year old me stuff. I actually said this in a debate, by the way. Can't remember if my team won that one or lost. Weather Options Kinda irrelevant to how it actually describes my portfolio, but I initially wanted to make rainy the only option, because I'm a big fan of dark, gloomy, cloudy weather — the kind that makes England look like heaven to me. 😭 Then I thought, why not just have all of them? So now each weather option comes with its own falling elements based on the selection, plus music that I feel fits the atmosphere. Again, it doesn't really serve any practical purpose, but I think it's a nice little touch to have, haha. DEV Writing Views with an API Key When I joined DEV in 2

2026-08-26 原文 →
AI 资讯

I built plugins for three editors. Everywhere, you're a guest in someone else's house

Over the last while I've built integrations for three places where people work with text and images: Obsidian , VS Code, and Figma. Doing a few of them back to back, I noticed something you don't see from a single one. They're all desktop apps. For your integration to exist at all, the person first installs a program on their machine, and then, inside it, your plugin. You're not writing for the web. You're writing code locked inside someone else's app — and each app has its own runtime, its own rules, and its own wall for you to walk into. The web trained us to think an HTTP request is one line. Inside someone else's sandbox, it turns out even that has to be earned. Figma was the strictest host of the three. I'll tell it through Figma, because it's locked down tighter than Obsidian or VS Code, and everything shows up on it at once. The task was almost comically simple: select a frame, write a caption, pick your social accounts, publish — without exporting the image and opening a second app. We already had the publishing API, so I expected the Figma side to be small. And it was: the main plugin file is 120 lines. The work wasn't in them. It was around them. Figma gives you bytes, not a file The first version came together easily. When the selection changes, the plugin checks whether there's one exportable node and tells the UI what it found. For the preview it exports a small copy; for publishing, separately, at 2×. const bytes = await nodes [ 0 ]. exportAsync ({ format : " PNG " , constraint : { type : " SCALE " , value : 2 }, }); 2× because the image still has a journey ahead of it: social networks recompress what you upload, and small text on a design goes noticeably softer by the time it lands in a feed. Then the first quirk of the foreign house. Figma hands the plugin not a file but raw PNG bytes — exportAsync() returns a Uint8Array . Our normal API won't eat that — it doesn't take a giant image stuffed into a JSON body. It creates a post first, hands the client

2026-08-26 原文 →
AI 资讯

How I "Vibe-Coded" a Privacy-First, Client-Side Base64 Tool (Deep-Dive into Unicode Handling in JS)

Hey DEV community! 👋 As developers, we handle Base64 encoding and decoding almost daily—whether we're debugging API payloads, formatting authorization headers, or embedding small graphic assets directly into stylesheets. However, many online translation utilities process your inputs on their backend servers. If you are dealing with sensitive configuration parameters, internal logs, or keys, pasting that data into a third-party web tool is a clear data privacy risk. To solve this, I decided to "vibe-code" a lightweight, strictly browser-based, privacy-oriented Base64 Encoder & Decoder . In this post, we will look at how this utility was built using AI assistance and vanilla JavaScript, along with the core logic to handle common encoding pitfalls. What is "Vibe Coding"? For those unfamiliar with the term, vibe coding is the practice of leveraging modern generative AI models to handle the bulk of the standard layout and event listeners, while you focus on the core logic, user experience, and privacy requirements. Instead of writing every CSS class and event listener manually, I guided an AI assistant to generate a clean, responsive layout using a standard grid framework, while ensuring that the core translation logic resides strictly in the user's browser. The Pitfall of Traditional JS Base64 (and How to Fix It) If you have ever used native JavaScript btoa() and atob() functions, you might know they struggle with Unicode/UTF-8 characters (like emojis or non-Latin scripts). Running this in your console will throw an error: btoa ( " Xin chào! 🚀 " ); // Throws "Uncaught DOMException" To resolve this during the development process, the utility implements modern TextEncoder and TextDecoder APIs. This approach converts strings into binary byte arrays before encoding them, avoiding exceptions. The Client-Side Implementation Here is the clean JavaScript snippet used for bidirectional encoding and decoding: function processBase64 ( action , inputValue ) { try { if ( action ===

2026-08-26 原文 →
AI 资讯

The function you wrote last month is a third-party API

There is a habit I have for other people's libraries that I do not have for my own code: before I call something, I read what it returns. With my own functions I skip that, because I wrote them, so I know. Three times in three days that turned out to be false, and the third time I caught it before it cost anything only because I had started treating my own modules like somebody else's. The version I had already been burned by twice I maintain qbofile , a set of browser-based converters between the file formats accounting software uses. It is a small codebase: a parser per input format, a generator per output format, and pages that wire one to the other. Wiring a new pair felt like plumbing, so I estimated it like plumbing. Two new pages, both reusing an existing parser and an existing generator: no new code. I said that out loud before opening either end. The generator had no column for the thing the parser produced. The parser could read the category a user had assigned to each transaction; the CSV generator emitted six fixed columns and category was not one of them. Not a bug — it had simply never needed one, because the format it was originally written for does not carry categories. That is a strange kind of wrong. Nothing was broken. The code did exactly what it always had. My model of it was built from the function name. The same evening, in the same pair of modules, the second one: L . push ( `P ${ sanitizeText ( tx . description )} ` ); P is the payee field in that output format. M is the memo. Two fields, and upstream, description was defined as memo || payee . So for any transaction that had a memo, the memo took the payee slot and the actual payee was dropped. Silently — the file is valid, it imports fine, and the missing name never announces itself. The two minutes that caught the third one After the second one I wrote down a rule and did not really believe I needed it: before wiring two components together, open both ends and read what actually crosses.

2026-08-26 原文 →
AI 资讯

Building a Unicode Text Transformer with Pure Character Maps

I built Unicode Text Tools , a free site with a bunch of text converters — superscript, subscript, bubble/circled text, upside-down text, small caps, and more. Type something, get it transformed, copy it out. The whole engine is one dependency-free JS file built entirely from character mapping tables . No AI, no server, no libraries. Here's why that's the right architecture for this class of tool, and how the trickier conversions work. The core idea: it's all just lookup tables Every conversion on the site is a function that maps each input character to a Unicode character (or does a small transform). The simplest cases are pure dictionaries: // Superscript (full a-z, 0-9) var SUP = { a : ' ᵃ ' , b : ' ᵇ ' , c : ' ᶜ ' , d : ' ᵈ ' , e : ' ᵉ ' , f : ' ᶠ ' , g : ' ᵍ ' , h : ' ʰ ' , i : ' ⁱ ' , j : ' ʲ ' , k : ' ᵏ ' , l : ' ˡ ' , m : ' ᵐ ' , n : ' ⁿ ' , o : ' ᵒ ' , p : ' ᵖ ' , q : ' ᵠ ' , r : ' ʳ ' , s : ' ˢ ' , t : ' ᵗ ' , u : ' ᵘ ' , v : ' ᵛ ' , w : ' ʷ ' , x : ' ˣ ' , y : ' ʸ ' , z : ' ᶻ ' , ' 0 ' : ' ⁰ ' , ' 1 ' : ' ¹ ' , ' 2 ' : ' ² ' , ' 3 ' : ' ³ ' , ' 4 ' : ' ⁴ ' , ' 5 ' : ' ⁵ ' , ' 6 ' : ' ⁶ ' , ' 7 ' : ' ⁷ ' , ' 8 ' : ' ⁸ ' , ' 9 ' : ' ⁹ ' , ' + ' : ' ⁺ ' , ' - ' : ' ⁻ ' , ' = ' : ' ⁼ ' , ' ( ' : ' ⁽ ' , ' ) ' : ' ⁾ ' }; The transform itself is trivial — walk the string, look up each char, append the mapped value (or the original char if unmapped). The work is in the tables: knowing which Unicode blocks exist, what's 1:1 reversible, and what's incomplete. The Unicode reality check Here's the thing nobody tells you about Unicode text transformation: the blocks are inconsistent. Superscript : complete for a-z and 0-9 — fully reversible. Subscript : incomplete — there's no subscript b , c , d , f , g , q , w , y , z . If you map an input with those letters, you have to decide what to do with them. Small caps : x has no small-cap form ( ꞯ is the closest, but it's a different character and looks wrong). j is a problem too — the Unicode small-cap ᴊ collides visually

2026-08-26 原文 →
AI 资讯

MyAnimeList-Module (NPM)

MyAnimeList Module This module is neither affiliated with nor endorsed by MyAnimeList. All data returned by this module is provided by MyAnimeList. Version 1.0.5 Installation Install myanimelist-module with npm npm install myanimelist-module Usage/Examples const { MyAnimeList } = require ( ' myanimelist-module ' ) const mal = new MyAnimeList ({ client_id : `YOUR_MAL_CLIENT_ID` // Get it here: https://myanimelist.net/apiconfig }) async function test () { const response = await mal . getAnimeInfo ({ name : " Anime name " }) if ( response . error ) { console . error ( response . error ) } else { console . log ( response . datas ) } } test () All functions new MyAnimeList() Parameter Type Description client_id string Required . Your MAL Client ID getAnimeInfo() Parameter Type Description name string Required . fields [array] Optional. More information in the "Available fields" section. limit number Optional. Number of items in the response. (Maximum of 100) offset number Optional. Default : 0 nsfw boolean Optional. Default: false getAnimeInfoByURL() Parameter Type Description api_url string Required . You must use any valid MyAnimeList API link. It also works with older responses via response.datas.paging.next and response.datas.paging.previous . getSpecificAnimeInfo() Parameter Type Description name string Required . fields [array] Optional. More information in the "Available fields" section. nsfw boolean Optional. Default: false getAnimeInfoByID() Parameter Type Description id string Required . fields [array] Optional. More information in the "Available fields" section. nsfw boolean Optional. Default: false getAnimeRanking() Parameter Type Description type string Optional. More information in the "Available ranking types" section. fields [array] Optional. More information in the "Available fields" section. limit number Optional. Number of items in the response. (Maximum of 500) offset number Optional. Default : 0 nsfw boolean Optional. Default: false getSeasonalAnime(

2026-08-26 原文 →
AI 资讯

Amazon, Temu, and AliExpress already have visual search. Desktop just hides it.

I shop on a laptop. A lamp on Amazon that costs too much. A jacket in a listing photo. Something I saw on eBay and wanted to check on Temu. On my phone, that is a camera tap. On desktop, the camera icon is mostly missing. So I built SameSame , a browser extension I still use every day. It does not send your photo to a third-party reverse-image API. It opens the visual search each store already runs - the same one their mobile apps and some out-of-stock flows use - from the page you are already on. The desktop gap Visual product search is not new. Amazon Lens, Temu camera search, AliExpress image search: they work because they search inside that store's catalog. That is different from Google Lens, which searches the open web and often returns a mix of blogs, pins, and shopping links. The catch is where those tools live. Amazon's image search is a first-class feature in the shopping app. On amazon.com in a browser, it is easy to miss or simply not there, depending on the page. Temu and AliExpress follow the same pattern: obvious on mobile, buried or absent on desktop. If you want to search by image from a laptop, the usual advice is: save the image, open the store app or a reverse-image site, upload, then repeat for the next store. That is a lot of friction for something the store already knows how to do. The searches were already there I did not invent a new matcher. Amazon, Temu, and AliExpress already run visual search against their own catalogs. On mobile that is the camera in the search bar. The same capability shows up in other places, including some out-of-stock and similar-items flows on the web. When a listing is unavailable, you have sometimes seen visually close alternatives. That is not a coincidence. The catalog search is already wired up. Desktop shopping just does not put a camera on every page. Those endpoints are not a public developer API you sign up for. They are the stores' own visual search, used by their apps and a handful of desktop pages, mostl

2026-08-25 原文 →
AI 资讯

React Form Backends Compared: Serverless Functions vs. Form-as-a-Service

React Form Backends Compared: Serverless Functions vs. Form-as-a-Service React makes building a form straightforward. What happens after onSubmit is a different question: you still need somewhere to validate, process, store, or forward the submission. Two common approaches are writing a serverless function yourself or using a hosted form backend such as onsubmit.dev (form backend). This article compares the two, using Vercel/Netlify-style functions for the DIY approach and onsubmit.dev with its React integration as the managed example. The basic problem Imagine a typical contact form: function ContactForm () { return ( < form > < input name = "email" type = "email" required /> < textarea name = "message" required /> < button type = "submit" > Send </ button > </ form > ); } The React component is only the UI. A real application usually needs backend behavior too: accepting the HTTP request validating and sanitizing input handling errors preventing abuse or spam delivering or storing the submission keeping credentials and other secrets off the client There are two broad ways to get that backend. Option 1: Build a serverless function With platforms such as Vercel and Netlify, you can create an HTTP function alongside your application and have your React form submit to it. Conceptually, the architecture looks like this: React form | v Your serverless function | +--> validation +--> email provider +--> database +--> other services The main advantage is control. Your function owns the request lifecycle, so you decide precisely how data is validated, transformed, authenticated, stored, and forwarded. If a submission needs to update PostgreSQL, call an internal API, enqueue a job, and return application-specific data, a custom backend is usually the natural solution. Serverless functions can also reduce product-level vendor lock-in. Although platforms have their own deployment conventions, HTTP handlers and their business logic are generally portable with some work. The tr

2026-08-25 原文 →
AI 资讯

Java News Roundup: JDK 27-RC1, OpenJDK JEPs, Jakarta EE, BellSoft, Helidon, Micrometer, Tika 4.0

This week's Java roundup for August 17th, 2026, features news highlighting: the first release candidate of JDK 27; JEP 541 and JEP 540 targeted for JDK 28; the GA release of Apache Tika 4.0; a maintenance release of Helidon; the first milestone releases of Micrometer Metrics 1.18 and Micrometer Tracing 1.8; an update on Jakarta EE 12; and BellSoft Critical Security Patch Updates. By Michael Redlich

2026-08-25 原文 →
AI 资讯

I removed the LLM call and replaced it with 200 lines of template code

The feature was a letter generator. Somebody fills in a few fields and gets a finished letter of recommendation, resignation letter or notice letter, in plain text, ready to paste into an email. The obvious build is a prompt and a model call. I wrote the deterministic version instead: a pure function, about two hundred lines, no network, no key, no tokens. I want to lay out the reasoning, because "just call a model" is the default now and the default is not always right. The three reasons, in order of weight 1. The output is short and the shape is fixed. A recommendation letter is a date block, a greeting, three or four paragraphs, a sign off and a name. There is no structural variation to discover. Generation is valuable when the space of good outputs is large and you cannot enumerate it. Here the space is small enough to write down, and once you have written it down the model is doing an expensive approximation of a switch statement. 2. It is a legal-adjacent document. Not legal advice, but it goes into an employment record. A resignation letter that invents a notice period, or a reference that invents a fact about a person, is a real problem for the person who sent it. Templates cannot hallucinate. Everything specific in the output either came from a form field or is a sentence I wrote and can be held to. 3. Zero marginal cost changes what the product can be. This is the one that actually decided it. A model call costs money per use, and anything that costs money per use needs an account, a rate limit and eventually a card. A pure function costs nothing, so the tool can stay open with no signup, forever, without a business case. That is a product decision expressed as an architecture decision, and it only works if the code path is free. What the code looks like The whole engine is one exported function over one input type. export type LetterKind = ' resignation ' | ' notice ' | ' recommendation ' ; export type LetterTone = ' formal ' | ' warm ' | ' brief ' ; expo

2026-08-25 原文 →
AI 资讯

Why every BaZi calculator disagrees with the almanac

Every Four Pillars calculator — saju in Korea, BaZi in China — agrees on the easy 95% of the job. Feed it a birth date and it maps that instant onto a traditional calendar: four pillars, each a heavenly stem paired with an earthly branch. The remaining 5% is boundaries. And at the boundaries, nearly all of them quietly disagree with the printed almanac they claim to reproduce. I maintain a saju reading service, and getting these four cases right was most of the actual engineering. Here they are, with the failing inputs. 1. A solar term is an instant, not a date The year pillar does not turn on January 1, and not on lunar new year either. It turns at 입춘 (ipchun, "start of spring") — one of the 24 solar terms, defined by the sun's apparent longitude. In 2024 that moment was February 4, 16:27 KST . A calculator that applies solar terms at day granularity says "February 4 → new year pillar" and hands the wrong year to everyone born that morning. npx k-saju 2024-02-04 04:00 # year 癸卯 — still the old year pillar, because 04:00 < 16:27 The fix is unglamorous: store term boundaries as instants and compare instants. The subtlety is that this correction applies to the year and month pillars only — the day pillar runs on its own sexagenary count and must not be touched. 2. The 23:00 hour belongs to two days at once Traditional practice starts the day at 23:00, not midnight — the hour of the Rat (자시). So for a birth at 23:31, there are two defensible answers about which day's stem the hour pillar derives from, and schools split on it. The convention this engine declares: the day pillar keeps clock midnight , while the hour stem takes the next day's stem (the 야자시 rule). npx k-saju 2000-05-15 23:31 # day 癸酉, hour 甲子 I am not claiming this is the One True Rule. I am claiming it should be written down. Most tools pick a side in silence, which is how two calculators give one person two charts and neither can explain why. 3. The clock is not the sun Korea keeps time on the 135°E meri

2026-08-25 原文 →
开发者

Nuxt 4.5: Experimental SSR Streaming, Vite 8 and an Rsbuild-Powered Rspack Builder

Nuxt has released version 4.5, featuring updates such as a switch to Vite 8, a new Rspack 2 builder, and experimental SSR streaming. This streaming enhances Time to First Byte by flushing the HTML shell instantly. The release also includes a stable error code system and new composables, alongside important upgrade instructions for developers moving from earlier versions. By Daniel Curtis

2026-08-25 原文 →
AI 资讯

Why your hreflang tags are being ignored

Originally published on the WeLocale blog . Most SEO work is a matter of degree. You improve a title, you gain a little. hreflang is not like that. It either forms a valid set that search engines act on, or it does nothing at all, and the failure is completely silent. No warning, no penalty, no message in Search Console telling you the tags you carefully added are being discarded. We build a translation widget, which means we generate hreflang tags for other people's sites. This post is what we have learned about why they get ignored, including the parts where our own approach has real limits. The rule that breaks most setups hreflang is not a property of a page. It is a property of a set of pages, and every page in that set has to agree. If your English page says the German version is at /de/ , the German page has to say the English version is at / . If it does not, the declaration is one-way, and one-way declarations get dropped. Google calls these return links and treats their absence as a reason to distrust the whole set. This is why hreflang fails in a way that feels unfair. Every individual page looks correct when you inspect it. The problem only exists in the relationship between pages, which is exactly the thing you cannot see by viewing source on one URL. The corollary catches people too: each page must list itself . A German page whose tags mention English and French but not German is an incomplete set. Incomplete sets get dropped. The other four failure modes en-UK. The language code comes from ISO 639-1 and the region code from ISO 3166-1. In ISO 3166-1 the United Kingdom is GB. There is no UK. The tag is silently invalid, and it is easily the most common hreflang error on the web. Same class of mistake: lowercase regions, uppercase languages, and a region with no language at all. URLs that redirect. hreflang has to point at the final URL. If it points at http and you redirect to https, or it omits a trailing slash your server adds, the target is a redir

2026-08-25 原文 →