惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
云风的 BLOG
云风的 BLOG
小众软件
小众软件
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 司徒正美
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
美团技术团队
宝玉的分享
宝玉的分享
量子位
V
Visual Studio Blog
罗磊的独立博客
Vercel News
Vercel News
B
Blog
J
Java Code Geeks
S
SegmentFault 最新的问题
Recent Announcements
Recent Announcements
有赞技术团队
有赞技术团队
P
Proofpoint News Feed
GbyAI
GbyAI
G
Google Developers Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
I rewrote my Electron app in Tauri and cut the installer ...
Dzmitry Salavei · 2026-06-01 · via DEV Community

The problem with Electron

I built the first version of LazyWords in Electron. It worked. It also shipped a 120MB installer for an app whose entire job is showing a small floating flashcard every few minutes.

That felt wrong. So I rewrote it in Tauri v2.

Final result: 8MB installer, same functionality, noticeably less RAM. But the migration wasn't smooth — here's what actually bit me.


What is LazyWords

Before the technical stuff: LazyWords is a passive vocabulary app for Windows. It runs in the system tray and shows word flashcards as an always-on-top overlay over whatever you're working on. Cards appear every few minutes, stay a few seconds, disappear. No interaction required.

The idea was "radio for vocabulary" — you don't focus on it, but it's there, and things stick over time.

LazyWords demo


What broke during migration

1. Global shortcuts behaved differently

Electron's globalShortcut and Tauri's plugin handle edge cases differently. My main issue was Ctrl+Shift+N — show next card immediately. In the Electron version this was straightforward. In Tauri I needed the shortcut to both show a card and reset the timer interval, without triggering a double card.

The fix was a manual_trigger Tokio Notify in the timer loop:

tokio::select! {
    _ = tokio::time::sleep(Duration::from_secs(1)) => {}
    _ = manual_trigger.notified() => { break; }
}

Enter fullscreen mode Exit fullscreen mode

When the shortcut fires, it notifies the trigger, the timer resets its interval. Clean, no double card.

2. Always-on-top + fullscreen detection

Getting a transparent frameless window that floats over normal apps but disappears during actual fullscreen (games, video players, presentations) had no built-in Tauri solution. I ended up calling winapi directly:

unsafe {
    let hwnd = GetForegroundWindow();
    let mut monitor_info: MONITORINFO = std::mem::zeroed();
    monitor_info.cbSize = std::mem::size_of::<MONITORINFO>() as u32;
    let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
    GetMonitorInfoW(monitor, &mut monitor_info);
    // compare window rect to monitor rect
}

Enter fullscreen mode Exit fullscreen mode

Not pretty, but it works reliably.

3. Settings window deadlock on first open

If you create the settings window on demand (on Ctrl+Shift+W), there's a subtle deadlock risk on the first call when AppState is involved. The fix was to pre-create the settings window hidden at startup:

// At startup — create hidden, never destroy
settings_window.hide()?;

// On shortcut — just show it
settings_window.show()?;
settings_window.set_focus()?;

Enter fullscreen mode Exit fullscreen mode

Also intercepting CloseRequested with prevent_close() + hide() so the window is never destroyed — just hidden. This also fixed the window only being openable once.

4. Single-instance lock

Electron has this built in. In Tauri on Windows I handled it manually with a named mutex:

let mutex_name = windows::core::w!("LazyWords_SingleInstanceMutex");
let mutex = CreateMutexW(None, true, mutex_name)?;
if GetLastError() == ERROR_ALREADY_EXISTS {
    return Ok(());
}

Enter fullscreen mode Exit fullscreen mode

Second launch exits immediately, first instance continues normally.


What worked better than expected

Multi-monitor positioning. Tauri's cursor_position() + available_monitors() just worked. The card always appears on whichever monitor the cursor is on, no hacks needed.

The async timer loop. Tokio's select! macro made the timer logic genuinely elegant — sleeping on interval OR manual trigger, whichever comes first. This would have been messier in Electron.

Bundle size. 8MB vs 120MB speaks for itself. Tauri uses the system WebView (WebView2 on Windows) instead of bundling Chromium.


The AI-assisted workflow

One more thing worth mentioning: this app was built almost entirely with Claude.

The workflow that worked for me:

  1. Describe a feature or bug to Claude in chat
  2. Claude produces a Markdown task spec — what to implement, edge cases, approach
  3. Paste the spec into Claude Code in VS Code terminal
  4. Claude Code implements it
  5. Test, report results, iterate

The key insight: Claude chat is good at architecture and tradeoffs. Claude Code is good at executing a well-scoped task. Separating the two produced much better results than asking Claude Code to also design the solution.

I also keep a CLAUDE.md in the repo root that Claude Code reads at the start of every session — current architecture, known bugs, version history. Without it, context resets every session.


Result

Before (Electron) After (Tauri v2)
Installer size ~120MB ~8MB
RAM idle ~200MB ~30MB
Unit tests 0 27

Live on Microsoft Store and GitHub.

Repo: github.com/DemonazGH/LazyWords-Tauri

Happy to answer questions about any of the Tauri-specific solutions above.