nixii 0.1.0__tar.gz
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.
- nixii-0.1.0/PKG-INFO +116 -0
- nixii-0.1.0/README.md +101 -0
- nixii-0.1.0/app/__init__.py +1 -0
- nixii-0.1.0/app/assets/110371-688648556_medium.mp4 +0 -0
- nixii-0.1.0/app/assets/Emo Robot Eye .mp4 +0 -0
- nixii-0.1.0/app/assets/Q19WSHi0PH.svg +1 -0
- nixii-0.1.0/app/assets/eyesv2.gif +0 -0
- nixii-0.1.0/app/config.py +41 -0
- nixii-0.1.0/app/environment.py +37 -0
- nixii-0.1.0/app/event_log.py +18 -0
- nixii-0.1.0/app/popup/__init__.py +1 -0
- nixii-0.1.0/app/popup/launcher.py +18 -0
- nixii-0.1.0/app/popup/main.py +405 -0
- nixii-0.1.0/app/server/__init__.py +1 -0
- nixii-0.1.0/app/server/actions.py +166 -0
- nixii-0.1.0/app/server/approve_cli.py +12 -0
- nixii-0.1.0/app/server/config.py +347 -0
- nixii-0.1.0/app/server/discovery.py +1038 -0
- nixii-0.1.0/app/server/llm.py +593 -0
- nixii-0.1.0/app/server/main.py +355 -0
- nixii-0.1.0/app/server/safety.py +136 -0
- nixii-0.1.0/app/server/server_log.py +135 -0
- nixii-0.1.0/app/server/vision.py +77 -0
- nixii-0.1.0/app/server/web_search.py +56 -0
- nixii-0.1.0/app/voice/__init__.py +1 -0
- nixii-0.1.0/app/voice/audio.py +108 -0
- nixii-0.1.0/app/voice/main.py +299 -0
- nixii-0.1.0/app/voice/popup_control.py +33 -0
- nixii-0.1.0/app/voice/recognition.py +10 -0
- nixii-0.1.0/app/voice/server_client.py +81 -0
- nixii-0.1.0/app/voice/stt.py +276 -0
- nixii-0.1.0/app/voice/tts.py +152 -0
- nixii-0.1.0/pyproject.toml +30 -0
nixii-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: nixii
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local voice-command assistant for Linux
|
|
5
|
+
Requires-Dist: aiohttp==3.14.3
|
|
6
|
+
Requires-Dist: ddgs>=9.14.4
|
|
7
|
+
Requires-Dist: faster-whisper==1.2.1
|
|
8
|
+
Requires-Dist: google-genai>=2.16.0
|
|
9
|
+
Requires-Dist: numpy==2.5.1
|
|
10
|
+
Requires-Dist: rich>=15.0.0
|
|
11
|
+
Requires-Dist: sarvamai>=0.1.31
|
|
12
|
+
Requires-Dist: tenacity>=9.1.4
|
|
13
|
+
Requires-Python: >=3.13, <3.14
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# Nixi
|
|
17
|
+
|
|
18
|
+
A desktop voice assistant for Linux. Trigger Nixi, ask a question or give a
|
|
19
|
+
command, and it handles it — change the volume, set a wallpaper, take a
|
|
20
|
+
screenshot, control media, or answer general queries using Gemini.
|
|
21
|
+
|
|
22
|
+
## What It Does
|
|
23
|
+
|
|
24
|
+
- **System control** — volume, brightness, mute, media playback, screenshots,
|
|
25
|
+
wallpaper changes, app launcher.
|
|
26
|
+
- **General queries** — ask anything. Nixi uses Gemini and can search the web
|
|
27
|
+
for live information like weather, news, and prices.
|
|
28
|
+
- **Vision** — say "open your eyes", "where should I click", or "why is this
|
|
29
|
+
red" and Nixi asks permission to screenshot your screen, then describes what
|
|
30
|
+
it sees. Approval is via desktop notification with a configurable timeout.
|
|
31
|
+
- **Voice interaction** — manual trigger-to-record mechanism, speech-to-text,
|
|
32
|
+
and text-to-speech. No constant background listening/wake-word overhead.
|
|
33
|
+
|
|
34
|
+
## Installation & Setup
|
|
35
|
+
|
|
36
|
+
### Option 1: Global Installation (Recommended)
|
|
37
|
+
|
|
38
|
+
Install Nixi globally on your system using [pipx](https://github.com/pypa/pipx):
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
pipx install nixii
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This will automatically expose the commands globally. Run any command once to automatically initialize your configuration directory:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
nixi-server
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Now, configure your API keys by editing `~/.config/nixi/.env`:
|
|
51
|
+
|
|
52
|
+
```dotenv
|
|
53
|
+
GOOGLE_CLOUD_API_KEY=your-vertex-express-mode-key
|
|
54
|
+
SARVAM_API_KEY=your-sarvam-key
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Option 2: Local Development Setup
|
|
58
|
+
|
|
59
|
+
If you'd like to run or develop Nixi locally, install [uv](https://docs.astral.sh/uv/) and sync dependencies:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
uv sync
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Run any command once (e.g. `uv run nixi-server`) to auto-generate your config, then add your API keys to `~/.config/nixi/.env`.
|
|
66
|
+
|
|
67
|
+
## Run
|
|
68
|
+
|
|
69
|
+
> **Note:** If you are using the local development setup, prefix all of the following commands with `uv run ` (e.g., `uv run nixi-server`).
|
|
70
|
+
|
|
71
|
+
First, discover your desktop environment (keybinds, scripts, wallpapers):
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
nixi-server --discover
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then start the server:
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
nixi-server
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Next, start the voice daemon (which runs as a background process listening for manual trigger events):
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
nixi-voice
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
To interact with Nixi, trigger the recording. You can bind keys or desktop shortcuts to run these commands:
|
|
90
|
+
|
|
91
|
+
- **Trigger Recording (Toggle)**: Start or stop recording your query.
|
|
92
|
+
```sh
|
|
93
|
+
nixi-voice trigger
|
|
94
|
+
```
|
|
95
|
+
- **New Session**: Start a fresh conversation session and trigger recording.
|
|
96
|
+
```sh
|
|
97
|
+
nixi-voice new
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Run the command once to start recording, speak your request, and run it again to stop and process the audio!
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
Nixi resolves its config in this order:
|
|
105
|
+
|
|
106
|
+
1. `--config <path>` CLI flag
|
|
107
|
+
2. `$NIXI_CONFIG` environment variable
|
|
108
|
+
3. `~/.config/nixi/nixi.toml` (user config)
|
|
109
|
+
4. `example_config/nixi.toml` (repo default)
|
|
110
|
+
5. Code defaults (no file needed)
|
|
111
|
+
|
|
112
|
+
The system prompt lives in code. Set `agent_name` in your TOML to change the
|
|
113
|
+
persona — the prompt template uses it automatically.
|
|
114
|
+
|
|
115
|
+
Edit your TOML to configure audio thresholds, STT, TTS, model
|
|
116
|
+
settings, vision, and local actions.
|
nixii-0.1.0/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Nixi
|
|
2
|
+
|
|
3
|
+
A desktop voice assistant for Linux. Trigger Nixi, ask a question or give a
|
|
4
|
+
command, and it handles it — change the volume, set a wallpaper, take a
|
|
5
|
+
screenshot, control media, or answer general queries using Gemini.
|
|
6
|
+
|
|
7
|
+
## What It Does
|
|
8
|
+
|
|
9
|
+
- **System control** — volume, brightness, mute, media playback, screenshots,
|
|
10
|
+
wallpaper changes, app launcher.
|
|
11
|
+
- **General queries** — ask anything. Nixi uses Gemini and can search the web
|
|
12
|
+
for live information like weather, news, and prices.
|
|
13
|
+
- **Vision** — say "open your eyes", "where should I click", or "why is this
|
|
14
|
+
red" and Nixi asks permission to screenshot your screen, then describes what
|
|
15
|
+
it sees. Approval is via desktop notification with a configurable timeout.
|
|
16
|
+
- **Voice interaction** — manual trigger-to-record mechanism, speech-to-text,
|
|
17
|
+
and text-to-speech. No constant background listening/wake-word overhead.
|
|
18
|
+
|
|
19
|
+
## Installation & Setup
|
|
20
|
+
|
|
21
|
+
### Option 1: Global Installation (Recommended)
|
|
22
|
+
|
|
23
|
+
Install Nixi globally on your system using [pipx](https://github.com/pypa/pipx):
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pipx install nixii
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This will automatically expose the commands globally. Run any command once to automatically initialize your configuration directory:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
nixi-server
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Now, configure your API keys by editing `~/.config/nixi/.env`:
|
|
36
|
+
|
|
37
|
+
```dotenv
|
|
38
|
+
GOOGLE_CLOUD_API_KEY=your-vertex-express-mode-key
|
|
39
|
+
SARVAM_API_KEY=your-sarvam-key
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Option 2: Local Development Setup
|
|
43
|
+
|
|
44
|
+
If you'd like to run or develop Nixi locally, install [uv](https://docs.astral.sh/uv/) and sync dependencies:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
uv sync
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Run any command once (e.g. `uv run nixi-server`) to auto-generate your config, then add your API keys to `~/.config/nixi/.env`.
|
|
51
|
+
|
|
52
|
+
## Run
|
|
53
|
+
|
|
54
|
+
> **Note:** If you are using the local development setup, prefix all of the following commands with `uv run ` (e.g., `uv run nixi-server`).
|
|
55
|
+
|
|
56
|
+
First, discover your desktop environment (keybinds, scripts, wallpapers):
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
nixi-server --discover
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then start the server:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
nixi-server
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Next, start the voice daemon (which runs as a background process listening for manual trigger events):
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
nixi-voice
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
To interact with Nixi, trigger the recording. You can bind keys or desktop shortcuts to run these commands:
|
|
75
|
+
|
|
76
|
+
- **Trigger Recording (Toggle)**: Start or stop recording your query.
|
|
77
|
+
```sh
|
|
78
|
+
nixi-voice trigger
|
|
79
|
+
```
|
|
80
|
+
- **New Session**: Start a fresh conversation session and trigger recording.
|
|
81
|
+
```sh
|
|
82
|
+
nixi-voice new
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Run the command once to start recording, speak your request, and run it again to stop and process the audio!
|
|
86
|
+
|
|
87
|
+
## Configuration
|
|
88
|
+
|
|
89
|
+
Nixi resolves its config in this order:
|
|
90
|
+
|
|
91
|
+
1. `--config <path>` CLI flag
|
|
92
|
+
2. `$NIXI_CONFIG` environment variable
|
|
93
|
+
3. `~/.config/nixi/nixi.toml` (user config)
|
|
94
|
+
4. `example_config/nixi.toml` (repo default)
|
|
95
|
+
5. Code defaults (no file needed)
|
|
96
|
+
|
|
97
|
+
The system prompt lives in code. Set `agent_name` in your TOML to change the
|
|
98
|
+
persona — the prompt template uses it automatically.
|
|
99
|
+
|
|
100
|
+
Edit your TOML to configure audio thresholds, STT, TTS, model
|
|
101
|
+
settings, vision, and local actions.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Nixi application packages."""
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg fill="none" height="100%" width="100%" viewBox="0 0 650 650" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><g id="i0"><g transform="translate(195,347)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="195 347; 195 347; 195 358; 195 347; 195 347; 195 358; 195 347; 195 347" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1)"><animateTransform repeatCount="indefinite" type="scale" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1" keyTimes="0; 0.125; 0.208333; 0.3125; 0.375; 0.416666; 0.6875; 0.770834; 0.875; 0.9375; 0.979167; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="translate(130,-22)"><g id="i1" transform="matrix(1,0,0,1,-135.828,16)"><ellipse ry="195.813" rx="127.813" cy="0" cx="0" fill="#fafafa" /></g></g></g></g></g><g id="i0"><g transform="translate(195,347)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="195 347; 195 347; 195 358; 195 347; 195 347; 195 358; 195 347; 195 347" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1)"><animateTransform repeatCount="indefinite" type="scale" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1" keyTimes="0; 0.125; 0.208333; 0.3125; 0.375; 0.416666; 0.6875; 0.770834; 0.875; 0.9375; 0.979167; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="translate(130,-22)"><g id="i2"><g transform="translate(-135,20)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="-135 20; -135 20; -135 69.176; -186 20; -186 20; -135 69.176; -135 20; -135 20" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1) translate(186,-20)"><g><g transform="translate(-186.82,16)"><g id="i3" transform="rotate(0)"><animateTransform repeatCount="indefinite" type="rotate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="0; 0; -22; 0; 0" keyTimes="0; 0.25; 0.333333; 0.375; 1" keySplines="0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1" fill="freeze" /><ellipse ry="80.813" rx="60.813" cy="0" cx="0" fill="#1d1d1d"><animate repeatCount="indefinite" attributeName="rx" dur="2s" begin="0s" fill="freeze" values="60.813; 60.813; 58.813; 60.813; 60.813" keyTimes="0; 0.25; 0.333333; 0.375; 1" keySplines="0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1" calcMode="spline" /><animate repeatCount="indefinite" attributeName="ry" dur="2s" begin="0s" fill="freeze" values="80.813; 80.813; 87.313; 80.813; 80.813" keyTimes="0; 0.25; 0.333333; 0.375; 1" keySplines="0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1" calcMode="spline" /></ellipse></g></g></g><g id="i4" transform="matrix(0.848,0.53,-0.53,0.848,-150.629,-32.18)"><ellipse ry="42.313" rx="31.84" cy="0" cx="0" fill="#fafafa" /></g></g></g></g></g></g></g></g><g id="i0"><g transform="translate(195,347)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="195 347; 195 347; 195 358; 195 347; 195 347; 195 358; 195 347; 195 347" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1)"><animateTransform repeatCount="indefinite" type="scale" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1" keyTimes="0; 0.125; 0.208333; 0.3125; 0.375; 0.416666; 0.6875; 0.770834; 0.875; 0.9375; 0.979167; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="translate(130,-22)"><g transform="matrix(1,0,0,1,0,0)" visibility="hidden" id="i5"><animate repeatCount="indefinite" begin="0s" calcMode="discrete" dur="2s" values="hidden; visible; hidden; hidden" keyTimes="0; 0.146; 0.375; 1" attributeName="visibility" /><g id="i6" transform="matrix(1,0,0,1,-135.828,16)"><path d="M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z" stroke-width="20" stroke="#1d1d1d" fill="#fafafa"><animate repeatCount="indefinite" attributeName="d" dur="2s" begin="0s" calcMode="spline" values="M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z; M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z; M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-64.112,0.5,-0.173,0.377C64.295,0.253,128.825,0,127.813,0Z; M127.812,0C127.812,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.824,0,127.812,0Z; M127.812,0C127.812,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.824,0,127.812,0Z" keyTimes="0; 0.125; 0.3125; 0.375; 1" keySplines="0 0 1 1; 0.065 0 0 1; 0.203 0 0 1; 0 0 1 1" fill="freeze" /></path></g><g id="i7" transform="matrix(1,0,0,1,-135.828,16)"><path d="M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z" stroke-width="20" stroke="#1d1d1d" fill="#fafafa"><animate repeatCount="indefinite" attributeName="d" dur="2s" begin="0s" calcMode="spline" values="M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,75.122,50,0.588,51.625C-69.901,53.161,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,75.122,50,0.588,51.625C-69.901,53.161,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z" keyTimes="0; 0.125; 0.25; 0.3125; 0.395833; 1" keySplines="0 0 1 1; 0.065 0 0 1; 0.167 0 0.833 1; 0.203 0 0 1; 0 0 1 1" fill="freeze" /></path></g></g></g></g></g></g><g id="i0"><g transform="translate(195,347)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="195 347; 195 347; 195 358; 195 347; 195 347; 195 358; 195 347; 195 347" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1)"><animateTransform repeatCount="indefinite" type="scale" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1" keyTimes="0; 0.125; 0.208333; 0.3125; 0.375; 0.416666; 0.6875; 0.770834; 0.875; 0.9375; 0.979167; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="translate(130,-22)"><g transform="matrix(1,0,0,1,0,0)" visibility="hidden" id="i8"><animate repeatCount="indefinite" begin="0s" calcMode="discrete" dur="2s" values="hidden; visible; hidden; hidden" keyTimes="0; 0.708; 0.938; 1" attributeName="visibility" /><g id="i9" transform="matrix(1,0,0,1,-135.828,16)"><path d="M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z" stroke-width="20" stroke="#1d1d1d" fill="#fafafa"><animate repeatCount="indefinite" attributeName="d" dur="2s" begin="0s" calcMode="spline" values="M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z; M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z; M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-64.112,0.5,-0.173,0.377C64.295,0.253,128.825,0,127.813,0Z; M127.812,0C127.812,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.824,0,127.812,0Z; M127.812,0C127.812,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.824,0,127.812,0Z" keyTimes="0; 0.6875; 0.875; 0.9375; 1" keySplines="0 0 1 1; 0.065 0 0 1; 0.203 0 0 1; 0 0 1 1" fill="freeze" /></path></g><g id="i10" transform="matrix(1,0,0,1,-135.828,16)"><path d="M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z" stroke-width="20" stroke="#1d1d1d" fill="#fafafa"><animate repeatCount="indefinite" attributeName="d" dur="2s" begin="0s" calcMode="spline" values="M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,75.122,50,0.588,51.625C-69.901,53.161,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,75.122,50,0.588,51.625C-69.901,53.161,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z" keyTimes="0; 0.6875; 0.8125; 0.875; 0.958333; 1" keySplines="0 0 1 1; 0.065 0 0 1; 0.167 0 0.833 1; 0.203 0 0 1; 0 0 1 1" fill="freeze" /></path></g></g></g></g></g></g><g id="i0"><g transform="translate(195,347)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="195 347; 195 347; 195 358; 195 347; 195 347; 195 358; 195 347; 195 347" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1)"><animateTransform repeatCount="indefinite" type="scale" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1" keyTimes="0; 0.125; 0.208333; 0.3125; 0.375; 0.416666; 0.6875; 0.770834; 0.875; 0.9375; 0.979167; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="translate(130,-22)"><g transform="matrix(1,0,0,1,0,0)" id="i11"><g id="i12" transform="matrix(1,0,0,1,-135.828,16)"><ellipse ry="195.813" rx="127.813" cy="0" cx="0" stroke-width="20" stroke="#1d1d1d" /></g></g></g></g></g></g><g id="i13"><g transform="translate(470,347)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="470 347; 470 347; 470 358; 470 347; 470 347; 470 358; 470 347; 470 347" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1)"><animateTransform repeatCount="indefinite" type="scale" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1" keyTimes="0; 0.125; 0.208333; 0.3125; 0.375; 0.416666; 0.6875; 0.770834; 0.875; 0.9375; 0.979167; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="translate(130,-22)"><g id="i14" transform="matrix(1,0,0,1,-135.828,16)"><ellipse ry="195.813" rx="127.813" cy="0" cx="0" fill="#fafafa" /></g></g></g></g></g><g id="i13"><g transform="translate(470,347)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="470 347; 470 347; 470 358; 470 347; 470 347; 470 358; 470 347; 470 347" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1)"><animateTransform repeatCount="indefinite" type="scale" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1" keyTimes="0; 0.125; 0.208333; 0.3125; 0.375; 0.416666; 0.6875; 0.770834; 0.875; 0.9375; 0.979167; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="translate(130,-22)"><g id="i15"><g transform="translate(-135,20)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="-135 20; -135 20; -135 69.176; -186 20; -186 20; -135 69.176; -135 20; -135 20" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1) translate(186,-20)"><g><g transform="translate(-186.82,16)"><g id="i16" transform="rotate(0)"><animateTransform repeatCount="indefinite" type="rotate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="0; 0; -22; 0; 0" keyTimes="0; 0.25; 0.333333; 0.375; 1" keySplines="0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1" fill="freeze" /><ellipse ry="80.813" rx="60.813" cy="0" cx="0" fill="#1d1d1d"><animate repeatCount="indefinite" attributeName="rx" dur="2s" begin="0s" fill="freeze" values="60.813; 60.813; 58.813; 60.813; 60.813" keyTimes="0; 0.25; 0.333333; 0.375; 1" keySplines="0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1" calcMode="spline" /><animate repeatCount="indefinite" attributeName="ry" dur="2s" begin="0s" fill="freeze" values="80.813; 80.813; 87.313; 80.813; 80.813" keyTimes="0; 0.25; 0.333333; 0.375; 1" keySplines="0 0 1 1; 0 0 1 1; 0 0 1 1; 0 0 1 1" calcMode="spline" /></ellipse></g></g></g><g id="i17" transform="matrix(0.848,0.53,-0.53,0.848,-150.629,-32.18)"><ellipse ry="42.313" rx="31.84" cy="0" cx="0" fill="#fafafa" /></g></g></g></g></g></g></g></g><g id="i13"><g transform="translate(470,347)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="470 347; 470 347; 470 358; 470 347; 470 347; 470 358; 470 347; 470 347" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1)"><animateTransform repeatCount="indefinite" type="scale" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1" keyTimes="0; 0.125; 0.208333; 0.3125; 0.375; 0.416666; 0.6875; 0.770834; 0.875; 0.9375; 0.979167; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="translate(130,-22)"><g transform="matrix(1,0,0,1,0,0)" visibility="hidden" id="i18"><animate repeatCount="indefinite" begin="0s" calcMode="discrete" dur="2s" values="hidden; visible; hidden; hidden" keyTimes="0; 0.146; 0.375; 1" attributeName="visibility" /><g id="i19" transform="matrix(1,0,0,1,-135.828,16)"><path d="M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z" stroke-width="20" stroke="#1d1d1d" fill="#fafafa"><animate repeatCount="indefinite" attributeName="d" dur="2s" begin="0s" calcMode="spline" values="M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z; M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z; M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-64.112,0.5,-0.173,0.377C64.295,0.253,128.825,0,127.813,0Z; M127.812,0C127.812,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.824,0,127.812,0Z; M127.812,0C127.812,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.824,0,127.812,0Z" keyTimes="0; 0.125; 0.3125; 0.375; 1" keySplines="0 0 1 1; 0.065 0 0 1; 0.203 0 0 1; 0 0 1 1" fill="freeze" /></path></g><g id="i20" transform="matrix(1,0,0,1,-135.828,16)"><path d="M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z" stroke-width="20" stroke="#1d1d1d" fill="#fafafa"><animate repeatCount="indefinite" attributeName="d" dur="2s" begin="0s" calcMode="spline" values="M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,75.122,50,0.588,51.625C-69.901,53.161,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,75.122,50,0.588,51.625C-69.901,53.161,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z" keyTimes="0; 0.125; 0.25; 0.3125; 0.395833; 1" keySplines="0 0 1 1; 0.065 0 0 1; 0.167 0 0.833 1; 0.203 0 0 1; 0 0 1 1" fill="freeze" /></path></g></g></g></g></g></g><g id="i13"><g transform="translate(470,347)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="470 347; 470 347; 470 358; 470 347; 470 347; 470 358; 470 347; 470 347" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1)"><animateTransform repeatCount="indefinite" type="scale" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1" keyTimes="0; 0.125; 0.208333; 0.3125; 0.375; 0.416666; 0.6875; 0.770834; 0.875; 0.9375; 0.979167; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="translate(130,-22)"><g transform="matrix(1,0,0,1,0,0)" visibility="hidden" id="i21"><animate repeatCount="indefinite" begin="0s" calcMode="discrete" dur="2s" values="hidden; visible; hidden; hidden" keyTimes="0; 0.708; 0.938; 1" attributeName="visibility" /><g id="i22" transform="matrix(1,0,0,1,-135.828,16)"><path d="M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z" stroke-width="20" stroke="#1d1d1d" fill="#fafafa"><animate repeatCount="indefinite" attributeName="d" dur="2s" begin="0s" calcMode="spline" values="M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z; M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.825,0,127.813,0Z; M127.813,0C127.813,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-64.112,0.5,-0.173,0.377C64.295,0.253,128.825,0,127.813,0Z; M127.812,0C127.812,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.824,0,127.812,0Z; M127.812,0C127.812,108.144,70.589,195.812,0,195.812C-70.589,195.812,-127.812,108.144,-127.812,0C-127.991,0.498,-123.172,192,-0.346,195.377C120.79,198.707,128.824,0,127.812,0Z" keyTimes="0; 0.6875; 0.875; 0.9375; 1" keySplines="0 0 1 1; 0.065 0 0 1; 0.203 0 0 1; 0 0 1 1" fill="freeze" /></path></g><g id="i23" transform="matrix(1,0,0,1,-135.828,16)"><path d="M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z" stroke-width="20" stroke="#1d1d1d" fill="#fafafa"><animate repeatCount="indefinite" attributeName="d" dur="2s" begin="0s" calcMode="spline" values="M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,75.122,50,0.588,51.625C-69.901,53.161,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,75.122,50,0.588,51.625C-69.901,53.161,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z; M0,-195.812C70.589,-195.812,127.812,-108.144,127.812,0C128.319,-0.499,118.829,-198.691,0.352,-196.375C-121.172,-194,-127.491,0,-127.812,0C-127.812,-108.144,-70.589,-195.812,0,-195.812Z" keyTimes="0; 0.6875; 0.8125; 0.875; 0.958333; 1" keySplines="0 0 1 1; 0.065 0 0 1; 0.167 0 0.833 1; 0.203 0 0 1; 0 0 1 1" fill="freeze" /></path></g></g></g></g></g></g><g id="i13"><g transform="translate(470,347)"><animateTransform repeatCount="indefinite" type="translate" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="470 347; 470 347; 470 358; 470 347; 470 347; 470 358; 470 347; 470 347" keyTimes="0; 0.125; 0.25; 0.375; 0.6875; 0.8125; 0.9375; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="scale(1,1)"><animateTransform repeatCount="indefinite" type="scale" attributeName="transform" dur="2s" begin="0s" calcMode="spline" values="1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1; 1.06 0.93; 1 1; 0.98 1.03; 1 1; 1 1" keyTimes="0; 0.125; 0.208333; 0.3125; 0.375; 0.416666; 0.6875; 0.770834; 0.875; 0.9375; 0.979167; 1" keySplines="0 0 1 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.667 1; 0.333 0 0.833 1; 0.167 0 0.667 1; 0 0 1 1" fill="freeze" /><g transform="translate(130,-22)"><g transform="matrix(1,0,0,1,0,0)" id="i24"><g id="i25" transform="matrix(1,0,0,1,-135.828,16)"><ellipse ry="195.813" rx="127.813" cy="0" cx="0" stroke-width="20" stroke="#1d1d1d" /></g></g></g></g></g></g></svg>
|
|
Binary file
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Public configuration API shared by every Nixi component.
|
|
2
|
+
|
|
3
|
+
The implementation remains import-compatible with older ``app.server.config``
|
|
4
|
+
imports while new code can depend on this component-neutral module.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from app.server.config import (
|
|
8
|
+
DEFAULT_CONFIG_PATH,
|
|
9
|
+
DEFAULT_SYSTEM_PROMPT,
|
|
10
|
+
USER_CONFIG_PATH,
|
|
11
|
+
ActionConfig,
|
|
12
|
+
LLMConfig,
|
|
13
|
+
NixiConfig,
|
|
14
|
+
ServerConfig,
|
|
15
|
+
STTConfig,
|
|
16
|
+
TTSConfig,
|
|
17
|
+
VisionConfig,
|
|
18
|
+
VoiceConfig,
|
|
19
|
+
default_config,
|
|
20
|
+
load_config,
|
|
21
|
+
resolve_config_path,
|
|
22
|
+
ensure_config_exists,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"DEFAULT_CONFIG_PATH",
|
|
27
|
+
"DEFAULT_SYSTEM_PROMPT",
|
|
28
|
+
"USER_CONFIG_PATH",
|
|
29
|
+
"ActionConfig",
|
|
30
|
+
"LLMConfig",
|
|
31
|
+
"NixiConfig",
|
|
32
|
+
"ServerConfig",
|
|
33
|
+
"STTConfig",
|
|
34
|
+
"TTSConfig",
|
|
35
|
+
"VisionConfig",
|
|
36
|
+
"VoiceConfig",
|
|
37
|
+
"default_config",
|
|
38
|
+
"load_config",
|
|
39
|
+
"resolve_config_path",
|
|
40
|
+
"ensure_config_exists",
|
|
41
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Load local secrets for interactive and systemd launches."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def load_environment() -> None:
|
|
12
|
+
"""Load simple KEY=VALUE secrets without overriding the process environment."""
|
|
13
|
+
from app.config import ensure_config_exists, USER_CONFIG_PATH
|
|
14
|
+
ensure_config_exists()
|
|
15
|
+
|
|
16
|
+
# Helper to parse and set non-empty environment variables from a file
|
|
17
|
+
def load_from_file(path: Path) -> None:
|
|
18
|
+
if not path.exists():
|
|
19
|
+
return
|
|
20
|
+
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
|
21
|
+
line = raw_line.strip()
|
|
22
|
+
if not line or line.startswith("#") or "=" not in line:
|
|
23
|
+
continue
|
|
24
|
+
name, value = line.split("=", 1)
|
|
25
|
+
name = name.strip()
|
|
26
|
+
value = value.strip()
|
|
27
|
+
if len(value) >= 2 and value[0] == value[-1] and value[0] in {'"', "'"}:
|
|
28
|
+
value = value[1:-1]
|
|
29
|
+
if name and value:
|
|
30
|
+
os.environ.setdefault(name, value)
|
|
31
|
+
|
|
32
|
+
# 1. Load from ~/.config/nixi/.env first (user overrides)
|
|
33
|
+
load_from_file(USER_CONFIG_PATH.parent / ".env")
|
|
34
|
+
|
|
35
|
+
# 2. Fall back to REPO_ROOT / ".env" (local development)
|
|
36
|
+
if REPO_ROOT != USER_CONFIG_PATH.parent:
|
|
37
|
+
load_from_file(REPO_ROOT / ".env")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Small structured event logger shared by Nixi processes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def log_event(component: str, event: str, request_id: str, **details: Any) -> None:
|
|
11
|
+
payload = {
|
|
12
|
+
"timestamp": datetime.now().astimezone().isoformat(timespec="milliseconds"),
|
|
13
|
+
"component": component,
|
|
14
|
+
"request_id": request_id,
|
|
15
|
+
"event": event,
|
|
16
|
+
**details,
|
|
17
|
+
}
|
|
18
|
+
print(json.dumps(payload, ensure_ascii=False, default=str), flush=True)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""GTK popup for Nixi."""
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Launch the GTK popup with the system interpreter that owns its GI bindings."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
SYSTEM_PYTHON = "/usr/bin/python3"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main() -> None:
|
|
13
|
+
popup = Path(__file__).with_name("main.py")
|
|
14
|
+
os.execv(SYSTEM_PYTHON, [SYSTEM_PYTHON, str(popup)])
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if __name__ == "__main__":
|
|
18
|
+
main()
|