Skip to content
Open
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
7 changes: 7 additions & 0 deletions docs/releases/2.0.0_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- The main toolbar, properties panel, settings, modals, popups, pickers, and minimap now share one visual and behavioral baseline.
- The first-run default preset and the default styles for keys, knobs, graphs, stats, counters, and notes have been rebuilt.
- Replaced the app icon.
- The new key design was created with inspiration from [@DominoKorean](https://github.com/DominoKorean)'s [custom CSS skins](https://github.com/DominoKorean/DM-Note-skins).

- Moved settings into the Side Panel
- Sound, font, Custom CSS, and plugin settings are managed on the current screen without a separate manager window.
Expand Down Expand Up @@ -54,6 +55,7 @@
- `Combined` triggers only while all bound keys are held.
- Keyboard, mouse, and HID buttons (Windows) can be mixed within the same slot.
- Add or remove keys and switch the trigger mode directly in the binding popup.
- [@superalan89](https://github.com/superalan89) suggested [multi-key binding support](https://github.com/DmNote-App/DmNote/issues/71). 🎉

### Color, Image, and Font

Expand Down Expand Up @@ -148,6 +150,7 @@

- Added Intel Mac support [Issues 125](https://github.com/DmNote-App/DmNote/issues/125)
- macOS builds are now universal binaries, so the same DMG runs on both Intel and Apple silicon Macs.
- [@ByrenYan1](https://github.com/ByrenYan1) suggested [Intel Mac support](https://github.com/DmNote-App/DmNote/issues/125). 🎉

- Added support for high refresh rate displays on macOS [Issues 122](https://github.com/DmNote-App/DmNote/issues/122)
- The overlay and editing screen update more smoothly on 120Hz and faster displays.
Expand All @@ -173,6 +176,7 @@
- Image, sound, and font files are now quarantined in a per-session trash for 30 days instead of being deleted immediately
- Fixed half-written state after a conflict or crash by making save file replacement, asset restore, and failure rollback atomic
- Fixed custom images on knobs disappearing after a restart [Issues 91](https://github.com/DmNote-App/DmNote/issues/91)
- [@superalan89](https://github.com/superalan89) reported [custom images on knobs going missing](https://github.com/DmNote-App/DmNote/issues/91). 🎉
- Fixed an issue where older undo/redo steps revived already-deleted sound, image, counter motion, and plugin references

### Panel, Popup, and Input
Expand All @@ -193,6 +197,7 @@
- Lowered the minimum note corner radius to 0 and changed the default `Glow Size` to 10
- Fixed short notes disappearing abruptly or looking cut off at the display boundary
- Reduced key event delivery latency by enabling low-latency transmission on the OBS WebSocket [Issues 72](https://github.com/DmNote-App/DmNote/issues/72)
- [@aigonanMTE](https://github.com/aigonanMTE) reported [key viewer stuttering in OBS mode](https://github.com/DmNote-App/DmNote/issues/72). 🎉

### Color, Image, Font, and CSS

Expand All @@ -205,6 +210,7 @@

- Fixed plugin JavaScript failures being reported as success, and truncated error messages
- Fixed the pressed state in the key viewer flickering every time a plugin updated its state [Issues 111](https://github.com/DmNote-App/DmNote/issues/111)
- [@hjg-06](https://github.com/hjg-06) contributed to [the pressed state flickering on plugin state updates](https://github.com/DmNote-App/DmNote/issues/111). 🎉
- Fixed an exception in a plugin's `onMount` blanking the entire overlay
- Fixed late writes from a failed or removed plugin overwriting the new run state
- Restored compatibility for native `window` methods, async context, and existing global APIs used by plugins
Expand All @@ -217,6 +223,7 @@
- Fixed the overlay on macOS not reaching the top of the screen and leaving empty space below it [Issues 121](https://github.com/DmNote-App/DmNote/issues/121)
- `Snap to Edge` now measures from the screen edge instead of the work area, removing the gap left by the Dock and the taskbar
- The track height reserved at the top is excluded from the window height when note effects are off
- [@AL-Pinecore](https://github.com/AL-Pinecore) contributed to [overlay placement on macOS](https://github.com/DmNote-App/DmNote/issues/121). 🎉
- Added a quit item to the macOS app menu and stabilized Dock quit, restart, and helper process cleanup
- macOS no longer raises the accessibility permission prompt on its own while the app is running
- Fixed cancellations being reported as failures and the UI blocking by making file and preset dialogs async and parented to the calling window
Expand Down
7 changes: 7 additions & 0 deletions docs/releases/2.0.0_ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- 메인 툴바, 속성 패널, 설정, 모달, 팝업, 피커, 미니맵의 디자인과 동작을 하나의 기준으로 통일했습니다.
- 첫 실행 기본 프리셋과 키, 노브, 그래프, 통계, 카운터, 노트의 기본 스타일을 새로 구성했습니다.
- 앱 아이콘을 교체했습니다.
- 신규 키 디자인은 @DominoKorean 님의 [커스텀 CSS 스킨](https://github.com/DominoKorean/DM-Note-skins)에서 영감을 받아 만들었습니다.

- 설정을 사이드 패널로 이동
- 사운드, 폰트, 커스텀 CSS, 플러그인 설정을 별도 관리 창 없이 현재 화면에서 바로 관리할 수 있습니다.
Expand Down Expand Up @@ -54,6 +55,7 @@
- `동시 입력`은 등록한 키를 모두 누르고 있는 동안에만 반응합니다.
- 키보드, 마우스, HID 버튼(Windows)을 한 슬롯 안에서 섞어 쓸 수 있습니다.
- 매핑 팝업에서 키를 추가, 제거하고 반응 방식을 바로 선택할 수 있습니다.
- @superalan89 님이 [다중 키 매핑 지원](https://github.com/DmNote-App/DmNote/issues/71)을 제안해주셨습니다. 🎉

### 색상, 이미지 및 폰트

Expand Down Expand Up @@ -148,6 +150,7 @@

- 인텔 맥 지원 [Issues 125](https://github.com/DmNote-App/DmNote/issues/125)
- macOS 빌드를 유니버설 바이너리로 전환해 인텔 맥과 애플 실리콘 맥에서 같은 DMG를 사용합니다.
- @ByrenYan1 님이 [인텔 맥 지원](https://github.com/DmNote-App/DmNote/issues/125)을 제안해주셨습니다. 🎉

- macOS 고주사율 디스플레이 지원 [Issues 122](https://github.com/DmNote-App/DmNote/issues/122)
- 120Hz 이상 디스플레이에서 오버레이와 편집 화면이 더 부드럽게 갱신됩니다.
Expand All @@ -173,6 +176,7 @@
- 이미지, 사운드, 폰트 파일을 즉시 삭제하지 않고 세션별 휴지통에 30일 격리하도록 변경
- 저장 파일 교체, 자산 복원, 실패 롤백을 원자적으로 처리해 충돌이나 크래시 뒤 일부 상태만 남던 문제 수정
- 노브에 적용한 커스텀 이미지가 재실행 후 사라지던 문제 수정 [Issues 91](https://github.com/DmNote-App/DmNote/issues/91)
- @superalan89 님이 [노브 커스텀 이미지 누락 문제](https://github.com/DmNote-App/DmNote/issues/91)를 제보해주셨습니다. 🎉
- 사운드, 이미지, 카운터 모션, 플러그인 데이터를 삭제한 뒤 과거 Undo/Redo가 이미 삭제된 참조를 복원하던 문제 수정

### 패널, 팝업 및 입력
Expand All @@ -193,6 +197,7 @@
- 노트 라운딩 최솟값을 0으로 조정하고 기본 글로우 범위를 10으로 변경
- 짧은 노트가 표시 경계에서 갑자기 사라지거나 길이가 끊겨 보이던 문제 수정
- OBS WebSocket에 저지연 전송 옵션을 적용해 키 이벤트 전달 지연을 줄임 [Issues 72](https://github.com/DmNote-App/DmNote/issues/72)
- @aigonanMTE 님이 [OBS 모드 키뷰어 끊김 문제](https://github.com/DmNote-App/DmNote/issues/72)를 제보해주셨습니다. 🎉

### 색상, 이미지, 폰트 및 CSS

Expand All @@ -205,6 +210,7 @@

- 플러그인 JavaScript 실행 실패를 성공으로 표시하거나 오류 안내가 잘려 보이던 문제 수정
- 플러그인이 상태를 갱신할 때마다 키뷰어의 눌림 표시가 깜빡이던 문제 수정 [Issues 111](https://github.com/DmNote-App/DmNote/issues/111)
- @hjg-06 님이 [플러그인 상태 갱신 시 눌림 표시 깜빡임](https://github.com/DmNote-App/DmNote/issues/111)에 기여해주셨습니다. 🎉
- 플러그인 `onMount` 예외가 오버레이 전체를 비우던 문제 수정
- 실패했거나 제거된 플러그인의 늦은 저장이 새 실행 상태를 덮어쓰던 문제 수정
- 플러그인이 사용하는 `window`의 네이티브 메서드, 비동기 컨텍스트, 기존 전역 API 호환성 복구
Expand All @@ -217,6 +223,7 @@
- macOS 오버레이가 화면 위쪽에 닿지 않고 빈 공간이 남던 문제 수정 [Issues 121](https://github.com/DmNote-App/DmNote/issues/121)
- `화면 모서리에 맞추기` 기준을 작업 영역에서 화면 끝으로 정정해 독과 작업 표시줄 영역만큼 떠 있던 여백 제거
- 노트 효과를 끄면 상단에 예약되던 트랙 높이를 창 높이에서 함께 제외
- @AL-Pinecore 님이 [macOS 오버레이 배치 문제](https://github.com/DmNote-App/DmNote/issues/121)에 기여해주셨습니다. 🎉
- macOS 앱 메뉴에 종료 항목을 추가하고 Dock 종료, 재시작, 헬퍼 프로세스 정리 안정화
- macOS에서 앱 실행 중 접근성 권한 요청창이 저절로 뜨지 않도록 변경
- 파일과 프리셋 선택창을 호출한 창에 붙는 비동기 다이얼로그로 통일해, 취소를 실패로 알리거나 UI를 막던 문제 수정
Expand Down
3 changes: 2 additions & 1 deletion src-tauri/src/commands/keys/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,8 @@ fn keys_reset_all_inner(
&stat_positions,
&app_data_dir,
);
let settings_diff = apply_patch_to_store(store, &settings_patch);
let settings_diff = apply_patch_to_store(store, &settings_patch)
.map_err(crate::services::settings::settings_patch_validation_error)?;
Ok((settings_diff, cleared_tab_css_ids))
},
)?;
Expand Down
5 changes: 2 additions & 3 deletions src-tauri/src/commands/layout/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ pub async fn settings_update(
state
.store
.commit_history_overlap_mutation_with_admission(admission, |store| {
Ok(crate::services::settings::apply_patch_to_store(
store, &patch,
))
crate::services::settings::apply_patch_to_store(store, &patch)
.map_err(crate::services::settings::settings_patch_validation_error)
})?;
if let Some(previous) = previous.as_ref() {
state.resync_global_css_watcher(previous, &state.store.snapshot());
Expand Down
13 changes: 10 additions & 3 deletions src-tauri/src/commands/preset/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,8 @@ fn preset_load_from_path(
}
rekey_full_preset_elements(store);
crate::state::migration::clear_dangling_group_ids(store);
let diff = apply_patch_to_store(store, &settings_patch);
let diff = apply_patch_to_store(store, &settings_patch)
.map_err(crate::services::settings::settings_patch_validation_error)?;
Ok((
diff,
previous_tab_css_overrides,
Expand Down Expand Up @@ -824,7 +825,9 @@ fn preset_load_tab_from_path(
..SettingsPatchInput::default()
},
)
});
})
.transpose()
.map_err(crate::services::settings::settings_patch_validation_error)?;
crate::state::migration::clear_dangling_group_ids(store);
Ok((
settings_diff,
Expand Down Expand Up @@ -1791,6 +1794,7 @@ mod tests {
}];
let mut store = AppStoreData {
custom_css_history: history.clone(),
ui_theme: crate::models::UiTheme::Light,
..AppStoreData::default()
};
let mut preset = PresetFile {
Expand All @@ -1802,6 +1806,8 @@ mod tests {
..PresetFile::default()
};
let resolved = resolve_full_preset_settings(&mut preset, &store);
let preset_wire = serde_json::to_value(&preset).expect("preset 직렬화");
assert!(preset_wire.get("uiTheme").is_none());
let patch = SettingsPatchInput {
use_custom_css: Some(resolved.use_custom_css),
custom_css: Some(CustomCssPatch {
Expand All @@ -1811,9 +1817,10 @@ mod tests {
..SettingsPatchInput::default()
};

apply_patch_to_store(&mut store, &patch);
apply_patch_to_store(&mut store, &patch).expect("preset 설정 적용");

assert_eq!(store.custom_css_history, history);
assert_eq!(store.ui_theme, crate::models::UiTheme::Light);
}

#[test]
Expand Down
6 changes: 5 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ const INTERACTION_BENCHMARK_ENV: &str = "DMN_INTERACTION_WEBVIEW_BENCHMARK";
// 메인 창 논리 크기 - 고정 크기이고, 분리 패널이 이 사각형을 기준으로 옆에 붙는다
const MAIN_WINDOW_WIDTH: f64 = 902.0;
const MAIN_WINDOW_HEIGHT: f64 = 488.0;

fn is_interaction_benchmark_mode() -> bool {
std::env::var_os(INTERACTION_BENCHMARK_ENV).is_some()
}
Expand Down Expand Up @@ -595,6 +594,11 @@ fn apply_main_window_configuration(
log::debug!("failed to hide main window before configuration: {err}");
}

let preference = app.state::<AppState>().store.snapshot().ui_theme;
if let Err(err) = state::window_theme::apply_main_window_theme(&window, preference) {
log::warn!("failed to synchronize main window theme: {err:#}");
}

if cfg!(target_os = "windows") {
if let Err(err) = window.set_decorations(false) {
log::warn!("failed to disable decorations: {err}");
Expand Down
42 changes: 42 additions & 0 deletions src-tauri/src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2394,11 +2394,45 @@ pub struct LayerGroupDef {

pub type LayerGroups = HashMap<String, Vec<LayerGroupDef>>;

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "lowercase")]
pub enum UiTheme {
#[default]
System,
Light,
Dark,
}

impl TryFrom<&str> for UiTheme {
type Error = ();

fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"system" => Ok(Self::System),
"light" => Ok(Self::Light),
"dark" => Ok(Self::Dark),
_ => Err(()),
}
}
}

impl UiTheme {
pub(crate) fn as_tauri_theme(self) -> Option<tauri::Theme> {
match self {
Self::System => None,
Self::Light => Some(tauri::Theme::Light),
Self::Dark => Some(tauri::Theme::Dark),
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AppStoreData {
pub hardware_acceleration: bool,
pub always_on_top: bool,
#[serde(default)]
pub ui_theme: UiTheme,
pub overlay_locked: bool,
#[serde(default)]
pub overlay_visible: bool,
Expand Down Expand Up @@ -2500,6 +2534,7 @@ impl Default for AppStoreData {
Self {
hardware_acceleration: true,
always_on_top: true,
ui_theme: UiTheme::default(),
overlay_locked: false,
overlay_visible: false,
note_effect: false,
Expand Down Expand Up @@ -2560,6 +2595,7 @@ impl AppStoreData {
SettingsState {
hardware_acceleration: self.hardware_acceleration,
always_on_top: self.always_on_top,
ui_theme: self.ui_theme,
overlay_locked: self.overlay_locked,
note_effect: self.note_effect,
note_settings: self.note_settings.clone(),
Expand Down Expand Up @@ -2820,6 +2856,8 @@ pub struct DefaultsPayload {
pub struct SettingsState {
pub hardware_acceleration: bool,
pub always_on_top: bool,
#[serde(default)]
pub ui_theme: UiTheme,
pub overlay_locked: bool,
pub note_effect: bool,
#[serde(default)]
Expand Down Expand Up @@ -2888,6 +2926,7 @@ pub struct NoteSettingsPatch {
pub struct SettingsPatchInput {
pub hardware_acceleration: Option<bool>,
pub always_on_top: Option<bool>,
pub ui_theme: Option<String>,
pub overlay_locked: Option<bool>,
pub note_effect: Option<bool>,
pub note_settings: Option<NoteSettingsPatch>,
Expand Down Expand Up @@ -2949,6 +2988,7 @@ impl SettingsDiff {
[
p.hardware_acceleration.is_some(),
p.always_on_top.is_some(),
p.ui_theme.is_some(),
p.overlay_locked.is_some(),
p.note_effect.is_some(),
p.note_settings.is_some(),
Expand Down Expand Up @@ -2984,6 +3024,8 @@ pub struct SettingsPatch {
#[serde(skip_serializing_if = "Option::is_none")]
pub always_on_top: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ui_theme: Option<UiTheme>,
#[serde(skip_serializing_if = "Option::is_none")]
pub overlay_locked: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub note_effect: Option<bool>,
Expand Down
Loading