Tauri 2 · React 19 · CodeMirror 6 · Rust
Oxinot
로컬 우선 마크다운 아웃라이너. 폴더가 곧 사이드바,
블록이 곧 문장, .md 파일이 곧 진실.
Tauri 2 셸 + React 19 + CodeMirror 6 + Rust 백엔드로 macOS(aarch64) 네이티브 앱.
1. 파일 트리 — 폴더가 곧 사이드바
별도 파일 탐색기 사이드바가 없다. 워크스페이스 폴더 자체가 아웃라인이고,
메인 뷰에서 바로 펼쳐 본다. 폴더는 자기 이름의 .md 파일을 자동으로 가져
메타데이터를 적을 수 있다.
왼쪽 사이드바 아이콘은 트리 / 검색 / 설정 / 그래프 뷰 / 일일 노트 등 핵심 진입점으로 모든 컨트롤이 타이틀바 안에 통합되어 콘텐츠 공간을 최대한 확보한다.
2. 블록 에디터 — Logseq 스타일 아웃라이너
파일을 열면 블록 단위 편집기로 들어간다. 모든 줄이 자체 ID를 가진 블록이고, Tab/Shift+Tab으로 무한 깊이로 들여쓴다. 입력하는 동시에 마크다운이 렌더링되고, Cmd/Ctrl + K로 검색, Cmd/Ctrl + P로 명령 팔레트를 연다.
2.1 핵심 인터랙션
- Tab / Shift+Tab — 블록 들여쓰기 / 내어쓰기 (무한 깊이)
- Enter — 같은 깊이의 새 블록
- [[Page Name]] — 위키 링크, 그래프 뷰의 노드가 됨
- 드래그 앤 드롭 — 블록 순서·깊이 자유롭게 재배열
- 1초 디바운스 — 타이핑 멈추면 1초 뒤 자동 저장, 1.5초 뒤 폴더 동기화
2.2 라이브 마크다운 — 입력과 동시에 렌더링
Lezer markdown 파서가 블록 콘텐츠를 파싱하고, CodeMirror 6 위에 즉시 그린다. 코드 펜스는 syntax highlight, 인용은 좌측 바, 리스트는 들여쓰기 깊이만큼 시각적으로 누적된다. 백엔드에는 유효한 CommonMark로 직렬화되어 저장된다.
3. 시스템 설계 — 프론트 / 백 / IPC
핵심 설계 원칙은 “검증은 경계에서”. 파일시스템·워크스페이스
뮤테이션은 src/tauri-api.ts의 타입드 invoke 래퍼를
거치고, 블록 뮤테이션은 스토어에서 직접 invoke를 호출한다.
그 안에서 경로 traversal / 불법 문자 / URL 스킴을 차단한다. UI 어디에도
sanitize 코드가 흩어져 있지 않다.
- 프론트 — React 19 + Mantine UI + CodeMirror 6 + D3. Zustand + immer로 상태 관리.
- 백엔드 — Tauri 2 (Rust). SQLite는 인덱싱·검색용 캐시, 진실은 평문
.md파일. - IPC —
#[tauri::command]단위로 함수 노출, 권한은capabilities/*.json에 화이트리스트. - 동기화 — 프론트의 디바운스(타이핑 멈춘 뒤 일정 시간 후)된 트리거가 인덱스를 동기화, SQLite 인덱스를 점진적 갱신. 충돌은 Rust 측에서 atomic.
- 자동 업데이트 —
tauri-plugin-updater로 새 릴리스 알림, 백그라운드 체크.
3.1 한 번의 Tauri invoke 처리 흐름
// 프론트 (React)
const newBlock: BlockData = await invoke('create_block', {
workspacePath,
request: { pageId, parentId, content: '' },
});
// 백엔드 (Rust, tauri-api.ts → src-tauri/src/commands/block.rs)
#[tauri::command]
pub async fn create_block(
workspace_path: String,
request: CreateBlockRequest,
) -> Result<BlockData, String> {
// 1) SQLite 트랜잭션 시작
// 2) 평문 .md 파일에 append
// 3) 인덱스 갱신
// 4) 새 BlockData 직렬화해 반환
} 한 번의 호출이 파일 + 인덱스를 atomic하게 갱신한다. 호출이 끝나면 UI는 즉시 새 상태로 re-render되고, 1초 뒤 디바운스 자동 저장이 같은 경로를 한 번 더 안전하게 덮어쓴다.
4. 라이트 모드 + 명령 팔레트
기본은 다크, 설정에서 라이트로 전환 가능 (시스템 설정 따르기 옵션도). 라이트 모드도 같은 디자인 토큰을 공유한다 — 보라색 액센트, Inter 본문 폰트, 동일한 들여쓰기 깊이 시각화.
명령 팔레트(Cmd/Ctrl + P)는 모든 페이지·동작을 한 곳에 모은다. 아래는 “oxinot”으로 검색한 결과 — 위키 링크 복사, 페이지 이동, 블록 액션이 한 줄로 보인다.
5. 결과 — 데스크탑 노트 앱의 새로운 모양
왜 Oxinot을 만들었나. 기존 마크다운 에디터는 “파일 단위” 사고에 묶여 있다.
한 문장을 옮기려면 파일을 통째로 다시 써야 하고, 다른 페이지에서 참조하려면 노션·옵시디언 같은
클라우드 DB가 필요했다. Oxinot은 파일 + 블록 두 추상화를 같이 다룬다 —
.md는 그대로, 단위 참조는 블록 단위로. 그리고 그게 어차피 사람이 생각하는 방식이다.
6. 레포지토리
단일 모노레포. 프론트(Vite + React) + 셸(Tauri 2 + Rust) + 자동 릴리스 파이프라인.
데스크탑 빌드는 GitHub Releases에서 macOS(aarch64)용 .dmg로 받는다. 자동 체인지셋 + 릴리스 파이프라인이 커밋 메시지에서 버전을 추론해 PR을 올린다.