Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

| 날짜 | 버전 | 변경 |
| --- | --- | --- |
| 2026-10-30 | v3.77 | **0.5.2 릴리스 준비(브랜치 `fix/mic-config-change-crash`).** 버전 0.5.2를 다섯 매니페스트와 잠금 파일에 맞췄다. 0.5.1 이후: 한국어 파인튜닝 엔진 `asr-ko-univa` 등록과 모델 화면 변환 엔진 라디오 목록(v3.75), 장치 전환 중 녹음 프로그램 크래시 수정(v3.76, 이슈 #57). 릴리스 노트 `docs/releases/v0.5.2.md`, 로드맵 항목. |
| 2026-10-30 | v3.76 | **녹음 중 입력 장치가 바뀌면 `tractus-rec`가 죽던 것(이슈 #57, 브랜치 `fix/mic-config-change-crash`).** `AVAudioEngineConfigurationChange` → `Recorder.restart` → `MicSource.open`의 `installTap`이 Objective-C 예외(NSException)를 던져 Swift `do/catch`를 지나 프로세스가 abort됐다(66분 녹음 중 크래시, `recording.json`의 `duration`이 null로 남음). ① `RecorderKit.micFormatProblem`: tap 전에 채널·표본 속도 0과 하드웨어 포맷 불일치를 오류로 던진다(`restart`의 경고와 watchdog 2초 재시도로 넘어감). 음성 처리(에코 제거) 중에는 두 포맷이 다를 수 있어 표본 속도 일치는 보지 않는다. ② `MicSource.stop()`마다 새 `AVAudioEngine`으로 바꾸고 관찰자를 다시 단다(장치가 바뀐 뒤 같은 엔진의 입력 노드가 낡은 포맷을 쥐는 것을 피함). ③ 새 SwiftPM 타깃 `ObjCCatch`(Objective-C, `TractusObjC.catching { }`)로 `installTap`을 감싸 NSException을 Swift 오류로 바꾼다(`recorder/`의 tools-version 5.10·플랫폼은 그대로). 이 Mac의 실제 마이크로 일반·에코 제거 녹음이 정상임은 확인했고, **실제 장치 전환(블루투스 연결·해제)은 재현하지 못했다.** 비정상 종료로 `duration`이 null인 녹음은 앱의 `변환 대기 중인 녹음`에 보이고 코어가 그대로 변환 전 노트로 저장한다는 것을 테스트로 고정했다. |
| 2026-10-30 | v3.75 | **변환 엔진 목록(브랜치 `feat/engine-list-univa`).** 소유자 불만: 0.5.1의 드롭다운에는 받아 둔 엔진만 있었고, 설치한 앱의 모델 폴더(`~/Library/Application Support/dev.tractus.app/models`)에는 개발 Mac의 `models/custom-models.json`에 넣은 TeamUNIVA 모델이 없었다. ① `asr-ko-univa`(`TeamUNIVA/qwen3_asr_1.7b_ko` 3e66f586, Apache-2.0, bf16 4.7GB, 계열 `qwen3`)를 등록 목록에 올렸다(`level_note: one_of`, 벤치마크 면제: CLAUDE.md 기록). `--preset all`은 약 12GB. ② 모델 화면 디자인: 변환 엔진 선택(드롭다운)·받기/삭제(평평한 모델 목록 속)·직접 추가(맨 아래)가 세 곳에 흩어져 있었고, 모델 10개가 같은 무게로 줄줄이 늘어서 한 줄이 5줄·오른쪽 열이 두 줄이었다. 이제 `변환 엔진` 구역 하나에 라디오 목록(자동 + 엔진 넷, 받지 않은 엔진도 고르면 설정에 저장하고 받기 시작, 받기 전에는 받아 둔 다른 엔진으로 변환)과 직접 추가를 모았다. 줄 제목은 모델 이름(`model_name`), 크기·상태·삭제·받기는 한 줄에 같은 자리. 엔진이 아닌 모델은 `다른 기능의 모델` 구역(컴포넌트 `EngineChoice`, `renderActions`). |
| 2026-10-30 | v3.74 | **0.5.1 릴리스 준비(브랜치 `release/0.5.1`).** 버전 0.5.1을 다섯 매니페스트와 잠금 파일에 맞췄다. ① 사이드바 노트 목록: 날짜별 묶음을 달 머리글(접고 펼침, `SidebarFold.months`에 직접 정한 값만 저장, 기본은 이번 달·지난달·열어 둔 노트가 든 달만 펼침, 거르면 모두 펼침)로 묶고 검색창과 달 머리글을 `sticky`로 붙였다. 검색창(`QuickFind`)은 입력하는 동안 제목이 맞는 노트를 바로 열 수 있게 하고(↑↓·Enter, `quickMatch.ts`), 고르지 않은 Enter는 예전처럼 내용 검색(SearchView)이다. ② 모델 화면 맨 위 `변환 엔진` 드롭다운으로 기본 엔진을 고른다(줄마다 있던 `기본으로 쓰기`·`자동으로 고르기` 단추를 없앴다, 받아 둔 엔진만 고를 수 있음). ③ 코어 `asr.Transcriber._transcribe`: Qwen3 계열이 자동 감지에서 토큰 0개(영어로 오감지)를 내면 그 청크만 한국어로 다시 변환한다(직접 추가한 TeamUNIVA/qwen3_asr_1.7b_ko 간이 점검에서 30초 청크의 27%가 빔: docs/design/asr-engines.md). 직접 추가한 엔진이 있는 개발 Mac에서 실패하던 `test_convert_needs_any_one_transcription_engine`을 빈 모델 폴더 기준으로 고쳤다. 릴리스 노트 `docs/releases/v0.5.1.md`, 로드맵 항목. |
| 2026-10-29 | v3.73 | **0.5.0 릴리스 준비(브랜치 `release/0.5.0`).** 버전 0.5.0을 다섯 매니페스트(`tauri.conf.json`·`Cargo.toml`·`app/package.json`·`pyproject.toml`·플러그인 `plugin.json`)와 잠금 파일에 맞췄다. 0.4.4 이후: 변환 중에도 녹음(v3.71), 내보내기에서 넣을 내용 고르기(v3.72), 시작 때 새 버전을 큰 창으로 알림, 글만 있는 노트의 떠 있는 녹음 추가 단추, 새 앱 아이콘과 사이드바 로고. 이름은 Tractus 그대로(Oat로 바꾸기는 소유자가 취소). README 스크린샷·데모 GIF를 새 아이콘으로 다시 찍었다. 릴리스 노트 `docs/releases/v0.5.0.md`, 로드맵 항목. |
Expand Down
2 changes: 1 addition & 1 deletion app/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "tractus-app",
"private": true,
"version": "0.5.1",
"version": "0.5.2",
"license": "Apache-2.0",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion app/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tractus-app"
version = "0.5.1"
version = "0.5.2"
description = "Tractus 데스크톱 앱"
license = "Apache-2.0"
edition = "2021"
Expand Down
2 changes: 1 addition & 1 deletion app/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Tractus",
"version": "0.5.1",
"version": "0.5.2",
"identifier": "dev.tractus.app",
"build": {
"beforeDevCommand": "pnpm dev",
Expand Down
55 changes: 55 additions & 0 deletions docs/releases/v0.5.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
version: 0.5.2
date: 2026-10-30
summary: 설정 › 모델에서 변환 엔진을 목록에서 바로 고르고 받을 수 있습니다. 한국어 파인튜닝 엔진을 목록에 더했고, 녹음 중 입력 장치가 바뀌면 녹음이 멈추던 문제를 고쳤습니다.
summary_en: Pick and download a transcription engine straight from a list in Settings › Models. A Korean fine-tuned engine is now listed, and recording no longer stops when the input device changes mid-recording.
---
# Tractus v0.5.2

녹음과 메모를 내 Mac 안에서 텍스트로 바꾸고, 요약·검색·AI 질문까지 가능한 앱입니다. 이번 버전은 변환 엔진을 고르는 화면을 다시 만들고, 녹음 중 장치가 바뀔 때 녹음이 죽던 문제를 고쳤습니다. (English below)

## 이번 버전에서 바뀐 것

- **변환 엔진을 한 목록에서 고릅니다.** 설정 › 모델의 `변환 엔진`에 `자동`과 엔진들이 라디오 목록으로 모였습니다.
- 받지 않은 엔진도 고를 수 있습니다. 고르면 바로 받기 시작하고, 다 받기 전에는 받아 둔 다른 엔진으로 변환합니다.
- 각 줄에서 모델 이름, 만든 곳, 크기, 상태, 받기·삭제를 한눈에 봅니다. `변환 엔진 직접 추가`도 같은 구역으로 옮겼습니다.
- 변환 엔진이 아닌 모델(목소리 구간 감지, 참석자 구분 등)은 아래 `다른 기능의 모델`에 따로 묶었습니다.
- **한국어 파인튜닝 엔진을 목록에 더했습니다.** `Qwen3-ASR 1.7B 한국어 (bf16)`(TeamUNIVA, 4.7GB, Apache-2.0)입니다. 자주 쓰는 단어를 쓸 수 있습니다.
- 아직 간이 점검만 했습니다(회의 5분 구간에서 반복 루프 없음). 한·영 혼용 정확도는 재지 않았습니다. 직접 써 보고 판단해 주세요.
- 이 엔진은 언어 자동 감지가 영어로 잘못 보고 구간을 비우는 일이 있어, 빈 글이 나오면 그 구간만 한국어로 다시 변환합니다.
- **녹음 중 입력 장치가 바뀌어도 녹음이 이어집니다.** AirPods 같은 블루투스 장치를 연결하거나 끊을 때 녹음 프로그램이 통째로 멈추던 문제(이슈 #57)를 고쳤습니다. 장치가 아직 준비되지 않았으면 오류로 알리고 2초 뒤 다시 연결합니다. 이 Mac의 마이크와 에코 제거 녹음이 정상인 것은 확인했지만, 실제 장치를 바꾸는 상황은 재현해 확인하지 못했습니다. 같은 문제가 또 생기면 알려 주세요.
- 멈춘 녹음이 있다면 `recordings` 폴더의 소리 파일은 남아 있고, 사이드바 `변환 대기 중인 녹음`에서 변환할 수 있습니다.

## 업데이트·설치

- **v0.1.1 이상을 쓰고 있다면:** 설정 › 업데이트에서 `업데이트 확인` → `업데이트 설치` → `다시 열기`.
- **새로 설치한다면:** DMG를 열고 `Tractus.app`을 `Applications`로 끌어다 놓습니다.
- 처음 열 때 "확인되지 않은 개발자" 경고가 나오면: 시스템 설정 → 개인정보 보호 및 보안 → "그래도 열기" (또는 `xattr -dr com.apple.quarantine /Applications/Tractus.app`)
- **ffmpeg가 필요합니다:** `brew install ffmpeg`
- 노트·모델·설정 파일은 그대로입니다.

## 받을 파일

| 파일 | 설명 |
| --- | --- |
| `Tractus-0.5.2-arm64.dmg` | 설치 파일 (Apple Silicon 전용) |
| `Tractus-0.5.2-arm64.dmg.sha256` | 체크섬. `shasum -a 256 -c Tractus-0.5.2-arm64.dmg.sha256` |
| `Tractus.app.tar.gz`, `.sig`, `latest.json` | 앱 안 업데이트가 쓰는 파일입니다. 직접 받을 필요는 없습니다. |

## 요구 사항

- Apple Silicon(M1 이상), 메모리 16GB 이상 권장(8GB는 저사양 구성)
- macOS 14.4 이상(설계상 최소)

---

## English

Tractus turns recordings and memos into text on your Mac, then summarizes, searches and answers questions about them. **New in 0.5.2: a simpler way to choose the transcription engine, and a fix for recording stopping when the input device changes.**

- **Choose the engine from one list.** In Settings › Models, `Transcription engine` is now a radio list with `Automatic` and each engine. You can pick an engine you haven't downloaded: it starts downloading right away, and until it finishes the engines you already have are used. Each row shows the model name, publisher, size, status and download/delete; `Add a transcription engine` moved into the same section. Models that aren't transcription engines sit below under `Models for other features`.
- **A Korean fine-tuned engine is listed.** `Qwen3-ASR 1.7B Korean (bf16)` (TeamUNIVA, 4.7GB, Apache-2.0) supports custom vocabulary. It has only had a quick check (no repetition loops over a 5-minute stretch of a meeting); mixed Korean–English accuracy wasn't measured. When its language auto-detection returns nothing for a chunk, that chunk is transcribed again as Korean.
- **Recording survives an input-device change.** Connecting or disconnecting a Bluetooth device such as AirPods during a recording used to abort the recorder (issue #57). If the device isn't ready, it now reports an error and reconnects after 2 seconds. Normal and echo-cancelled recording on a Mac were checked, but a real device switch could not be reproduced here — please report it if it happens again. If a recording was cut short, its audio is still in the `recordings` folder and can be transcribed from `Recordings waiting to transcribe` in the sidebar.
- Install: open the DMG and drag `Tractus.app` to `Applications`; you need `brew install ffmpeg`. The build isn't notarized yet — use System Settings › Privacy & Security › Open Anyway on first launch. Apple Silicon, macOS 14.4+.

Guides: [Setup](https://github.com/sjoon21/Tractus/blob/main/docs/en/setup.md) · [Usage](https://github.com/sjoon21/Tractus/blob/main/docs/en/usage.md) · [Agents (MCP)](https://github.com/sjoon21/Tractus/blob/main/docs/en/mcp.md)
1 change: 1 addition & 0 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
- [x] 0.4.4: 스마트 교정 강화(군말·반복·문장부호·띄어쓰기·맞춤법까지, 녹취록 전체 문맥, 받은 용어를 한 번에 자주 쓰는 단어로), 앱 경량화(설치 698 → 538MB, 업데이트 198 → 154MB, 앱 반응·변환 시작 빨라짐), AI 프롬프트를 md 파일로 (v3.67~v3.69)
- [x] 0.5.0: 변환 중에도 녹음, 내보내기에서 넣을 요약·내용 고르기, 시작 때 새 버전 안내 창, 글만 있는 노트의 떠 있는 녹음 추가 단추, 새 아이콘 (v3.71~v3.73)
- [x] 0.5.1: 사이드바 노트 목록이 길어져도 쓰기 쉽게(달 묶음 접기·위에 붙는 검색창과 달 머리글·제목 바로가기), 모델 화면의 변환 엔진 드롭다운, 자동 감지가 빈 글을 낼 때 한국어로 다시 변환 (v3.74)
- [x] 0.5.2: 모델 화면 변환 엔진을 라디오 목록 하나로(받지 않은 엔진도 고르면 받기 시작), 한국어 파인튜닝 엔진(TeamUNIVA) 목록에 추가, 녹음 중 입력 장치가 바뀌면 녹음이 죽던 것 수정 (v3.75~v3.77, 이슈 #57)
- [ ] Windows 지원 (MLX를 대신할 런타임, Windows 녹음 프로그램, 설치·서명). 2026-10-22 소유자 결정: macOS에서 충분히 다듬은 뒤로 최대한 미룬다(1.0일 수 있다)
- [ ] 영어 화면에서 실제 AI 요약·노트 완성 품질 확인
- [ ] 깨끗한 Mac(새 계정)과 macOS 14.4~15에서 미리 만든 앱 실행, 내려받은 `.dmg`의 Gatekeeper 동작과 권한 이름 확인
Expand Down
2 changes: 1 addition & 1 deletion plugins/tractus/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "tractus",
"displayName": "Tractus",
"version": "0.5.1",
"version": "0.5.2",
"description": "Your meetings as local memory for Claude Code: search notes, transcripts, decisions and action items recorded on this Mac with Tractus. Nothing leaves your Mac except what Claude reads.",
"author": { "name": "sjoon21", "url": "https://github.com/sjoon21" },
"homepage": "https://github.com/sjoon21/Tractus",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "tractus"
version = "0.5.1"
version = "0.5.2"
description = "Local-first 노트·녹음 도구"
readme = "README.md"
license = "Apache-2.0"
Expand Down
6 changes: 4 additions & 2 deletions recorder/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ let package = Package(
],
targets: [
.target(name: "RecorderKit"),
// Swift do/catch가 못 잡는 Objective-C 예외(AVAudioNode.installTap 등)를 NSError로 바꾸는 작은 C 계열 타깃
.target(name: "ObjCCatch"),
.executableTarget(
name: "tractus-rec",
dependencies: ["RecorderKit"],
dependencies: ["RecorderKit", "ObjCCatch"],
linkerSettings: [
.unsafeFlags(["-Xlinker", "-sectcreate", "-Xlinker", "__TEXT", "-Xlinker", "__info_plist", "-Xlinker", infoPlist]),
]
),
// tractus-rec에 의존해 swift test가 실행 파일도 빌드하게 한다(fake 모드 E2E 테스트용).
.testTarget(name: "RecorderKitTests", dependencies: ["RecorderKit", "tractus-rec"]),
.testTarget(name: "RecorderKitTests", dependencies: ["RecorderKit", "ObjCCatch", "tractus-rec"]),
],
swiftLanguageVersions: [.v5]
)
18 changes: 18 additions & 0 deletions recorder/Sources/ObjCCatch/ObjCCatch.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#import "ObjCCatch.h"

@implementation TractusObjC

+ (BOOL)catching:(NS_NOESCAPE void (^)(void))block error:(NSError *_Nullable *_Nullable)error {
@try {
block();
return YES;
} @catch (NSException *exception) {
if (error) {
NSString *text = [NSString stringWithFormat:@"%@: %@", exception.name, exception.reason ?: @""];
*error = [NSError errorWithDomain:@"tractus.objc" code:1 userInfo:@{NSLocalizedDescriptionKey: text}];
}
return NO;
}
}

@end
13 changes: 13 additions & 0 deletions recorder/Sources/ObjCCatch/include/ObjCCatch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface TractusObjC : NSObject

/// block 안에서 던진 Objective-C 예외를 NSError로 바꾼다. Swift의 do/catch는 NSException을 잡지 못해 프로세스가 abort되므로
/// AVFAudio처럼 예외를 던지는 호출(installTap 등)을 감싸 쓴다. Swift에서는 `try TractusObjC.catching { … }`.
+ (BOOL)catching:(NS_NOESCAPE void (^)(void))block error:(NSError *_Nullable *_Nullable)error;

@end

NS_ASSUME_NONNULL_END
15 changes: 15 additions & 0 deletions recorder/Sources/RecorderKit/MicFormat.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/// 마이크 입력 노드에 tap을 걸어도 되는지 본다. 걸어도 되면 nil, 아니면 이유.
/// 장치를 막 바꾼 뒤에는 채널 0이나 낡은 표본 속도가 남아 있을 수 있고, 그때 installTap은 NSException을 던진다(2026-10-30 이슈 #57).
/// 음성 처리(에코 제거)를 켜면 tap 포맷과 하드웨어 포맷이 다를 수 있어 표본 속도 일치는 음성 처리가 꺼져 있을 때만 본다.
public func micFormatProblem(tapRate: Double, tapChannels: Int, hardwareRate: Double, hardwareChannels: Int, voiceProcessing: Bool) -> String? {
if tapRate <= 0 || tapChannels <= 0 {
return "입력 장치 포맷이 아직 준비되지 않았습니다 (\(Int(tapRate))Hz, \(tapChannels)채널)"
}
if hardwareRate <= 0 || hardwareChannels <= 0 {
return "입력 장치가 아직 준비되지 않았습니다 (\(Int(hardwareRate))Hz, \(hardwareChannels)채널)"
}
if !voiceProcessing && tapRate != hardwareRate {
return "입력 장치 포맷이 바뀌는 중입니다 (\(Int(tapRate))Hz ≠ \(Int(hardwareRate))Hz)"
}
return nil
}
Loading
Loading