Skip to content

Commit e7b0b78

Browse files
jun0-dsclaude
andcommitted
feat: edge snapping, friendly process names, fix macOS build icons
- Add screen edge snapping (20px threshold) - Aggregate same-name processes and show friendly names - Add icon.png for macOS build - Fix bundle identifier (.app → .widget) - Fix CI rustflags to allow warnings - Add window position/monitor permissions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 384134f commit e7b0b78

9 files changed

Lines changed: 111 additions & 17 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ jobs:
2323
- uses: actions-rust-lang/setup-rust-toolchain@v1
2424
with:
2525
targets: ${{ matrix.target }}
26+
rustflags: ""
2627

2728
- uses: actions/setup-node@v4
2829
with:

docs/실사용피드백.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
만드신거 너무 멋지게 생겼는데요?! 코덱스 클 로드코드 토큰사용량도 측정하면 더좋겠어요 ㅋㅋㅋ
2+
openusage가 그거하고있으니까 그거 베끼면됨
3+
4+
크롬 익스텐션으로도 해주세여

src-tauri/capabilities/default.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
"core:window:allow-start-dragging",
99
"core:window:allow-set-size",
1010
"core:window:allow-inner-size",
11-
"core:window:allow-close"
11+
"core:window:allow-close",
12+
"core:window:allow-set-position",
13+
"core:window:allow-outer-position",
14+
"core:window:allow-outer-size",
15+
"core:window:allow-current-monitor",
16+
"core:window:allow-scale-factor"
1217
]
1318
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"default":{"identifier":"default","description":"Default capability","local":true,"windows":["main"],"permissions":["core:default","core:window:allow-start-dragging","core:window:allow-set-size","core:window:allow-inner-size","core:window:allow-close"]}}
1+
{"default":{"identifier":"default","description":"Default capability","local":true,"windows":["main"],"permissions":["core:default","core:window:allow-start-dragging","core:window:allow-set-size","core:window:allow-inner-size","core:window:allow-close","core:window:allow-set-position","core:window:allow-outer-position","core:window:allow-outer-size","core:window:allow-current-monitor","core:window:allow-scale-factor"]}}

src-tauri/icons/32x32.png

783 Bytes
Loading

src-tauri/icons/icon.png

12.2 KB
Loading

src-tauri/src/main.rs

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,33 @@ fn get_gpu_process_memory() -> std::collections::HashMap<u32, f64> {
8686
map
8787
}
8888

