Picture this: You’re deep in a coding session, multiple terminal tabs open, when your manager pings you about an urgent meeting in 5 minutes. You need to take notes, but opening Notion feels like overkill and breaks your flow. VS Code is already running your project. You just want something fast, lightweight, and native to your terminal workflow.
This exact scenario happened to me countless times over the past year. As a developer who spends 80% of my day in the terminal, I found myself constantly context-switching just to jot down quick thoughts or meeting notes. The existing solutions either felt too heavy (Electron apps), too disconnected from my workflow (web apps), or too limited (basic CLI tools).
That’s when I decided to build Splitmark - a CLI-first markdown editor that doesn’t compromise on either speed or functionality.
From day one, I knew this had to be a CLI-native experience. No Electron, no web-wrapper disguised as a desktop app. Just pure, fast terminal goodness. But I also recognized that pure CLI tools have limitations - what about mobile access? What about sharing with non-technical team members?
The solution was to build it as a CLI-first, cloud-enhanced platform:
For the CLI, I chose Node.js despite the usual performance concerns. Why? Because the target audience (developers) already has Node installed, and the ecosystem is unmatched for rapid development. Plus, for text editing operations, the performance difference vs. Rust or Go is negligible in practice.
The real magic comes from Ink - React for the terminal. This was a game-changer because:
// Clean, component-based terminal UI
function FileExplorer({ files, selectedIndex }) {
return (
<Box flexDirection="column">
{files.map((file, index) => (
<Text
key={file.name}
color={index === selectedIndex ? "blue" : "white"}
>
{file.name}
</Text>
))}
</Box>
);
}
No more wrestling with terminal control sequences or cursor positioning. Just React components that render to the terminal. The development velocity this enabled was incredible.
For the backend, I went all-in on Cloudflare’s edge stack:
This wasn’t just about performance (though <50ms response times globally are nice). Cloudflare’s integrated stack meant I could focus on features instead of DevOps. No Kubernetes clusters, no database scaling concerns, no CDN configuration.
// Workers API endpoint - deploys to 300+ edge locations
export default {
async fetch(request, env) {
const response = await handleApiRequest(request, env.DB, env.R2);
return new Response(JSON.stringify(response), {
headers: { "Content-Type": "application/json" },
});
},
};
The entire backend infrastructure is defined in code and deploys in seconds. When you’re a solo developer building a side project, this kind of simplicity is invaluable.
For the web interface, I chose React 19 to experiment with the new features:
The web editor needed to feel fast and responsive, especially since users might be coming from a lightning-fast CLI experience. React 19’s improvements to concurrent rendering and automatic optimizations helped achieve that seamless feel.
// Using React 19's new form actions
function CreateFileForm() {
const [isPending, formAction] = useActionState(createFileAction);
return (
<form action={formAction}>
<input name="filename" placeholder="File name" />
<button type="submit" disabled={isPending}>
{isPending ? "Creating..." : "Create File"}
</button>
</form>
);
}
The biggest technical challenge was syncing files between CLI and web without building a complex real-time system. WebSockets felt like overkill for what’s essentially a personal note-taking tool.
Solution: Smart Polling + Conflict Resolution
I implemented a hybrid approach:
// Intelligent sync that adapts to usage patterns
async function syncFile(localFile, remoteVersion) {
if (localFile.lastModified > remoteVersion.lastModified) {
return await uploadFile(localFile);
} else if (remoteVersion.lastModified > localFile.lastModified) {
return await downloadFile(remoteVersion);
}
// Files are in sync
return localFile;
}
This gives 95% of the benefits of real-time sync with 10% of the complexity.
As file counts grew during testing, the CLI’s file explorer became sluggish. Terminal rendering is inherently slower than GUI rendering, so every optimization matters.
Solution: Virtualization + Smart Caching
I implemented terminal virtualization - only rendering visible items:
// Only render visible files in the terminal viewport
function VirtualizedFileList({ files, viewportHeight }) {
const visibleFiles = useMemo(() => {
const start = Math.max(0, scrollTop - buffer);
const end = Math.min(files.length, start + viewportHeight + buffer * 2);
return files.slice(start, end);
}, [files, scrollTop, viewportHeight]);
return visibleFiles.map((file) => <FileItem key={file.id} file={file} />);
}
Plus aggressive caching of file metadata and lazy loading of file contents. The result? Smooth scrolling through thousands of files.
Getting the CLI into developers’ hands needed to be frictionless. npm was the obvious choice, but there were platform-specific gotchas around file permissions and PATH configuration.
Solution: Smart Installation + Self-Healing
The npm package includes post-install scripts that:
# Cross-platform installation that "just works"
npm install -g splitmark
splitmark --version # Validates installation
splitmark init # Sets up shell completions
Since this tool handles potentially sensitive notes and documents, security was non-negotiable. I implemented client-side encryption where files are encrypted before leaving the user’s device.
// Files are encrypted client-side before upload
async function encryptFile(content, userKey) {
const iv = crypto.getRandomValues(new Uint8Array(16));
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
userKey,
new TextEncoder().encode(content),
);
return { encrypted, iv };
}
The server never sees plaintext content. Even if Cloudflare’s infrastructure were compromised, user files would remain encrypted. This was crucial for adoption among security-conscious developers.
Throughout development, I obsessively measured performance metrics that directly impact user experience:
These weren’t arbitrary goals - they were based on user expectations coming from other developer tools. VS Code starts in ~500ms, so Splitmark needed to be faster. GitHub loads in ~2s, so the web interface needed to beat that.
One particularly valuable insight: developers don’t want another “productivity system” - they want tools that fit into their existing workflow. This reinforced the CLI-first approach.
To move fast as a solo developer, I invested heavily in development tooling:
# GitHub Actions workflow for testing and deployment
name: CI/CD Pipeline
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: npm test
- name: Deploy to staging
if: github.ref == 'refs/heads/develop'
run: wrangler publish --env staging
This setup enabled me to ship features confidently and quickly iterate based on user feedback.
Based on user feedback and usage patterns, here’s what’s coming:
The goal remains the same: enhance the developer workflow without getting in the way.
Splitmark started as a personal frustration and became a tool used by thousands of developers. The key insight was building something I genuinely needed and used daily. When you’re your own primary user, product decisions become clearer, and quality standards remain high.
The technical choices - CLI-first design, Cloudflare’s edge stack, client-side encryption - weren’t just about following trends. They were deliberate decisions to create the fastest, most reliable experience possible for the target audience.
Most importantly, the engineering process remained enjoyable throughout. Modern tooling, thoughtful architecture, and focusing on real user problems made building Splitmark feel less like work and more like solving interesting puzzles.
If you’re considering building a developer tool, my advice is simple: start with your own pain point, choose boring technology where possible, and obsess over the fundamentals - performance, reliability, and user experience. The rest follows naturally.
Try Splitmark:
npm install -g splitmark
This post was written in Splitmark’s CLI editor and polished in the web interface - a perfect example of the workflow the tool enables.