AI 资讯
How to Add Watermarks to PDFs in the Browser with Vue 3 and pdf-lib
Watermarking a PDF — adding semi-transparent text over pages — sounds like something only desktop software handles. But with pdf-lib and a bit of canvas math, you can build a fully browser-based watermark tool. This post walks through the implementation details, including text rendering, rotation, and multi-page support. Why client-side? Traditional watermark tools upload your file, process it on a server, and send the result back. For documents that might be confidential or contain sensitive information, this introduces an unnecessary privacy risk. A browser-based approach: Processes everything locally Keeps files on the user's device Works offline after loading Avoids server-side bandwidth costs The stack Vue 3 + Composition API pdf-lib for PDF manipulation and watermarks PDF.js ( pdfjs-dist ) for preview rendering Vite for bundling Adding a text watermark pdf-lib provides a built-in PDFDocument.embedFont() method for custom fonts and page.drawText() for placing text. Here's how to add a rotated, semi-transparent watermark across all pages: < script setup lang= "ts" > import { ref } from ' vue ' import { PDFDocument , rgb , StandardFonts } from ' pdf-lib ' const file = ref < File | null > ( null ) const watermarkText = ref ( ' DRAFT ' ) const opacity = ref ( 0.3 ) const fontSize = ref ( 72 ) const rotationDeg = ref ( - 45 ) const applying = ref ( false ) async function handleFileUpload ( selected : File ) { file . value = selected } async function applyWatermark () { if ( ! file . value ) return applying . value = true try { const arrayBuffer = await file . value . arrayBuffer () const pdfDoc = await PDFDocument . load ( arrayBuffer ) // Embed the Helvetica font — required for correct rendering const helveticaFont = await pdfDoc . embedFont ( StandardFonts . HelveticaBold ) const pages = pdfDoc . getPages () pages . forEach (( page ) => { const { width , height } = page . getSize () page . drawText ( watermarkText . value , { x : ( width - helveticaFont . widthOfT
AI 资讯
Why You Should Try Nano Kit
Hi, my name is Dan, I'm a frontend engineer and open-source maintainer. I've spent the last couple of years building Nano Kit — a lightweight, modular state management ecosystem for modern web apps: signals-based stores , a router , data fetching , i18n , and SSR support , all built on the same tiny reactive core. It recently hit 1.0 , and in this post I want to give you four honest reasons to try it. 1. It's fast At the heart of Nano Kit is a push-pull reactivity system based on the algorithm from alien-signals — one of the fastest signal implementations in the JavaScript ecosystem. I didn't use alien-signals directly, though. Nano Kit needed things it doesn't provide, so I built a dedicated fork called Agera : Signal lifecycles — you can listen to signal activation and deactivation, which powers Nano Kit's mountable stores (run setup logic on first listener, clean up on last). Real tree-shaking — Agera is designed so that only the code you use ends up in your bundle; alien-signals is not well tree-shakable. The result keeps almost all of alien-signals' raw speed. Here is how @nano_kit/store compares to other popular state management libraries in a reactivity benchmark : Library Latency avg (ns) Throughput avg (ops/s) alien-signals 294.00 ± 2.24% 3,559,763 @nano_kit/store 303.55 ± 0.75% 3,365,816 svelte/store 428.58 ± 0.61% 2,479,118 rxjs 454.74 ± 0.07% 2,250,397 nanostores 1,373.2 ± 5.96% 952,399 mobx 3,474.7 ± 1.86% 306,094 valtio 5,041.3 ± 11.46% 254,109 jotai 9,454.6 ± 16.45% 157,853 effector 24,885 ± 11.78% 62,744 @reatom/core 59,430 ± 15.61% 22,741 Benchmark was run on AMD Ryzen 5 PRO 3400G with Node.js v24.14.1 That's ~3.5× faster than nanostores and an order of magnitude faster than most atomic state managers — while shipping lifecycles and mountable stores on top. 2. It's small Nano Kit exists largely because of Nano Stores . I love its philosophy: atomic stores, mountable resources, logic moved out of components, and an obsessive focus on bundle size. Nan
AI 资讯
I Rebuilt the 90s Tamagotchi for the Browser — And Accidentally Learned More About State Machines Than Any Tutorial Taught Me
In 1996, Bandai sold 82 million Tamagotchis. Kids carried egg-shaped plastic keychains everywhere, frantically pressing three buttons to feed, clean, and play with a pixelated blob that would literally die if you ignored it during math class. It was the first time millions of people felt genuine emotional attachment to a piece of software. 30 years later, I rebuilt that entire experience — in the browser, with TypeScript, zero dependencies, completely open source. No app store. No download. No install. Just open a tab and adopt your pet. And in the process, I learned more about state machines, game loops, and emotional design than any computer science course ever taught me. Why Build a Virtual Pet in 2025? Three reasons: 1. Nostalgia Is a Distribution Hack People share things that trigger childhood memories. It's not rational — it's emotional. A browser-based Tamagotchi hits a nerve that no todo app or dashboard ever will. When I shared an early prototype, the response wasn't "cool tech stack." It was: "OH MY GOD I used to cry when mine died in second grade" That emotional reaction is worth more than any Product Hunt launch. 2. Game State Machines Are Criminally Underrated Every tutorial teaches state machines with traffic lights or toggle buttons. Boring. Useless. Forgettable. A virtual pet has dozens of interconnected states , real-time decay, evolution paths, conditional transitions, and edge cases that force you to actually think about state architecture. After building this, implementing complex UI flows in production apps felt trivial. 3. Not Everything Needs to Be a SaaS The indie dev world is obsessed with "revenue-generating side projects." Sometimes you should build something purely because it makes people smile. The best projects are the ones you'd use even if nobody else existed. Meet Your New Pet When you open Tamagochi, you get an egg. It hatches. A tiny pixelated creature appears. It has needs. Meet them, and it thrives. Ignore them, and... well, game
开发者
Stop Running `terraform apply` From Your Laptop: Building Your First Terraform CI/CD Pipeline with GitHub Actions
One of the biggest mistakes beginners make when learning Terraform is treating their local machine as the deployment server. A typical workflow looks like this: terraform init terraform plan terraform apply While this approach is perfectly fine for learning, it quickly becomes problematic when working on real-world projects with multiple engineers. Consider these questions: Who deployed the infrastructure? Was the infrastructure reviewed before deployment? Can someone else reproduce the deployment? What happens if the engineer's laptop is lost or misconfigured? How do we know exactly what changed? These are some of the reasons Infrastructure as Code (IaC) is almost always integrated with Continuous Integration and Continuous Deployment (CI/CD) pipelines in professional environments. In this article, we'll build a simple Terraform CI/CD pipeline using GitHub Actions. Instead of focusing only on the YAML syntax, we'll first understand why each stage exists and how they work together to produce safe, repeatable infrastructure deployments. What is Terraform CI/CD? Terraform CI/CD is the process of automating the validation, planning, and deployment of infrastructure whenever changes are made to Terraform code. Instead of running Terraform commands manually from a developer's laptop, a CI/CD platform executes those commands automatically in a controlled environment. The workflow typically looks like this: Developer │ ▼ Git Push │ ▼ GitHub Repository │ ▼ GitHub Actions │ ▼ Terraform Init │ ▼ Terraform Validate │ ▼ Terraform Plan │ ▼ Manual Approval │ ▼ Terraform Apply │ ▼ AWS Infrastructure This approach provides consistency, visibility, and security while reducing the chances of human error. Why Not Run Terraform Manually? Running Terraform from your laptop works well for personal projects, but it introduces several risks in a team environment. Manual Deployment CI/CD Deployment Requires someone to remember every command Runs automatically Easy to skip validation Validat
AI 资讯
I Built a Self-Hostable URL Shortener Because Bitly Now Charges $10/Month for Basic Links
In 2023, Bitly's free plan gave you 100 links per month with full analytics. In 2025, they slashed it to 5 links per month . Five. And they started showing full-screen interstitial ads before redirecting — even on links you created years ago. In 2026, their cheapest paid plan is $10/month — just to shorten links without ads. I was paying $120/year to turn long URLs into short ones. A problem that takes 3 lines of code to solve. So I solved it myself. For free. Forever. Introducing ZipLink ZipLink is a fast, self-hostable URL shortener built with Next.js and Firebase. Deploy it once, use it forever. No monthly fees. No link limits. No ads. No "upgrade to unlock analytics." Your links. Your data. Your domain. Your rules. https://yourdomain.com/abc123 → https://some-really-long-url.com/path/to/thing?utm=whatever That's it. That's the product. Except you own it completely. The State of URL Shorteners in 2026 (It's Embarrassing) Let me show you what the "industry leaders" are charging for what is essentially a database lookup: The Comparison Table Feature Bitly Rebrandly Short.io Dub.co TinyURL Pro ZipLink Monthly cost $10-$300/mo $13-$349/mo $19-$149/mo $24-$299/mo $9.99/mo $0 Annual cost $96-$2,388/yr $156-$4,188/yr $228-$1,788/yr $288-$3,588/yr $119.88/yr $0 Free tier links 5/month 10/month 1,000 total 25/month Unlimited (no analytics) Unlimited Custom domain Paid only Free (1 domain) Required Paid only Paid only Yes (yours) Click analytics Paid only Paid only Free (basic) Paid only Paid only Full, free Link expiration Paid only Paid only Yes Yes No Yes Password protection Enterprise only Paid only Paid Paid No Yes API access Paid only Paid only Yes Yes Paid Full, free QR codes 2 free, then paid Paid Yes Paid Paid Unlimited Self-hostable No No No Yes (complex) No Yes (simple) Data ownership Their servers Their servers Their servers Their servers Their servers Your Firebase Ads/interstitials Yes (free tier) No No No Yes (free tier) Never Links disappear if you stop pay
AI 资讯
GoPro Wireless Mic System review: An audio upgrade with fixable flaws
GoPro's Wireless Mic System nails the basics, but it has some frustrating flaws.
开发者
Achieving Compliance as a Platform Engineering Team by Helping Developers
When a new platform team set out on implementing their roadmap through forced workflows with poor documentation, developer experience declined. Success came from simplifying governance, prioritizing what matters, and rolling out compliance incrementally through prevention, detection, and communication. Empathy, focus, and shared purpose drove successful adoption. By Ben Linders
开发者
White House report says Trump can usher in a "new golden age" of science
The resulting report is a near-random mixture of real problems and grievances.
开发者
America’s Hot Hybrid Summer
Gas prices are up, and US car dealerships are full of hybrids. Customers are going for it.
AI 资讯
QCon AI New York 2026: Registration Opens for December 15-16 Production-AI Conference
QCon AI New York 2026 (Dec 15-16) has opened registration at The Westin Jersey City Newport. Six tracks on production AI, chaired by Eder Ignatowicz with Faye Zhang and Wes Reisz. First sessions announced in August, full program by November. By Artenisa Chatziou
开发者
Google Turns a Selfie Video Into Your Account’s Spare Key
The next time you’re locked out of your Google account, you can use your face as part of the account recovery process.
AI 资讯
Zero-Trust Encrypted Backups with Restic on Ubuntu 24.04
Data preservation layouts are no longer just an exercise in handling routine disk failures; they are a direct line of defense in an active cyber-warfare environment. Far too many system administrators blindly default to writing simple, unencrypted shell scripts tied to legacy system utilities. Operating obsolete data-mirroring procedures introduces severe vulnerabilities to enterprise architectures. Traditional file sync tools completely lack client-side encryption barriers, leaving raw production data completely exposed to third-party infrastructure hosts. Furthermore, standard backup approaches consume vast amounts of unnecessary bandwidth by redundantly transferring identical files over and over again. Restic completely destroys this insecure paradigm. Written from the ground up in Go, Restic enforces client-side AES-256-CTR cryptographic encryption by default, ensuring no plain-text data ever traverses the network interface. Leveraging advanced content-defined chunking algorithms, it performs lightning-fast block-level deduplication to compress your overall storage footprint to a minimum. Phase 1: The Backup Orchestration Myth Understanding the architectural superiority of a native Go-compiled, client-side encrypted backup engine is critical before designing your disaster recovery pipeline: Architectural Metric Legacy Sync Tools BorgBackup Platform Modern Restic Engine Native Cloud S3 Support Requires Rclone Mounts Requires Third-Party Proxy Layers Native Compiled Support Default Cryptography None (Plain-Text Transmissions) Client-Side AES-256 AES-256-CTR Client-Side Data Deduplication File-Level Verification Only Content-Defined Block Level Content-Defined Block Level Cross-Platform Portability Variable Compatibility Strictly UNIX/Linux Constrained Single Static Go Binary Phase 2: The Append-Only Lock Paradox (IAM Fix) The most dangerous operational vulnerability found in generic Linux documentation involves key privileges. Amateurs store fully unconstrained ad
开发者
Ink & Switch Introduces Bijou64: Canonical Variable-Length Integer Encoding for Safe Parsing
Ink & Switch published bijou64, a variable-length integer encoding where every number has exactly one byte representation, closing the canonicality bug class behind attacks on PKCS#1, JWT libraries, and Bitcoin. The design also decodes two to ten times faster than LEB128. Community ports to Elixir, Go, Perl, and Java followed, while HN commenters debated SIMD performance and residual range checks. By Steef-Jan Wiggers
AI 资讯
Unhandled Promise Rejections in Node.js: Why They Silently Kill Jobs
A background worker sits quietly for weeks, chewing through a job queue without incident, and then one Tuesday afternoon it disappears mid-batch, taking forty in-flight jobs down with it. No stack trace pointing at the offending line, no alert until a customer notices their export never finished. Nine times out of ten, the culprit is one of the most under-discussed failure modes in server-side JavaScript: unhandled promise rejections. They are easy to introduce, easy to miss in code review, and in modern Node.js they no longer just print a warning — they can end your process outright. This post walks through what unhandled promise rejections actually are at the engine level, why they are far more dangerous in long-running Node.js services than in typical web requests, how Node's default behavior around them has shifted over the years, and the concrete patterns you can use to stop them from quietly killing your jobs. What an Unhandled Promise Rejection Actually Is At the JavaScript engine level, a promise is a state machine with three possible states: pending, fulfilled, or rejected. When an async operation fails — a network call times out, a database query throws, a JSON parse blows up — the promise representing that operation transitions to the rejected state and carries a reason, usually an Error object. That's all a rejection is: a value flowing down the promise's error channel instead of its success channel. The engine only considers a rejection "handled" if, by the time it does its bookkeeping (which happens on a microtask checkpoint, not synchronously), there is a .catch() handler, a second argument to .then() , or a surrounding try/catch around an await somewhere in that promise's chain. If none of those exist anywhere in the chain, the rejection is classified as unhandled. This is the precise, narrow definition of unhandled promise rejections: a promise that reaches the rejected state with zero handlers attached anywhere along its chain of consumers. It's wo
产品设计
My Week of Transparent Living With Apple’s ‘More Clear’ Liquid Glass Setting
I cranked Apple’s iOS 27 beta to maximum transparency on my iPhone and enjoyed every warped second.
AI 资讯
How to Import JSON into MongoDB and Export to CSV with Data Masking
Every morning, an online store receives the previous day’s orders from a marketplace partner. The file comes in JSON format. The company needs to add those orders to its main MongoDB orders collection. The sales manager also needs a CSV report that can be opened in Excel. That sounds like a small task. Import the file, copy the documents, export the report. But in practice, a few things can break the process. A date can be imported as a string. A field can have the wrong name. One batch may use total , while the main collection uses totalAmount . A temporary collection can keep old records and trigger duplicate key errors. A CSV export can create null values because the mapping points to fields that do not exist. And then there is customer data. The manager may need the sales numbers, but they probably do not need real customer names or internal customer IDs. This article walks through a real daily workflow: Import marketplace JSON ↓ Store the batch in a temporary MongoDB collection ↓ Copy the orders into the main orders collection ↓ Mask customer fields during export ↓ Create a CSV report The goal is not just to move data from JSON to CSV. The goal is to make the process repeatable, easier to check, and safer to share. The workflow The workflow has three jobs: Import Yesterday Orders ↓ Add Orders to Main ↓ Export Daily Sales Report The important part is the parent relationship between the jobs. Add Orders to Main depends on Import Yesterday Orders , so it only runs after the JSON file is imported successfully. Export Daily Sales Report depends on Add Orders to Main , so the CSV is created only after the main orders collection has been updated. This prevents the report from being generated when data is missing or incomplete. The incoming JSON file The partner sends a file with yesterday’s completed orders. A single order looks like this: { "orderId" : "ORD-2026-07-201" , "customerId" : "CUST-1003" , "customerName" : "Sofia Rossi" , "orderDate" : "2026-07-21T08:20:00
AI 资讯
Gemini 3.6 Flash: 17% fewer tokens, lower cost, and a Python cold start fix you didn't have to ask for
This week's releases cluster around a theme: reducing the overhead that compounds in production agentic systems. Gemini 3.6 Flash ships with measurable token reduction and a price cut, Vercel's AI Gateway gets service tier routing for latency-cost tradeoffs, and Python cold starts quietly drop by half with zero code changes required. Nothing experimental here—most of this is worth touching immediately if you're already in these ecosystems. Gemini 3.6 Flash cuts output tokens by 17% Google's 3.6 Flash reduces output token usage by 17% versus 3.5 Flash while lowering cost to $1.50/1M input and $7.50/1M output. The improvement is most pronounced on coding and web tasks, which happen to be the workload profile of most production agents. The companion model, 3.5 Flash-Lite, trades some quality for throughput—350 output tokens/sec—at $0.30/$2.50 per million tokens. Token efficiency isn't a vanity metric in agentic systems. Multi-step workflows compound output costs: every intermediate reasoning step, tool call response, and context accumulation multiplies what you pay. A 17% reduction per model call can translate to significantly more than 17% savings across a full agent loop, depending on how many hops your workflow runs. The throughput number on Flash-Lite matters too—if you're running high-volume document classification or search reranking, 350 tokens/sec opens architectures that weren't cost-viable before. The API swap is a single parameter change. No migration friction, no new authentication surface. Ship it now if Gemini is already in your stack and you're paying attention to inference costs. Replace 3.5 Flash with 3.6 Flash for general agentic tasks; move high-throughput, lower-stakes subtasks to Flash-Lite. Gemini 3.6 Flash and 3.5 Flash-Lite on AI Gateway Both new Gemini models are immediately available through Vercel's AI Gateway, callable via the unified AI SDK with the same cost tracking, failover, and routing you'd use for any other provider. Model selection
AI 资讯
I Let My AI Assistant Read and Reply to My Emails for a Week. Here’s What Actually Happened.
An AI can write a perfect email in seconds. Having a real back-and-forth conversation is much harder. Sarah runs a salon. She has an AI assistant that emails her customers when a slot opens up. Last Friday, a customer canceled his booking. The assistant sent an email: "We have an opening tomorrow at 2 PM. Want it?" The customer replied in a minute: "Yes, book it!" The assistant never saw that reply. The slot stayed open. The customer never got a confirmation. This happens more than people realise — not because it's hard to receive email, but because most setups were never wired to close the loop. Sending is easy. Wiring the whole loop isn't. To be fair, receiving and parsing email isn't some unsolved problem — providers like SendGrid, Mailgun, and Postmark have offered inbound email parsing for years. Point your domain at them, and they'll hand you the clean message. But those webhooks only push the message once. There's no inbox to check back later, and no built-in way to link a reply to the right conversation. You have to build that part yourself — and you still can't run any of it on your own servers. There's a second issue too. AI assistants sometimes send a slightly odd reply — nothing harmful, just a little off. Many managed email providers watch for exactly that pattern, and can suspend an account fast. One strange sentence, and Sarah's whole booking system could go dark with no warning. What a real AI assistant needs For an assistant like Sarah's to actually hold a conversation, a few things need to work together: Replies need to land somewhere the AI can read them They need to arrive clean, not messy They need to stay linked to the right conversation The AI needs to reply back from the same email address All of it needs to run on infrastructure you control, not three different vendors This is what we built Reloop for Reloop puts that whole loop in one place, self-hosted. When Sarah's customer replied "Yes, book it!", Reloop caught the reply, cleaned it up,
AI 资讯
Remember Jibo? Its Successor Is a Wearable That Turns Your Life Into AI Slop
With “blessings” from the original Jibo founders, iKairos is a wearable or desk-mounted “AI journal” that turns your family moments into AI images and video.
AI 资讯
Building Profitable Niche Lifestyle Stores: A Developer's Guide to E-Commerce Essentials
Building Profitable Niche Lifestyle Stores: A Developer's Guide to E-Commerce Essentials Launching a niche lifestyle e-commerce store is more than just setting up a storefront—it's a technical challenge that requires thoughtful architecture, performance optimization, and strategic content planning. Whether you're building a store for outdoor gear, fashion accessories, or humor products like humor24.se , here's what developers need to know. Choose the Right Tech Stack Most profitable niche stores run on WordPress + WooCommerce + a performance-focused theme (like Blocksy). This combination offers: Flexibility : Extend functionality without rewrites SEO-friendly : Built-in structured data support Cost-effective : No complex deployment infrastructure needed Content integration : Blog + products on the same platform Use a headless CMS approach only if you have compelling reasons (high-traffic requirements, complex frontend needs). For 99% of niche stores, the overhead isn't worth it. Core Web Vitals = Revenue Google's Core Updates consistently penalize slow stores. Prioritize: LCP (Largest Contentful Paint) < 2.5s : Optimize images (WebP/AVIF format), lazy load below-fold content, defer non-critical CSS INP (Interaction to Next Paint) < 200ms : Minimize main thread blocking, defer JavaScript CLS (Cumulative Layout Shift) < 0.1 : Use fixed image dimensions, avoid late-loading ads/widgets # Generate WebP variants of product images convert image.jpg -quality 80 image.webp # Check your Core Web Vitals # Use PageSpeed Insights API or Lighthouse CI in your deployment pipeline Each 0.1s improvement in LCP can yield 3-5% conversion uplift. Performance is a feature. Structured Data Wins Traffic Rich snippets dramatically improve click-through rates. Implement: Product schema : name , price , availability , image , brand , AggregateRating BreadcrumbList : Navigation structure FAQ schema : If you have FAQs (47% higher CTR for FAQ snippets) { "@context" : "https://schema.org/" , "@t