89+
fn friendly_name(raw: &str) -> String {
90+
let name = raw.strip_suffix(".exe").unwrap_or(raw);
91+
match name.to_lowercase().as_str() {
92+
"code" => "VS Code".into(),
93+
"msedgewebview2" | "msedge" => "Edge".into(),
94+
"chrome" => "Chrome".into(),
95+
"firefox" => "Firefox".into(),
96+
"discord" => "Discord".into(),
97+
"slack" => "Slack".into(),
98+
"spotify" => "Spotify".into(),
99+
"explorer" => "Explorer".into(),
100+
"dwm" => "Desktop Window Mgr".into(),
101+
"searchhost" => "Windows Search".into(),
102+
"runtimebroker" => "Runtime Broker".into(),
103+
"memory compression" => "Mem Compression".into(),
104+
"windowsterminal" => "Terminal".into(),
105+
"powershell" => "PowerShell".into(),
106+
"cmd" => "CMD".into(),
107+
"claude" => "Claude".into(),
108+
"teams" => "Teams".into(),
109+
"notion" => "Notion".into(),
110+
"obs64" | "obs" => "OBS".into(),
111+
"steam" | "steamwebhelper" => "Steam".into(),
112+
_ => name.to_string(),
113+
}
114+
}
115+
89116
#[tauri::command]
90117
fn get_top_processes(state: State<AppState>, sort_by: String) -> Vec<ProcessInfo> {
91118
let mut sys = state.sys.lock().unwrap();
@@ -96,18 +123,26 @@ fn get_top_processes(state: State<AppState>, sort_by: String) -> Vec<ProcessInfo
96123
#[cfg(not(windows))]
97124
let gpu_mem: std::collections::HashMap<u32, f64> = std::collections::HashMap::new();
98125

99-
let mut procs: Vec<_> = sys
100-
.processes()
101-
.values()
102-
.map(|p| {
103-
let pid = p.pid().as_u32();
104-
ProcessInfo {
105-
name: p.name().to_string_lossy().to_string(),
106-
cpu_usage: p.cpu_usage(),
107-
memory_mb: p.memory() as f64 / 1_048_576.0,
108-
gpu_memory_mb: gpu_mem.get(&pid).copied().unwrap_or(0.0),
109-
pid,
110-
}
126+
// Aggregate by friendly name
127+
let mut aggregated: std::collections::HashMap<String, (f32, f64, f64)> =
128+
std::collections::HashMap::new();
129+
for p in sys.processes().values() {
130+
let pid = p.pid().as_u32();
131+
let name = friendly_name(&p.name().to_string_lossy());
132+
let entry = aggregated.entry(name).or_insert((0.0, 0.0, 0.0));
133+
entry.0 += p.cpu_usage();
134+
entry.1 += p.memory() as f64 / 1_048_576.0;
135+
entry.2 += gpu_mem.get(&pid).copied().unwrap_or(0.0);
136+
}
137+
138+
let mut procs: Vec<_> = aggregated
139+
.into_iter()
140+
.map(|(name, (cpu, mem, gpu))| ProcessInfo {
141+
name,
142+
cpu_usage: cpu,
143+
memory_mb: mem,
144+
gpu_memory_mb: gpu,
145+
pid: 0,
111146
})
112147
.collect();
113148

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "https://raw.githubusercontent.com/nicoco007/tauri/dev-2/crates/tauri-utils/schema.json",
33
"productName": "Clance",
44
"version": "0.1.0",
5-
"identifier": "com.clance.app",
5+
"identifier": "com.clance.widget",
66
"build": {
77
"frontendDist": "../src",
88
"devUrl": "http://localhost:1420"

src/main.js

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
const { invoke } = window.__TAURI__.core;
2-
const { getCurrentWindow } = window.__TAURI__.window;
3-
const { LogicalSize } = window.__TAURI__.dpi;
2+
const { getCurrentWindow, currentMonitor } = window.__TAURI__.window;
3+
const { LogicalSize, LogicalPosition } = window.__TAURI__.dpi;
44

55
const WIDGET_WIDTH = 300;
6+
const SNAP_DISTANCE = 20;
67
let currentSort = 'cpu';
78

89
function formatGb(gb) {
@@ -115,6 +116,54 @@ document.getElementById('close-btn').addEventListener('click', () => {
115116
getCurrentWindow().close();
116117
});
117118

119+
// Edge snapping
120+
let snapTimeout = null;
121+
getCurrentWindow().listen('tauri://move', () => {
122+
if (snapTimeout) clearTimeout(snapTimeout);
123+
snapTimeout = setTimeout(snapToEdge, 50);
124+
});
125+
126+
async function snapToEdge() {
127+
try {
128+
const win = getCurrentWindow();
129+
const monitor = await currentMonitor();
130+
if (!monitor) return;
131+
132+
const pos = await win.outerPosition();
133+
const size = await win.outerSize();
134+
const scale = monitor.scaleFactor;
135+
136+
// Monitor work area (logical pixels)
137+
const mx = monitor.position.x / scale;
138+
const my = monitor.position.y / scale;
139+
const mw = monitor.size.width / scale;
140+
const mh = monitor.size.height / scale;
141+
142+
// Current position (physical → logical)
143+
let x = pos.x / scale;
144+
let y = pos.y / scale;
145+
const w = size.width / scale;
146+
const h = size.height / scale;
147+
148+
let snapped = false;
149+
150+
// Snap to left edge
151+
if (Math.abs(x - mx) < SNAP_DISTANCE) { x = mx; snapped = true; }
152+
// Snap to right edge
153+
if (Math.abs((x + w) - (mx + mw)) < SNAP_DISTANCE) { x = mx + mw - w; snapped = true; }
154+
// Snap to top edge
155+
if (Math.abs(y - my) < SNAP_DISTANCE) { y = my; snapped = true; }
156+
// Snap to bottom edge
157+
if (Math.abs((y + h) - (mx + mh)) < SNAP_DISTANCE) { y = my + mh - h; snapped = true; }
158+
159+
if (snapped) {
160+
await win.setPosition(new LogicalPosition(x, y));
161+
}
162+
} catch (e) {
163+
// ignore snap errors
164+
}
165+
}
166+
118167
// Start polling
119168
update();
120169
setInterval(update, 2000);

0 commit comments

Comments
 (0)