@neuralmindlabs/tokn 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +22 -0
- package/.oxlintrc.json +8 -0
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/assets/index-Dq74RfU-.css +1 -0
- package/dist/assets/index-_9e8Z58Z.js +66 -0
- package/dist/favicon.svg +1 -0
- package/dist/icons.svg +24 -0
- package/dist/index.html +14 -0
- package/index.html +13 -0
- package/package.json +45 -0
- package/packages/tokn/LICENSE +21 -0
- package/packages/tokn/README.md +85 -0
- package/packages/tokn/package.json +56 -0
- package/packages/tokn/src/aggregate.ts +83 -0
- package/packages/tokn/src/cli.ts +181 -0
- package/packages/tokn/src/cursor.ts +85 -0
- package/packages/tokn/src/index.ts +6 -0
- package/packages/tokn/src/node.ts +50 -0
- package/packages/tokn/src/parsers/claude.ts +53 -0
- package/packages/tokn/src/parsers/codex.ts +74 -0
- package/packages/tokn/src/pricing/index.ts +106 -0
- package/packages/tokn/src/pricing/prices.json +948 -0
- package/packages/tokn/src/pricing/prices.ts +947 -0
- package/packages/tokn/src/types.ts +51 -0
- package/packages/tokn/tests/__snapshots__/eval.test.ts.snap +12 -0
- package/packages/tokn/tests/aggregate.test.ts +73 -0
- package/packages/tokn/tests/claude.test.ts +87 -0
- package/packages/tokn/tests/codex.test.ts +54 -0
- package/packages/tokn/tests/cursor.test.ts +126 -0
- package/packages/tokn/tests/eval.test.ts +69 -0
- package/packages/tokn/tests/fixtures/claude-session.jsonl +3 -0
- package/packages/tokn/tests/fixtures/codex-session.jsonl +5 -0
- package/packages/tokn/tests/pricing.test.ts +137 -0
- package/packages/tokn/tsconfig.json +21 -0
- package/public/favicon.svg +1 -0
- package/public/icons.svg +24 -0
- package/scripts/validate.ts +122 -0
- package/src/App.tsx +12 -0
- package/src/assets/hero.png +0 -0
- package/src/assets/vite.svg +1 -0
- package/src/index.css +250 -0
- package/src/lib/refresh.ts +134 -0
- package/src/lib/scanner.ts +108 -0
- package/src/main.tsx +7 -0
- package/src/ui/Dashboard.tsx +203 -0
- package/src/ui/Popover.tsx +91 -0
- package/src/ui/Settings.tsx +114 -0
- package/src/ui/useAppData.ts +71 -0
- package/src-tauri/Cargo.lock +5972 -0
- package/src-tauri/Cargo.toml +35 -0
- package/src-tauri/build.rs +3 -0
- package/src-tauri/capabilities/default.json +114 -0
- package/src-tauri/icons/128x128.png +0 -0
- package/src-tauri/icons/128x128@2x.png +0 -0
- package/src-tauri/icons/32x32.png +0 -0
- package/src-tauri/icons/64x64.png +0 -0
- package/src-tauri/icons/Square107x107Logo.png +0 -0
- package/src-tauri/icons/Square142x142Logo.png +0 -0
- package/src-tauri/icons/Square150x150Logo.png +0 -0
- package/src-tauri/icons/Square284x284Logo.png +0 -0
- package/src-tauri/icons/Square30x30Logo.png +0 -0
- package/src-tauri/icons/Square310x310Logo.png +0 -0
- package/src-tauri/icons/Square44x44Logo.png +0 -0
- package/src-tauri/icons/Square71x71Logo.png +0 -0
- package/src-tauri/icons/Square89x89Logo.png +0 -0
- package/src-tauri/icons/StoreLogo.png +0 -0
- package/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml +5 -0
- package/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png +0 -0
- package/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png +0 -0
- package/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png +0 -0
- package/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png +0 -0
- package/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png +0 -0
- package/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
- package/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
- package/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/src-tauri/icons/android/values/ic_launcher_background.xml +4 -0
- package/src-tauri/icons/icon.icns +0 -0
- package/src-tauri/icons/icon.ico +0 -0
- package/src-tauri/icons/icon.png +0 -0
- package/src-tauri/icons/ios/AppIcon-20x20@1x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-20x20@2x-1.png +0 -0
- package/src-tauri/icons/ios/AppIcon-20x20@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-20x20@3x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-29x29@1x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-29x29@2x-1.png +0 -0
- package/src-tauri/icons/ios/AppIcon-29x29@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-29x29@3x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-40x40@1x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-40x40@2x-1.png +0 -0
- package/src-tauri/icons/ios/AppIcon-40x40@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-40x40@3x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-512@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-60x60@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-60x60@3x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-76x76@1x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-76x76@2x.png +0 -0
- package/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png +0 -0
- package/src-tauri/icons/source/app-icon-1024.png +0 -0
- package/src-tauri/icons/source/app-icon.svg +74 -0
- package/src-tauri/icons/source/tray-22.png +0 -0
- package/src-tauri/icons/source/tray-44.png +0 -0
- package/src-tauri/icons/source/tray-icon.svg +14 -0
- package/src-tauri/permissions/allow-custom-commands.toml +11 -0
- package/src-tauri/src/lib.rs +238 -0
- package/src-tauri/src/main.rs +6 -0
- package/src-tauri/tauri.conf.json +57 -0
- package/tsconfig.app.json +27 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +23 -0
- package/vite.config.ts +7 -0
- package/vitest.config.ts +28 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "app"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A Tauri App"
|
|
5
|
+
authors = ["you"]
|
|
6
|
+
license = ""
|
|
7
|
+
repository = ""
|
|
8
|
+
edition = "2021"
|
|
9
|
+
rust-version = "1.77.2"
|
|
10
|
+
|
|
11
|
+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
12
|
+
|
|
13
|
+
[lib]
|
|
14
|
+
name = "app_lib"
|
|
15
|
+
crate-type = ["staticlib", "cdylib", "rlib"]
|
|
16
|
+
|
|
17
|
+
[build-dependencies]
|
|
18
|
+
tauri-build = { version = "2.6.3", features = [] }
|
|
19
|
+
|
|
20
|
+
[dependencies]
|
|
21
|
+
serde_json = "1.0"
|
|
22
|
+
serde = { version = "1.0", features = ["derive"] }
|
|
23
|
+
log = "0.4"
|
|
24
|
+
tauri = { version = "2.11.3", features = ["tray-icon", "image-png", "macos-private-api"] }
|
|
25
|
+
tauri-plugin-log = "2"
|
|
26
|
+
tauri-plugin-fs = "2.5.1"
|
|
27
|
+
tauri-plugin-http = "2.5.9"
|
|
28
|
+
tauri-plugin-store = "2.4.3"
|
|
29
|
+
tauri-plugin-autostart = "2.5.1"
|
|
30
|
+
tauri-plugin-opener = "2.5.4"
|
|
31
|
+
|
|
32
|
+
[target.'cfg(target_os = "macos")'.dependencies]
|
|
33
|
+
cocoa = "0.25"
|
|
34
|
+
objc = "0.2"
|
|
35
|
+
tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2.1" }
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../gen/schemas/desktop-schema.json",
|
|
3
|
+
"identifier": "default",
|
|
4
|
+
"description": "Popover and dashboard windows: read AI tool logs, call Cursor API, persist settings",
|
|
5
|
+
"windows": [
|
|
6
|
+
"popover",
|
|
7
|
+
"dashboard"
|
|
8
|
+
],
|
|
9
|
+
"permissions": [
|
|
10
|
+
"core:default",
|
|
11
|
+
"core:window:allow-show",
|
|
12
|
+
"core:window:allow-hide",
|
|
13
|
+
"core:window:allow-set-focus",
|
|
14
|
+
"core:window:allow-set-position",
|
|
15
|
+
"core:window:allow-outer-size",
|
|
16
|
+
{
|
|
17
|
+
"identifier": "fs:allow-read-text-file",
|
|
18
|
+
"allow": [
|
|
19
|
+
{
|
|
20
|
+
"path": "$HOME/.claude/**"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"path": "$HOME/.codex/**"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"identifier": "fs:allow-read-text-file-lines",
|
|
29
|
+
"allow": [
|
|
30
|
+
{
|
|
31
|
+
"path": "$HOME/.claude/**"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"path": "$HOME/.codex/**"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"identifier": "fs:allow-read-dir",
|
|
40
|
+
"allow": [
|
|
41
|
+
{
|
|
42
|
+
"path": "$HOME/.claude"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"path": "$HOME/.claude/**"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"path": "$HOME/.codex"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"path": "$HOME/.codex/**"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"identifier": "fs:allow-stat",
|
|
57
|
+
"allow": [
|
|
58
|
+
{
|
|
59
|
+
"path": "$HOME/.claude/**"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"path": "$HOME/.codex/**"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"identifier": "fs:allow-exists",
|
|
68
|
+
"allow": [
|
|
69
|
+
{
|
|
70
|
+
"path": "$HOME/.claude"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"path": "$HOME/.claude/**"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"path": "$HOME/.codex"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"path": "$HOME/.codex/**"
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"identifier": "http:default",
|
|
85
|
+
"allow": [
|
|
86
|
+
{
|
|
87
|
+
"url": "https://cursor.com/*"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"url": "https://*.cursor.com/*"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"url": "https://raw.githubusercontent.com/*"
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
"store:default",
|
|
98
|
+
"autostart:allow-enable",
|
|
99
|
+
"autostart:allow-disable",
|
|
100
|
+
"autostart:allow-is-enabled",
|
|
101
|
+
"allow-custom-commands",
|
|
102
|
+
{
|
|
103
|
+
"identifier": "opener:allow-open-url",
|
|
104
|
+
"allow": [
|
|
105
|
+
{
|
|
106
|
+
"url": "https://cursor.com/*"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"url": "https://www.cursor.com/*"
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="face" x1="0.15" y1="0.05" x2="0.85" y2="0.95">
|
|
4
|
+
<stop offset="0" stop-color="#9a6bff"/>
|
|
5
|
+
<stop offset="0.45" stop-color="#6f6cfb"/>
|
|
6
|
+
<stop offset="1" stop-color="#3c7cff"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<linearGradient id="edge" x1="0" y1="0" x2="0" y2="1">
|
|
9
|
+
<stop offset="0" stop-color="#4a58c8"/>
|
|
10
|
+
<stop offset="1" stop-color="#2e4bd6"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
<radialGradient id="glow" cx="0.5" cy="0.42" r="0.65">
|
|
13
|
+
<stop offset="0" stop-color="#3c7cff" stop-opacity="0.35"/>
|
|
14
|
+
<stop offset="0.6" stop-color="#3c7cff" stop-opacity="0.08"/>
|
|
15
|
+
<stop offset="1" stop-color="#3c7cff" stop-opacity="0"/>
|
|
16
|
+
</radialGradient>
|
|
17
|
+
<linearGradient id="rim" x1="0" y1="0" x2="1" y2="1">
|
|
18
|
+
<stop offset="0" stop-color="#ffffff" stop-opacity="0.5"/>
|
|
19
|
+
<stop offset="1" stop-color="#ffffff" stop-opacity="0.06"/>
|
|
20
|
+
</linearGradient>
|
|
21
|
+
</defs>
|
|
22
|
+
|
|
23
|
+
<!-- squircle canvas -->
|
|
24
|
+
<rect x="64" y="64" width="896" height="896" rx="200" fill="#0f1821"/>
|
|
25
|
+
<rect x="64" y="64" width="896" height="896" rx="200" fill="url(#glow)"/>
|
|
26
|
+
|
|
27
|
+
<!-- coin, tilted -->
|
|
28
|
+
<g transform="rotate(-14 512 512)">
|
|
29
|
+
<!-- edge (thickness) -->
|
|
30
|
+
<ellipse cx="500" cy="548" rx="308" ry="300" fill="url(#edge)"/>
|
|
31
|
+
<!-- ridges hint on the lower edge -->
|
|
32
|
+
<g stroke="#20347f" stroke-width="10" opacity="0.55">
|
|
33
|
+
<line x1="268" y1="700" x2="252" y2="672"/>
|
|
34
|
+
<line x1="330" y1="756" x2="318" y2="726"/>
|
|
35
|
+
<line x1="402" y1="796" x2="394" y2="764"/>
|
|
36
|
+
<line x1="482" y1="816" x2="478" y2="782"/>
|
|
37
|
+
<line x1="562" y1="812" x2="562" y2="778"/>
|
|
38
|
+
<line x1="640" y1="786" x2="644" y2="754"/>
|
|
39
|
+
<line x1="708" y1="740" x2="716" y2="712"/>
|
|
40
|
+
</g>
|
|
41
|
+
<!-- face -->
|
|
42
|
+
<circle cx="512" cy="500" r="304" fill="url(#face)"/>
|
|
43
|
+
<!-- inner ring -->
|
|
44
|
+
<circle cx="512" cy="500" r="304" fill="none" stroke="url(#rim)" stroke-width="10"/>
|
|
45
|
+
<circle cx="512" cy="500" r="252" fill="none" stroke="#ffffff" stroke-opacity="0.16" stroke-width="6"/>
|
|
46
|
+
|
|
47
|
+
<!-- T glyph: slanted top bar + stem -->
|
|
48
|
+
<g fill="#ffffff">
|
|
49
|
+
<path d="M 360 388
|
|
50
|
+
L 664 360
|
|
51
|
+
Q 686 358 682 380
|
|
52
|
+
L 674 424
|
|
53
|
+
Q 671 442 652 444
|
|
54
|
+
L 380 468
|
|
55
|
+
Q 356 470 358 448
|
|
56
|
+
Z"/>
|
|
57
|
+
<path d="M 490 486
|
|
58
|
+
L 586 478
|
|
59
|
+
L 548 664
|
|
60
|
+
Q 540 700 504 704
|
|
61
|
+
L 462 708
|
|
62
|
+
Q 442 710 446 690
|
|
63
|
+
Z"/>
|
|
64
|
+
</g>
|
|
65
|
+
<!-- pixel dots trailing off the bar -->
|
|
66
|
+
<g fill="#ffffff">
|
|
67
|
+
<rect x="596" y="500" width="34" height="34" rx="6" opacity="0.9"/>
|
|
68
|
+
<rect x="646" y="500" width="34" height="34" rx="6" opacity="0.45"/>
|
|
69
|
+
<rect x="596" y="550" width="34" height="34" rx="6" opacity="0.6"/>
|
|
70
|
+
<rect x="646" y="550" width="34" height="34" rx="6" opacity="0.25"/>
|
|
71
|
+
<rect x="596" y="600" width="34" height="34" rx="6" opacity="0.3"/>
|
|
72
|
+
</g>
|
|
73
|
+
</g>
|
|
74
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg width="44" height="44" viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- menu bar template icon: pure black + alpha, macOS recolors it -->
|
|
3
|
+
<g transform="rotate(-12 22 22)">
|
|
4
|
+
<circle cx="22" cy="22" r="18" fill="none" stroke="#000000" stroke-width="3.4"/>
|
|
5
|
+
<!-- slanted T bar -->
|
|
6
|
+
<path fill="#000000" d="M 13.2 15.6 L 30.6 14.0 Q 31.9 13.9 31.6 15.2 L 31.2 17.4 Q 31.0 18.4 29.9 18.5 L 14.4 19.9 Q 13.0 20.0 13.1 18.8 Z"/>
|
|
7
|
+
<!-- stem -->
|
|
8
|
+
<path fill="#000000" d="M 20.2 21.4 L 25.4 21.0 L 23.4 30.4 Q 23.0 32.2 21.2 32.4 L 19.4 32.6 Q 18.3 32.7 18.5 31.6 Z"/>
|
|
9
|
+
<!-- dots -->
|
|
10
|
+
<rect x="26.4" y="21.8" width="2.6" height="2.6" rx="0.6" fill="#000000"/>
|
|
11
|
+
<rect x="29.8" y="21.8" width="2.6" height="2.6" rx="0.6" fill="#000000" opacity="0.55"/>
|
|
12
|
+
<rect x="26.4" y="25.2" width="2.6" height="2.6" rx="0.6" fill="#000000" opacity="0.55"/>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
use tauri::{
|
|
2
|
+
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
|
3
|
+
Manager, WindowEvent,
|
|
4
|
+
};
|
|
5
|
+
use std::sync::Mutex;
|
|
6
|
+
use std::time::Instant;
|
|
7
|
+
|
|
8
|
+
#[cfg(target_os = "macos")]
|
|
9
|
+
use tauri_nspanel::tauri_panel;
|
|
10
|
+
|
|
11
|
+
#[cfg(target_os = "macos")]
|
|
12
|
+
tauri_panel! {
|
|
13
|
+
panel!(Panel {
|
|
14
|
+
config: {
|
|
15
|
+
can_become_key_window: true,
|
|
16
|
+
can_become_main_window: false
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
panel_event!(PanelEventHandler {
|
|
21
|
+
window_did_resign_key(notification: &NSNotification) -> (),
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[allow(dead_code)]
|
|
26
|
+
struct PopoverState {
|
|
27
|
+
last_hidden: Mutex<Option<Instant>>,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl Default for PopoverState {
|
|
31
|
+
fn default() -> Self {
|
|
32
|
+
Self {
|
|
33
|
+
last_hidden: Mutex::new(None),
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#[tauri::command]
|
|
39
|
+
fn set_tray_title(app: tauri::AppHandle, title: String) {
|
|
40
|
+
if let Some(tray) = app.tray_by_id("main") {
|
|
41
|
+
let _ = tray.set_title(Some(title));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#[tauri::command]
|
|
46
|
+
fn show_dashboard(app: tauri::AppHandle) {
|
|
47
|
+
// Menu bar apps run as Accessory (no Dock icon); give the dashboard a
|
|
48
|
+
// Dock icon and normal cmd-tab presence while it's open.
|
|
49
|
+
#[cfg(target_os = "macos")]
|
|
50
|
+
let _ = app.set_activation_policy(tauri::ActivationPolicy::Regular);
|
|
51
|
+
if let Some(win) = app.get_webview_window("dashboard") {
|
|
52
|
+
let _ = win.show();
|
|
53
|
+
let _ = win.set_focus();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#[tauri::command]
|
|
58
|
+
fn hide_popover(app: tauri::AppHandle) {
|
|
59
|
+
#[cfg(target_os = "macos")]
|
|
60
|
+
{
|
|
61
|
+
use tauri_nspanel::ManagerExt;
|
|
62
|
+
if let Ok(panel) = app.get_webview_panel("popover") {
|
|
63
|
+
panel.hide();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if let Some(win) = app.get_webview_window("popover") {
|
|
68
|
+
let _ = win.hide();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
pub fn run() {
|
|
73
|
+
let mut builder = tauri::Builder::default();
|
|
74
|
+
|
|
75
|
+
#[cfg(target_os = "macos")]
|
|
76
|
+
{
|
|
77
|
+
builder = builder.plugin(tauri_nspanel::init());
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
builder
|
|
81
|
+
.plugin(tauri_plugin_fs::init())
|
|
82
|
+
.plugin(tauri_plugin_http::init())
|
|
83
|
+
.plugin(tauri_plugin_store::Builder::default().build())
|
|
84
|
+
.plugin(tauri_plugin_opener::init())
|
|
85
|
+
.plugin(tauri_plugin_autostart::init(
|
|
86
|
+
tauri_plugin_autostart::MacosLauncher::LaunchAgent,
|
|
87
|
+
None,
|
|
88
|
+
))
|
|
89
|
+
.manage(PopoverState::default())
|
|
90
|
+
.invoke_handler(tauri::generate_handler![set_tray_title, show_dashboard, hide_popover])
|
|
91
|
+
.setup(|app| {
|
|
92
|
+
#[cfg(target_os = "macos")]
|
|
93
|
+
{
|
|
94
|
+
app.set_activation_policy(tauri::ActivationPolicy::Accessory);
|
|
95
|
+
|
|
96
|
+
if let Some(win) = app.get_webview_window("popover") {
|
|
97
|
+
use tauri_nspanel::{CollectionBehavior, StyleMask, WebviewWindowExt};
|
|
98
|
+
|
|
99
|
+
let panel = win.to_panel::<Panel>().unwrap();
|
|
100
|
+
let handler = PanelEventHandler::new();
|
|
101
|
+
let handle = app.handle().clone();
|
|
102
|
+
handler.window_did_resign_key(move |_notification| {
|
|
103
|
+
use tauri_nspanel::ManagerExt;
|
|
104
|
+
if let Ok(panel) = handle.get_webview_panel("popover") {
|
|
105
|
+
panel.hide();
|
|
106
|
+
}
|
|
107
|
+
// Also update our state so next click toggles correctly
|
|
108
|
+
if let Some(state) = handle.try_state::<PopoverState>() {
|
|
109
|
+
if let Ok(mut last) = state.last_hidden.lock() {
|
|
110
|
+
*last = Some(std::time::Instant::now());
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
panel.set_event_handler(Some(handler.as_ref()));
|
|
115
|
+
|
|
116
|
+
// Non-activating: the panel can become key without activating the
|
|
117
|
+
// app. Activating would make macOS switch away from a fullscreen
|
|
118
|
+
// Space instead of showing the panel on top of it.
|
|
119
|
+
panel.set_style_mask(
|
|
120
|
+
StyleMask::empty().nonactivating_panel().value(),
|
|
121
|
+
);
|
|
122
|
+
panel.set_level(101); // NSPopUpMenuWindowLevel
|
|
123
|
+
// CanJoinAllSpaces (not MoveToActiveSpace, which doesn't work with
|
|
124
|
+
// fullscreen Spaces) + FullScreenAuxiliary, set once — toggling
|
|
125
|
+
// behaviors around show() races the window server's Space migration.
|
|
126
|
+
panel.set_collection_behavior(
|
|
127
|
+
CollectionBehavior::new()
|
|
128
|
+
.can_join_all_spaces()
|
|
129
|
+
.full_screen_auxiliary()
|
|
130
|
+
.ignores_cycle()
|
|
131
|
+
.value(),
|
|
132
|
+
);
|
|
133
|
+
panel.set_hides_on_deactivate(false);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let tray_icon = tauri::image::Image::from_bytes(include_bytes!("../icons/source/tray-44.png"))
|
|
138
|
+
.unwrap_or_else(|_| app.default_window_icon().unwrap().clone());
|
|
139
|
+
TrayIconBuilder::with_id("main")
|
|
140
|
+
.icon(tray_icon)
|
|
141
|
+
.icon_as_template(true)
|
|
142
|
+
.title("$–.––")
|
|
143
|
+
.on_tray_icon_event(|tray, event| {
|
|
144
|
+
if let TrayIconEvent::Click {
|
|
145
|
+
button: MouseButton::Left,
|
|
146
|
+
button_state: MouseButtonState::Up,
|
|
147
|
+
rect,
|
|
148
|
+
..
|
|
149
|
+
} = event
|
|
150
|
+
{
|
|
151
|
+
let app = tray.app_handle();
|
|
152
|
+
let state = app.state::<PopoverState>();
|
|
153
|
+
let now = Instant::now();
|
|
154
|
+
|
|
155
|
+
let recently_hidden = if let Some(last_hidden) = *state.last_hidden.lock().unwrap() {
|
|
156
|
+
now.duration_since(last_hidden).as_millis() < 200
|
|
157
|
+
} else {
|
|
158
|
+
false
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
if recently_hidden {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if let Some(win) = app.get_webview_window("popover") {
|
|
166
|
+
#[cfg(target_os = "macos")]
|
|
167
|
+
{
|
|
168
|
+
use tauri_nspanel::ManagerExt;
|
|
169
|
+
if let Ok(panel) = app.get_webview_panel("popover") {
|
|
170
|
+
if panel.is_visible() {
|
|
171
|
+
panel.hide();
|
|
172
|
+
*state.last_hidden.lock().unwrap() = Some(Instant::now());
|
|
173
|
+
} else {
|
|
174
|
+
let icon_pos = rect.position.to_physical::<f64>(1.0);
|
|
175
|
+
let icon_size = rect.size.to_physical::<f64>(1.0);
|
|
176
|
+
let width = win
|
|
177
|
+
.outer_size()
|
|
178
|
+
.map(|s| s.width as f64)
|
|
179
|
+
.unwrap_or(380.0);
|
|
180
|
+
let x = icon_pos.x + icon_size.width / 2.0 - width / 2.0;
|
|
181
|
+
let y = icon_pos.y + icon_size.height + 4.0;
|
|
182
|
+
let _ = win.set_position(tauri::PhysicalPosition::new(x, y));
|
|
183
|
+
|
|
184
|
+
// Order front + make key without activating the app
|
|
185
|
+
// (the panel is non-activating), so it appears on the
|
|
186
|
+
// current Space even when a fullscreen app is active.
|
|
187
|
+
panel.show_and_make_key();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
#[cfg(not(target_os = "macos"))]
|
|
193
|
+
{
|
|
194
|
+
let is_visible = win.is_visible().unwrap_or(false);
|
|
195
|
+
if is_visible {
|
|
196
|
+
let _ = win.hide();
|
|
197
|
+
*state.last_hidden.lock().unwrap() = Some(Instant::now());
|
|
198
|
+
} else {
|
|
199
|
+
let icon_pos = rect.position.to_physical::<f64>(1.0);
|
|
200
|
+
let icon_size = rect.size.to_physical::<f64>(1.0);
|
|
201
|
+
let width = win
|
|
202
|
+
.outer_size()
|
|
203
|
+
.map(|s| s.width as f64)
|
|
204
|
+
.unwrap_or(380.0);
|
|
205
|
+
let x = icon_pos.x + icon_size.width / 2.0 - width / 2.0;
|
|
206
|
+
let y = icon_pos.y + icon_size.height + 4.0;
|
|
207
|
+
let _ = win.set_position(tauri::PhysicalPosition::new(x, y));
|
|
208
|
+
let _ = win.show();
|
|
209
|
+
let _ = win.set_focus();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
.build(app)?;
|
|
216
|
+
Ok(())
|
|
217
|
+
})
|
|
218
|
+
.on_window_event(|window, event| {
|
|
219
|
+
if window.label() == "popover" {
|
|
220
|
+
if let WindowEvent::Focused(false) = event {
|
|
221
|
+
let _ = window.hide();
|
|
222
|
+
let state = window.state::<PopoverState>();
|
|
223
|
+
*state.last_hidden.lock().unwrap() = Some(Instant::now());
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if window.label() == "dashboard" {
|
|
227
|
+
if let WindowEvent::CloseRequested { api, .. } = event {
|
|
228
|
+
api.prevent_close();
|
|
229
|
+
let _ = window.hide();
|
|
230
|
+
// Back to menu-bar-only: drop the Dock icon
|
|
231
|
+
#[cfg(target_os = "macos")]
|
|
232
|
+
let _ = window.app_handle().set_activation_policy(tauri::ActivationPolicy::Accessory);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
.run(tauri::generate_context!())
|
|
237
|
+
.expect("error while running tauri application");
|
|
238
|
+
}
|