AI 资讯
I built a free, no-signup AI text toolkit - here's the stack and why
I kept hitting the same small friction: I'd want to quickly rewrite an email, clean up some text, or summarize a long thread — and every tool wanted me to sign up, pick a plan, or watch an ad first. For a ten-second task, that's absurd. So I built the thing I wanted: a set of free, no-signup AI text tools , each doing one job well. This is a quick write-up of the stack and the decisions behind it. 👉 Live: https://www.texttoolsai.app The core idea: one tool, one job, zero friction Instead of a single mega-app, it's a collection of single-purpose tools — rewrite, tone change, summarize, prompt generation — each on its own page. You land, paste, get output. No account, no modal, no paywall. The "no signup" rule forced good constraints: everything has to work instantly and statelessly, which kept the whole thing simple. The stack Next.js (App Router) — server components for the content/SEO pages, client components only where the tool actually needs interactivity. Vercel for hosting — the deploy story is boringly good, which is what you want. An LLM API on the backend — the browser never sees a key; requests go through a Next.js route handler that owns the prompt and the provider call. Tailwind for styling — fast to iterate, easy to keep consistent across dozens of tool pages. One decision that paid off: data-driven pages Every tool is defined as a config object (label, placeholder, system prompt, endpoint) rather than a hand-built page. Adding a new tool is mostly adding data, not wiring up new routing. That's what made it realistic to ship a lot of tools without the codebase turning into spaghetti. // simplified shape { slug: 'rewrite', label: 'Paste your text', endpoint: '/api/tools/rewriter', systemPrompt: '...' } The route handler resolves the endpoint key against a map of system prompts, so the API surface stays tiny even as the tool count grows. What I'd tell anyone building something similar Keep the API key server-side. Obvious, but easy to leak through a miscon
AI 资讯
How We Built a 160-Article AI Education Platform with Next.js and Static HTML
How We Built a 160-Article AI Education Platform with Next.js and Static HTML Three months ago, I launched IAcademy — an AI education platform in Spanish with 160+ free guides covering everything from prompting basics to autonomous agents, LLM deployment, and MCP servers. Here's what worked, what didn't, and the architecture behind it. Why Spanish AI Education is Underserved The AI education space is dominated by English content. Coursera, Udemy, DeepLearning.AI — all English-first. Spanish-speaking professionals (500M+ people) get translated scraps or nothing. The opportunity: 0% competition on keywords like "agentes ia" (400 monthly searches), "herramientas ia" (400), "formación ia" (250). In English, these keywords have 30-50% competition. In Spanish, nobody's writing quality content. Architecture: Why Static HTML, Not a CMS Each blog post is a standalone index.html file. No WordPress, no Gatsby, no MDX compilation step. site/blog/ ├── agentes-ia-que-son/ │ └── index.html ├── herramientas-ia-guia/ │ └── index.html ├── formacion-ia/ │ └── index.html └── ... (160+ directories) Why this approach: Zero build time. Adding an article = creating a directory + file. No compilation, no hydration errors, no framework upgrades breaking 160 pages. Perfect SEO control. Every <title> , <meta> , JSON-LD schema, internal link, and heading hierarchy is hand-crafted per page. No CMS template imposing its structure. Instant deploy. Push to GitHub → Cloudflare Pages deploys in ~30 seconds. No build queue. No JavaScript required for content. Google indexes immediately. Core Web Vitals are perfect — there's nothing to load. The dynamic parts (auth, course portal, labs) use Supabase + vanilla JS. But the blog — which is the SEO engine — is pure static HTML. Content Strategy: Niche Prompts Beat Head Terms After 3 months, here's what ranks and what doesn't: What ranks (top 10 in Google): prompts-ia-facturacion — prompts for accountants (position 8.4) prompts-ia-logistica — prompts for lo
AI 资讯
I Built HackForPinas to Make Philippine Hackathons Easier to Discover
In my previous article, I talked about Train Track, the transit app I built around Metro Manila's railway systems. This project started with a completely different problem. I kept thinking about how difficult it can be to discover hackathons and coding competitions. Not because they don't exist. They do. The problem is that they're scattered everywhere. A university might announce one. A government agency might host another. A private company might run one. A developer community might post another. And suddenly you're checking multiple websites just to figure out: What can I actually join? So I built HackForPinas. What is HackForPinas? HackForPinas is a free, public, and open-source directory for Philippine: Hackathons Coding challenges Technology competitions The idea is pretty straightforward: Make opportunities easier to discover. Events can be filtered by: Region Format Organizer type Status Organizers are categorized as: Government University Private Instead of browsing through unrelated websites, users can explore opportunities in one place. But the more I worked on it, the more I realized that the directory itself wasn't the hardest part. The data was. The Data Problem Imagine trying to collect hackathons from different websites. One might have an RSS feed. Another might use WordPress. Another might expose an API. Another might have an ordinary HTML page. And another might not have anything structured at all. So HackForPinas uses multiple scraping strategies: WordPress REST API RSS GDG Community Eventbrite HTML + Cheerio The scraper runs through a background endpoint and collects events from different Philippine technology sources. The interesting part wasn't: "Can I scrape a website?" It was: Can I turn information from completely different sources into one consistent dataset? That became a much more interesting engineering problem. I Didn't Want Anyone to Publish Directly There's another problem with a public directory. If anyone can submit an event, what s
AI 资讯
I built a quiz-driven gift recommender (Next.js + Cloudflare Pages)
Most "AI gift finders" are a search box with a chatbot glued on. I wanted to build something different — a quiz-driven gift recommender that ranks real Amazon products by who the recipient actually is , not just keywords. I call it GiftHive . In this post I'll walk through the architecture, the conversion tricks I learned shipping it, and the bits I'm proudest of. The Problem Picking gifts is emotionally expensive. You scroll Amazon for an hour, second-guess every option, and end up buying a gift card. Existing tools don't help because they optimize for keyword match , not recipient fit . GiftHive flips the input: instead of "show me gifts under $50", you answer a 30-second quiz about the person (relationship, interests, occasion, budget) and get a ranked shortlist with explanations of why each gift fits. Stack Next.js (App Router) — SSR for fast first paint, RSC for product data Tailwind CSS — design system + dark mode via CSS variables Cloudflare Pages — edge-deployed, free tier covers the traffic Amazon Associates — affiliate revenue model The Funnel The whole site is a 3-step conversion funnel: Landing page — exit-intent modal + social proof toasts prime the visitor Quiz — 30-second, one-question-per-screen flow, no login Results — ranked products with countdown bar and "X people found gifts this week" social proof Every step has a single primary CTA. The exit-intent modal is route-aware — it only fires on / and stays silent on /quiz and /results so it never interrupts the funnel mid-flow. That bug cost me ~15% of quiz completions before I caught it. Personalization Logic Each quiz answer maps to a vector of attributes (interests, style, budget, relationship). Products in the catalog have matching tags. Ranking is a weighted score: score = tag_overlap * w1 + budget_match * w2 + occasion_match * w3 No ML model needed — a few hundred products and clean tagging is enough to feel personal. Amazon Affiliate Integration Every product link runs through getAmazonUrl() w
AI 资讯
🟩 Team Matrix or ⬜ Team Paper? | Alan Babychan
🚀 Shipping a major update to my portfolio After weeks of designing, developing, and refining, I'm excited to share the latest version of my personal portfolio. Rather than building another static portfolio, I wanted to treat it like a real product—focusing on performance, interaction design, accessibility, analytics, and user experience. 🌐 Live: https://www.alanbabychan.online What I built 🟩 Matrix Theme A cyberpunk-inspired dark mode featuring animated binary effects, glowing UI elements, and an immersive developer experience. ⬜ Paper Theme A clean, modern light mode designed with readability, visual hierarchy, and clarity in mind. 🎵 Interactive Audio System •Background music •UI sound effects •Dedicated settings panel •Adjustable volume controls •Built using the Web Audio API 🖱️ Interactive Cursor A custom mouse-follow glow and subtle cursor interactions that enhance the browsing experience without becoming distracting. ✨ Micro-interactions Hover states, smooth page transitions, animated UI components, and responsive visual feedback to make every interaction feel intentional. 📖 UX & Accessibility Built around clear typography, intuitive navigation, responsive layouts, and accessibility-focused design to provide a consistent experience across devices. 📊 Performance & Analytics Built with Next.js and optimized for speed, SEO, and scalability. Implemented a complete Google Analytics 4 setup including: •SPA page tracking •Google Consent Mode v2 •Custom event tracking •User interaction analytics Tech Stack: Next.js • React • Tailwind CSS • Framer Motion • Web Audio API • Google Analytics 4 • Microsoft Clarity Coming Soon... 👀 I'm currently building a personal AI assistant that will allow visitors to interact with my portfolio, ask questions about my projects, experience, and skills, and explore everything conversationally. What I learned This project pushed me to dive deeper into: •Theme architecture •Frontend performance optimization •Animation systems •Custom UI inte
AI 资讯
The half of California's AB 723 that nobody implements
`California's AB 723 has been in force since January 1, 2026. It amends Business & Professions Code § 10140.8 and it applies to any real estate listing image that has been digitally altered. Virtual staging is the obvious case, but the definition is wider than that. The rule has two parts: A statement that the image has been altered, "reasonably conspicuous" and placed on or adjacent to the image. A link to a publicly accessible URL, or a QR code, that includes and clearly identifies the original, unaltered image. Everyone builds the first part. It is a text label on a photo, an afternoon of work. The second part is a small piece of infrastructure: a permanent public URL, per image, that outlives the tab the agent had open when they exported. I build a virtual staging product, so I had to ship both. This is how the second part is put together, and the one thing I got wrong. What counts as altered Worth getting right before writing any code, because it decides which of your features need the label and which do not. Subsection (b)(2) carves out ordinary photo editing. Covered: Adding furniture, rugs, art or decor Removing furniture, clutter or personal items Changing paint, flooring or wall finishes Sky replacement and day to dusk Greening or reshaping lawns and landscaping Anything that changes the facade or the property itself Not covered: Exposure, lighting, white balance, color correction Sharpening Straightening, cropping, angle In the codebase that line is a set, and the two omissions are deliberate: ts export const TOOLS_ALTERING_LISTING_IMAGES = new Set([ "virtual-staging", "sky-replacement", "day-to-dusk", "grass-greener", "declutter", "object-remover", ]); image-enhancer is out because exposure and white balance are precisely what the statute excludes. A floor plan generator is out because a diagram is not an altered photograph. Attaching a legal claim to a feature the law does not cover is not a harmless extra: it is the fastest way to make the rest of your
AI 资讯
These startups are chasing the next big thing in LLMs
MIT Technology Review’s What’s Next series looks across industries, trends, and technologies to give you a first look at the future. You can read the rest of them here. Way back in the summer of 2017, AI researchers at Google put out a paper called “Attention Is All You Need,” in which they described a new…
AI 资讯
Advanced Server-Side Caching Patterns in Next.js: From Basic ISR to Granular Control
Originally published on tamiz.pro . Caching in modern web development is no longer just about serving static assets faster; it is the primary mechanism for balancing performance, cost, and data freshness. In the context of Next.js, the caching architecture has evolved significantly, shifting from a simple getStaticProps / getServerSideProps dichotomy to a sophisticated, multi-layered system that spans the Edge Runtime, the Server Components architecture, and the Node.js server environment. For software engineers and systems architects, understanding the default behaviors of Next.js caching is insufficient. To build production-grade applications that handle high concurrency without hammering your database, you must master the advanced patterns: granular revalidation, cache tagging, and external cache management. This article dives deep into these mechanisms, explaining how they work under the hood and how to orchestrate them for optimal performance. The Evolution of Next.js Caching To appreciate advanced patterns, we must first contextualize the current caching model. Next.js 13+ (App Router) introduced a new caching paradigm that is both simpler by default and more powerful when customized. The default behavior is now: App Router (RSC): Components are cached by default. Server Components are rendered once and cached on the server. The next request for the same data returns the cached result. Static Generation: Pages and layouts are built at build time and served statically. Server Components: Fetched data is cached in memory on the server, not in the browser. The critical shift here is that caching is opt-out, not opt-in . Previously, you had to explicitly mark things as static. Now, you must explicitly invalidate cache when data changes. This inversion of control places the responsibility of consistency squarely on the developer, requiring precise tools to manage invalidation. Granular Revalidation: The Tag-Based System The most significant advanced caching pattern
AI 资讯
The Day Our Web App Took 8 Seconds to Load (and How We Cut It in Half)
There is a quiet moment of panic every developer knows. You hit deploy, open the live site on your phone, and wait. One second. Two seconds. Four seconds. Still a blank white screen. A while back, I was working on a Next JS application that looked fast on high speed office Wi Fi. But when tested on a spotty mobile connection, it felt painfully slow. The initial page load was clocking in at nearly 8 seconds, and our main JavaScript bundle was a bloated 1.8 megabytes. Here is how we diagnosed the bloat, cut our load times by 47 percent, and the simple performance rules every developer should know. The Investigation: Where Was the Weight Coming From? When a website is slow, our first instinct is often to blame slow backend APIs or heavy database queries. But when I ran a performance audit, the backend was not the problem at all. The front door was just jammed with too much stuff. We were making three classic mistakes: First, we were packing for a long trip on a short walk. We were loading heavy charting libraries, complex admin tables, and pop up modals the second a user landed on the home page, even if that user only came to read a single line of text. Second, giant images were being served to tiny mobile screens, hogging precious bandwidth before any interactive buttons could even load. Third, a single state update at the top of our app was causing dozens of unseen child components to recalculate and re render unnecessarily behind the scenes. The Strategy: Trimming the Fat Instead of rewriting the entire codebase from scratch, we focused on three targeted fixes. 1. Don't Load It Until They Ask For It Why force a user to download a complex analytics chart if they have not even clicked on the dashboard tab yet? We split the app into smaller, independent code chunks. Now, the user downloads only the absolute bare minimum needed to view the immediate screen. The heavy features stay on the server until the exact moment the user interacts with them. 2. Smart Asset Delivery
AI 资讯
I built a tier list that re-rates 245+ AI tools every week — the automation behind it
AI tool reviews rot faster than anyone can rewrite them. A tool that was S-tier in March ships a broken pricing change in June, a "top 10" listicle from last year recommends products that no longer exist, and every directory slowly turns into a graveyard of dead links. I run AI Tier List , a bilingual (EN/KO) directory that ranks 245+ AI tools from S to D. My answer to review rot: don't re-review by hand. Make a pipeline re-rate everything weekly, and let humans only approve or reject. The architecture Everything runs on one weekly GitHub Actions cron (Next.js 16 + Prisma + Neon Postgres + Vercel): weekly cron (Sun 00:00 UTC) ├─ collect Google Trends per tool → trend scores ├─ collect OpenRouter usage rankings → weekly LLM leaderboard ├─ deactivate dead tools → site checks + trend slump ├─ discover new tools → search + AI triage ├─ re-evaluate tiers (LLM) → PendingUpdate rows └─ generate weekly blog draft → MDX The key design decision: the LLM never writes directly to the live site. Re-evaluations land in an approval queue ( PendingUpdate table). I review diffs in an admin panel and approve batches. The pipeline proposes; a human disposes. That one boundary is what keeps automated content from becoming automated garbage. Two collectors do the heavy lifting: Trend collector — Google Trends per tool, weekly. A tool in a sustained slump gets flagged; if its website also starts failing health checks, it gets deactivated automatically. Dead products remove themselves from the directory. OpenRouter collector — real token-usage data powers a weekly LLM leaderboard . No opinions, just "which models did people actually route traffic to this week," with usage share, pricing, and context length. What the tier actually means Each tool stores bilingual tierReason , strengths , and weakness fields, and the tier maps S→5 … D→1 into review schema markup. When the weekly re-evaluation moves a tool, the reason is regenerated with it — so the rating and its justification never drift a
AI 资讯
How much to share in a monorepo when building common features for web and mobile
This article is an English translation of the original Japanese article. When I added an Expo app to an existing Next.js web service, I initially wanted to share as much code as possible. In practice, types and business rules share well, while UI and runtime dependencies are easier to manage separately. SquadNote uses pnpm workspace and Turborepo, composed of apps/web , apps/mobile , and packages/* . Current split apps/ web/ Next.js, Cloudflare Workers mobile/ Expo, React Native packages/ api/ tRPC and Zod shared parts db/ Drizzle schema design-tokens/ Root workspace configuration is simple: packages : - " apps/*" - " packages/*" turbo.json manages only build and typecheck dependencies. Rather than adding custom build steps for sharing, I started with a structure where each package exports TypeScript sources. What I share The biggest benefit came from tRPC types. Mobile type-imports the web AppRouter , using the same input and output. import type { AppRouter } from " ../../apps/web/src/server/api/root " ; export const api = createTRPCReact < AppRouter > (); I also separated colors, spacing, and font sizes into @squadnote/design-tokens . export { colors } from " ./colors " ; export { spacing } from " ./spacing " ; export { radius } from " ./radius " ; Business rules like waitlists become sharing candidates as pure functions with no dependency on React or DB. They are easy to test, and results do not diverge between web and mobile. What I do not share I do not share screen components. Next.js DOM and React Native View have different interactions, accessibility, and layout constraints, even when they look similar. Authentication storage also differs: Web: NextAuth cookie session Mobile: SecureStore Bearer JWT Both reach the same API, but sharing the login screen and token storage would require handling each environment's concerns with many branches. Routing also has separate implementations for Next.js App Router and Expo Router. What I share is the meaning of organiza
AI 资讯
Why Nodemailer Doesn't Work on Cloudflare Workers (And What To Do Instead)
A short explanation of a wall a lot of developers hit, why it isn't going away, and the five lines that replace it. You wrote a contact form. It worked locally. You deployed it to a Cloudflare Worker, or a Vercel Edge Function, or Deno Deploy, and got something like this: TypeError: Class extends value #<Object> is not a constructor or null Or, if you were luckier and got a useful error: Module not found: Can't resolve 'net' Then you spent an hour trying compatibility flags, polyfills, and bundler aliases. I want to save you the rest of that hour. This isn't a bug, and no amount of configuration will fix it. The actual reason Nodemailer's default transport is SMTP. SMTP is a protocol that runs over a raw TCP connection. To open one in Node.js, you call net.createConnection() . Cloudflare Workers don't run on Node.js. They run on V8 isolates — the same engine as Chrome, without the Node runtime around it. Vercel's Edge Runtime and Deno Deploy are built on similar principles. In that environment, there is no net module, because there are no raw TCP sockets. All networking is handled by managed infrastructure outside the runtime — Cloudflare's own writeup on bringing node:http to Workers is explicit about this: connection pooling, TLS negotiation, and egress IP management are handled at the system level, which is precisely why a subset of Node APIs can never be supported. So the chain is: No raw TCP → no net.createConnection() → no SMTP client → no Nodemailer. There's a second, smaller issue that often gets conflated with this one. Nodemailer issue #1621 points out that Nodemailer imports built-in modules without the node: prefix, which breaks the Workers build step. That one is fixable. But fixing it wouldn't help — you'd just move the failure from build time to runtime, where net still doesn't exist. Issue #1623 covers the broader edge-function problem. It's worth being clear that none of this is a knock on Nodemailer. It's an excellent library, actively maintained,
AI 资讯
I Built The Most Advanced Job Application Tracker
If you're actively applying for jobs, you probably know the struggle: Did I already apply to this company? Which resume version did I send? What salary did I mention when I applied? What was the budget mentioned in the job posting? When did I apply for this one? Which interviews are scheduled this week? What were the HR contact details again? What exactly were the requirements for this role? When is my next interview? Where did I even find this posting? How many of my applications are actually turning into interviews? Every one of those is answerable. The problem is that the answers are scattered across a spreadsheet, a notes app, your inbox, and your memory — and reassembling them takes longer than the follow-up you were trying to send. Spreadsheets are where most people start, and they hold up until somewhere around application number twelve. After that, searching, filtering, and keeping the thing current becomes its own small job — and a spreadsheet still won't tell you that six applications have been sitting in "Applied" for a month, or whether your last twenty went better than the twenty before them. That's why I built HireLoop — an advanced job application tracker meant to reduce the mental load of a job search rather than add to it. Live app: hireloop.yogeshchavan.dev — free to use, with a demo account if you'd rather look around before signing in. Check out the application demo video below: Check out some preview images of the application The short version With HireLoop you can: Track every application in one place — status, dates, salary, source, and links See where your search stands at a glance on a dashboard Move applications through a Kanban pipeline Search, filter, and sort as the list grows See interviews and deadlines on a calendar Analyse interview rates, offer rates, application trends, and which sources actually work Store notes, resume versions, HR contacts, salary details, and job links per application Mark the ones that matter as favourites Kee
AI 资讯
Migrating a WordPress Blog with Claude Code
With the help of Claude Code, I finished a task that I had pushed aside for years in two days: To move away from WordPress for my blog . 1. Background WordPress had served well between 2016 and 2019, when I was still learning how to write apps with a framework like Ember. Over time, however, the annual cost of $48 (plus $28 for the domain, excluding taxes) felt overpriced, given the lack of features (e.g. no syntax highlighting for *.{gjs,gts} ) and many paywalls for customization. Reposting a blog post on dev.to was also tedious, since I need to write the content on WordPress using a proprietary, interactive editor, while in Markdown on dev.to. I would copy the output text from WordPress, then convert the output to Markdown. I finally had enough when WordPress broke the styles for code blocks again : Once after I had migrated from the classic editor to the current one, and the second time recently while playing with the admin dashboard. 2. Move to Next.js I decided to rebuild my blog in Next.js, a framework suited for blogs and in demand. The app is to be deployed on Netlify, and the domain stays with WordPress through a DNS configuration. I saw the opportunity to use Claude Code for the first time, as I had little experience with Next.js and wanted to see how far I can get with unknown technologies in two days. Thanks to prior experience in blogging on different platforms, I had a good idea of how to store blog content and metadata ( front matter ) in a Markdown file and what users should be able to do when they visit my blog. I also studied the current URLs so that (1) I can tell Claude how to structure the project in Next.js and (2) URLs won't be broken after the migration. What I knew would take the most time and delegated to Claude Code: Create components and routes to provide a similar functionality. Generate Markdown files for blog posts that I didn't repost on dev.to. Many of these were related to math and engineering and included LaTeX in inline and block
AI 资讯
Post-Mortem: Why My Hybrid Virtualization Engine Stalled at 20 FPS -- 07 August 26
Building the layout orchestrator for Linkscribe wasn't a simple case of slapping a pre-made library onto a list. It was an ambitious attempt to construct a hybrid rendering stack—wrapping React Virtualized, delegating observer callbacks, and orchestrating DOM updates across dynamic multi-column folders. Having built virtualization engines completely from scratch before—ranging from off-thread Web Worker layout calculators to adaptive sync engines using relative spatial rendering—I approached this with a specific theoretical model in mind. Calling this an orchestrator or a custom engine fits what it was designed to do. However, my initial mental model fell apart when real-world DOM mutations, multi-column sections, and rapid reload cycles collapsed the execution pipeline. Testing 200 items in nested folders dropped the frame rate to ~20 FPS during fast reloads and rapid scrolling. The orchestration overhead simply choked the main thread. Problem 1: DOM Event Saturation and Thread Blocking The core bottleneck came down to how the delegation layer managed element state changes. Connecting MutationObservers and IntersectionObservers directly to global store triggers filled the browser event queue with continuous updates. The Old Approach The delegation manager listened for node insertions across the DOM tree and triggered immediate state changes on every single intersection callback. observerRef . current = new IntersectionObserver (( entries ) => { entries . forEach ( entry => { // Continuous individual state calls during rapid layout shifts hydrateActiveMonoLink ( id ); }); }); Why this broke down During rapid scrolling or fast view reloads, dozens of elements entered and left the viewport simultaneously. Processing these events individually saturated the main thread, forcing constant DOM querying and component re-evaluations while the browser was trying to handle paint cycles. The Refactored Direction Consolidating intersection calculations into unified updates preve
AI 资讯
Preventing Overselling: Inventory Locks Under Concurrent Checkouts
Two customers are looking at the same product. One unit left. Within the same second, both click Pay. If your checkout reads the stock count, decides there's enough, and then writes the decrement, both requests pass the check and both succeed. You've now sold two units of something you had one of. That's overselling, and it's not a rare edge case — it's the default behaviour of any checkout that treats "check stock" and "reduce stock" as two separate steps. The window is small, but on a product that's nearly sold out, or during a launch when everyone hits the same SKU at once, small windows fire constantly. I've built the order pipeline for two production e-commerce platforms — pikkuna.fi and pi-pi.ee — where concurrent webhooks and concurrent checkouts hit the same order and product rows. This is the layer I reach for when a store sells finite stock. I covered the bare SELECT ... FOR UPDATE primitive briefly in PostgreSQL Production Patterns ; this article is the whole system built on top of it — reservations, multi-line carts, the payment window, and the parts that actually bite you in production. When You Don't Need Any of This Start with the honest disclaimer, because it decides everything downstream. Both pikkuna.fi and pi-pi.ee are made-to-order . A vinyl curtain is cut to the customer's dimensions; a waterless urinal system ships from a supply chain, not a shelf with a hard unit count. When there's no fixed quantity to run out of, overselling isn't a failure mode — you can't sell the tenth unit of something you manufacture on demand. So neither of those platforms needs a row lock on a stock column, and I didn't build one there. You need this article when you sell discrete, finite stock : limited runs, event tickets, one-off items, anything where "5 left" is a real number and selling the sixth is a promise you can't keep. If your catalogue is print-on-demand, made-to-order, or backed by effectively unlimited supply, stop here — the locking below is complexity
AI 资讯
I Built a Photo-to-Cross-Stitch Pattern Maker That Runs in Your Browser
Photo-to-cross-stitch conversion looks like a resizing problem. It is not. A pixelated preview can look convincing and still be frustrating to stitch. It may contain too many colors, lack readable symbols, provide no reliable dimensions, or become useless when printed. I built StitchFromPhoto to handle the practical part of that workflow. It turns an image into a counted cross-stitch chart in the browser, lets you tune the result before committing to it, and keeps the source photo on your device. The useful output is a pattern, not a pixelated image A cross-stitch preview only answers one question. It shows roughly what the finished piece might look like. A usable pattern must also tell you how many stitches wide and tall the design is, which thread color belongs in each square, whether similar colors remain distinguishable on paper, and how large the result will be on your chosen fabric. That distinction shaped the app. The color preview is useful, but the symbol chart, thread key, stitch totals, fabric dimensions, and printable pages are the real deliverables. What the photo-to-cross-stitch pattern maker does The workflow starts with a sample image, so anyone can explore the controls before uploading a file. It also accepts JPG, PNG, and WebP images up to 20 MB. The main controls are stitch width, DMC color count, and fabric count. You can choose a pattern from 30 to 120 stitches wide, limit the palette to between 6 and 36 DMC colors, and calculate the finished size for 14, 16, 18, or 22 count Aida. You can move between the original photo, a color stitch preview, and a high-contrast symbol view. The thread key lists every retained DMC color code and the number of stitches assigned to it. Creating and previewing a pattern is free. High-resolution PNG and print-ready PDF downloads are unlocked per source image. I wanted that boundary to be visible before checkout rather than hidden behind the final button. How the browser turns pixels into stitches The conversion pi
AI 资讯
I built a Markdown resume builder for the AI-paste workflow — here's everything that broke
There's a workflow that basically didn't exist three years ago and now half the job-seekers I know use it: ask ChatGPT, Claude, Gemini, or any AI to write your resume bullets, get back beautifully structured text… and then spend forty minutes mangling it into Word or a drag-and-drop resume builder, fixing bullet indentation and font sizes by hand. Here's the thing that bugged me: LLMs already speak Markdown. Ask any chatbot for a resume and you get ## Experience , **Senior Engineer** , - Shipped X — clean, structured Markdown. Then every resume tool on earth makes you throw that structure away and re-enter it into form fields. So I built ResumeMD: a split-pane editor where you paste Markdown on the left, see a typeset resume on the right, pick a template, and download a PDF. No signup to start, everything in localStorage by default. This post is about the parts that fought back. Decision 1: Markdown is the source of truth Most resume builders store your resume as a proprietary JSON blob mapped to form fields. I wanted the document itself to be portable text. That means the entire product is "just" a Markdown renderer with opinions: h2 = section headers (Experience, Education) — these get the decorative treatment per template: uppercase, border, background, prefix glyphs. h3 = job titles — plain, bold, primary color. One weird trick I'm genuinely fond of: the sidebar template splits a single Markdown document into main column and sidebar using an HTML comment ( <!-- sidebar --> ) as the split marker. Content above the marker is the main column; below is the sidebar. It keeps the document valid Markdown everywhere else. The preview is react-markdown + remark-gfm with a 300ms debounce, styled by a template system that turned out to need three parallel implementations of every template: CSS classes for the live preview, inline-style functions shared between preview and template cards, and pure-JS styles for the PDF renderer. Thirty-two templates, three layers each. When
AI 资讯
Building a Reliable AI Image Pipeline: Tasks, Failures, and Credit Refunds
Most AI image generators look like a prompt box with a Generate button. That is also how my first version started. But once real users entered the workflow, the difficult problems appeared somewhere else: browser refreshes, external task IDs, reference images, partial failures, credit refunds, private assets, and public artwork moderation. While building Magggic , I learned that an AI image generator is less like a form submission and more like a small distributed job system. This article covers the decisions that made that workflow more reliable. The code samples below are intentionally simplified. The important part is the shape of the workflow, not a specific database or image provider. The prompt box is only the beginning A synchronous prototype is easy to imagine: const images = await provider . generate ( prompt ); return images ; That version works until the request takes a minute, the provider times out, one of four requested images fails, or the user refreshes the page. The production workflow I needed looked more like this: Prompt + references ↓ Create a local queued task ↓ Charge credits with an idempotency key ↓ Submit work to the image provider ↓ Persist every completed output immediately ↓ Finalize the task and refund failed outputs ↓ Keep the result private until the user publishes it The provider request is only one step. The local task is the source of truth for what the user sees. 1. Persist the task before calling the provider The first important decision was to create a generation record before making the external API request. A generation stores the information needed to reconstruct the job: type Generation = { id : string ; userId : string ; idempotencyKey : string ; prompt : string ; referenceImages : string []; model : string ; ratio : string ; resolution : string ; count : number ; cost : number ; status : " queued " | " generating " | " completed " | " failed " ; outputs : string []; providerRequestIds : string []; failureReason : string |
AI 资讯
LinkBreeze. The self-hosted Linktree alternative. Migrate in 30 seconds. One-line install.
LinkBreeze is a self-hosted alternative to Linktree. I built it because Linktree's $15/mo Pro plan didn't justify the feature set, email capture is another $9/mo, embed widgets are paywalled, link scheduling is paywalled. I wanted something I actually own: my data on my server, no subscription, no tracking pixels. The interesting technical bit: the public page ships zero client-side JavaScript. The entire link-in-bio page, themes, animations, hover effects, QR codes, embed widgets, renders server-side as pure HTML/CSS. No React runtime, no hydration, no framework JS. The visitor downloads HTML + CSS + their fonts. Page loads in under 300ms. That's it. Feature gap vs. the competition (what pushed me to build this): Feature Linktree LinkStack LittleLink Shako LinkBreeze Price $15/mo Free Free Free Free Admin Panel ✅ Slow ❌ ❌ ✅ Fast Multi-Page Paid ❌ ❌ ❌ ✅ Migration Wizard ❌ ❌ ❌ ❌ ✅ Built-in Analytics Paid Basic ❌ ❌ ✅ Full External Analytics ✅ ✅ ❌ ❌ ✅ Email Capture Paid ❌ ❌ ❌ ✅ Embed Widgets Paid ❌ ❌ ❌ ✅ Link Thumbnails Paid ❌ ❌ ❌ ✅ Link Scheduling Paid ❌ ❌ ❌ ✅ Themes Paid Limited CSS only Config ✅ Full Token System + Import/Export Custom CSS ❌ ❌ ✅ ❌ ✅ Language Closed PHP HTML Astro TypeScript Docker Deploy N/A Complex Simple Simple One command License Closed AGPL MIT GPL MIT Live demo (read-only): https://linkbreeze-demo.omnirise.dev/alex Admin demo: https://linkbreeze-demo.omnirise.dev/login (demo / demo1234) Repo: https://github.com/Manak-hash/LinkBreeze I'd genuinely appreciate feedback, bug reports, or feature suggestions. What's missing compared to what you'd expect from a self-hosted tool like this?