clipsy 1.5.0__py3-none-any.whl → 1.7.0__py3-none-any.whl
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.
- clipsy/__init__.py +1 -1
- clipsy/__main__.py +165 -1
- {clipsy-1.5.0.dist-info → clipsy-1.7.0.dist-info}/METADATA +18 -17
- {clipsy-1.5.0.dist-info → clipsy-1.7.0.dist-info}/RECORD +8 -8
- {clipsy-1.5.0.dist-info → clipsy-1.7.0.dist-info}/WHEEL +0 -0
- {clipsy-1.5.0.dist-info → clipsy-1.7.0.dist-info}/entry_points.txt +0 -0
- {clipsy-1.5.0.dist-info → clipsy-1.7.0.dist-info}/licenses/LICENSE +0 -0
- {clipsy-1.5.0.dist-info → clipsy-1.7.0.dist-info}/top_level.txt +0 -0
clipsy/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "1.
|
|
1
|
+
__version__ = "1.7.0"
|
|
2
2
|
__app_name__ = "Clipsy"
|
clipsy/__main__.py
CHANGED
|
@@ -1,11 +1,136 @@
|
|
|
1
|
+
import argparse
|
|
1
2
|
import logging
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
2
5
|
import sys
|
|
6
|
+
from pathlib import Path
|
|
3
7
|
|
|
4
8
|
from clipsy.config import LOG_PATH
|
|
5
9
|
from clipsy.utils import ensure_dirs
|
|
6
10
|
|
|
11
|
+
PLIST_NAME = "com.clipsy.app.plist"
|
|
12
|
+
LAUNCHAGENT_DIR = Path.home() / "Library" / "LaunchAgents"
|
|
13
|
+
PLIST_PATH = LAUNCHAGENT_DIR / PLIST_NAME
|
|
7
14
|
|
|
8
|
-
|
|
15
|
+
|
|
16
|
+
def get_clipsy_path() -> str:
|
|
17
|
+
"""Get the path to the clipsy executable."""
|
|
18
|
+
import shutil
|
|
19
|
+
|
|
20
|
+
# Check if we're running from an installed location
|
|
21
|
+
clipsy_path = shutil.which("clipsy")
|
|
22
|
+
if clipsy_path:
|
|
23
|
+
return clipsy_path
|
|
24
|
+
# Fallback to current Python module
|
|
25
|
+
return f"{sys.executable} -m clipsy"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def create_plist(clipsy_path: str) -> str:
|
|
29
|
+
"""Generate the LaunchAgent plist content."""
|
|
30
|
+
data_dir = Path.home() / ".local" / "share" / "clipsy"
|
|
31
|
+
return f"""<?xml version="1.0" encoding="UTF-8"?>
|
|
32
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
33
|
+
<plist version="1.0">
|
|
34
|
+
<dict>
|
|
35
|
+
<key>Label</key>
|
|
36
|
+
<string>com.clipsy.app</string>
|
|
37
|
+
<key>ProgramArguments</key>
|
|
38
|
+
<array>
|
|
39
|
+
<string>{clipsy_path}</string>
|
|
40
|
+
</array>
|
|
41
|
+
<key>RunAtLoad</key>
|
|
42
|
+
<true/>
|
|
43
|
+
<key>KeepAlive</key>
|
|
44
|
+
<true/>
|
|
45
|
+
<key>StandardOutPath</key>
|
|
46
|
+
<string>{data_dir}/clipsy.log</string>
|
|
47
|
+
<key>StandardErrorPath</key>
|
|
48
|
+
<string>{data_dir}/clipsy.log</string>
|
|
49
|
+
</dict>
|
|
50
|
+
</plist>
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def install_launchagent() -> int:
|
|
55
|
+
"""Install and start the LaunchAgent."""
|
|
56
|
+
ensure_dirs()
|
|
57
|
+
|
|
58
|
+
clipsy_path = get_clipsy_path()
|
|
59
|
+
print(f"Installing LaunchAgent for: {clipsy_path}")
|
|
60
|
+
|
|
61
|
+
# Create LaunchAgents directory if needed
|
|
62
|
+
LAUNCHAGENT_DIR.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
|
|
64
|
+
# Unload existing if present
|
|
65
|
+
if PLIST_PATH.exists():
|
|
66
|
+
subprocess.run(
|
|
67
|
+
["launchctl", "unload", str(PLIST_PATH)],
|
|
68
|
+
capture_output=True,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Write plist
|
|
72
|
+
PLIST_PATH.write_text(create_plist(clipsy_path))
|
|
73
|
+
print(f"Created: {PLIST_PATH}")
|
|
74
|
+
|
|
75
|
+
# Load the agent
|
|
76
|
+
result = subprocess.run(
|
|
77
|
+
["launchctl", "load", str(PLIST_PATH)],
|
|
78
|
+
capture_output=True,
|
|
79
|
+
text=True,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if result.returncode == 0:
|
|
83
|
+
print("Clipsy is now running in the background.")
|
|
84
|
+
print("It will start automatically on login.")
|
|
85
|
+
return 0
|
|
86
|
+
else:
|
|
87
|
+
print(f"Failed to load LaunchAgent: {result.stderr}")
|
|
88
|
+
return 1
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def uninstall_launchagent() -> int:
|
|
92
|
+
"""Stop and remove the LaunchAgent."""
|
|
93
|
+
if not PLIST_PATH.exists():
|
|
94
|
+
print("LaunchAgent not installed.")
|
|
95
|
+
return 0
|
|
96
|
+
|
|
97
|
+
# Unload
|
|
98
|
+
subprocess.run(
|
|
99
|
+
["launchctl", "unload", str(PLIST_PATH)],
|
|
100
|
+
capture_output=True,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Remove plist
|
|
104
|
+
PLIST_PATH.unlink()
|
|
105
|
+
print("LaunchAgent uninstalled.")
|
|
106
|
+
print("Clipsy will no longer start on login.")
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def check_status() -> int:
|
|
111
|
+
"""Check if Clipsy is running."""
|
|
112
|
+
result = subprocess.run(
|
|
113
|
+
["launchctl", "list", "com.clipsy.app"],
|
|
114
|
+
capture_output=True,
|
|
115
|
+
text=True,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if result.returncode == 0:
|
|
119
|
+
print("Clipsy is running.")
|
|
120
|
+
if PLIST_PATH.exists():
|
|
121
|
+
print(f"LaunchAgent: {PLIST_PATH}")
|
|
122
|
+
return 0
|
|
123
|
+
else:
|
|
124
|
+
print("Clipsy is not running.")
|
|
125
|
+
if PLIST_PATH.exists():
|
|
126
|
+
print(f"LaunchAgent installed but not loaded: {PLIST_PATH}")
|
|
127
|
+
else:
|
|
128
|
+
print("LaunchAgent not installed. Run: clipsy install")
|
|
129
|
+
return 1
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def run_app():
|
|
133
|
+
"""Run the Clipsy application."""
|
|
9
134
|
ensure_dirs()
|
|
10
135
|
|
|
11
136
|
logging.basicConfig(
|
|
@@ -18,9 +143,48 @@ def main():
|
|
|
18
143
|
)
|
|
19
144
|
|
|
20
145
|
from clipsy.app import ClipsyApp
|
|
146
|
+
|
|
21
147
|
app = ClipsyApp()
|
|
22
148
|
app.run()
|
|
23
149
|
|
|
24
150
|
|
|
151
|
+
def main():
|
|
152
|
+
parser = argparse.ArgumentParser(
|
|
153
|
+
description="Clipsy - Clipboard history manager for macOS",
|
|
154
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
155
|
+
epilog="""
|
|
156
|
+
Commands:
|
|
157
|
+
(none) Install and run as background service (default)
|
|
158
|
+
uninstall Remove LaunchAgent
|
|
159
|
+
status Check if Clipsy is running
|
|
160
|
+
run Run in foreground (for debugging)
|
|
161
|
+
|
|
162
|
+
Examples:
|
|
163
|
+
clipsy # Install and start as background service
|
|
164
|
+
clipsy status # Check if running
|
|
165
|
+
clipsy uninstall # Stop and remove from login items
|
|
166
|
+
clipsy run # Run in foreground (debugging)
|
|
167
|
+
""",
|
|
168
|
+
)
|
|
169
|
+
parser.add_argument(
|
|
170
|
+
"command",
|
|
171
|
+
nargs="?",
|
|
172
|
+
choices=["uninstall", "status", "run"],
|
|
173
|
+
help="Command to run",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
args = parser.parse_args()
|
|
177
|
+
|
|
178
|
+
if args.command == "uninstall":
|
|
179
|
+
sys.exit(uninstall_launchagent())
|
|
180
|
+
elif args.command == "status":
|
|
181
|
+
sys.exit(check_status())
|
|
182
|
+
elif args.command == "run":
|
|
183
|
+
run_app()
|
|
184
|
+
else:
|
|
185
|
+
# Default: install and start as background service
|
|
186
|
+
sys.exit(install_launchagent())
|
|
187
|
+
|
|
188
|
+
|
|
25
189
|
if __name__ == "__main__":
|
|
26
190
|
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clipsy
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
4
4
|
Summary: Lightweight clipboard history manager for macOS
|
|
5
5
|
Author-email: Brendan Conrad <brendan.conrad@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -59,10 +59,19 @@ A lightweight clipboard history manager for macOS. Runs as a menu bar icon — n
|
|
|
59
59
|
|
|
60
60
|
## Installation
|
|
61
61
|
|
|
62
|
-
### Via
|
|
62
|
+
### Via pipx (recommended)
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
brew install pipx
|
|
66
|
+
pipx install clipsy
|
|
67
|
+
clipsy
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Via pip
|
|
63
71
|
|
|
64
72
|
```bash
|
|
65
73
|
pip install clipsy
|
|
74
|
+
clipsy
|
|
66
75
|
```
|
|
67
76
|
|
|
68
77
|
### From source
|
|
@@ -72,14 +81,12 @@ git clone https://github.com/brencon/clipsy.git
|
|
|
72
81
|
cd clipsy
|
|
73
82
|
python3 -m venv .venv
|
|
74
83
|
.venv/bin/pip install -e .
|
|
84
|
+
.venv/bin/clipsy
|
|
75
85
|
```
|
|
76
86
|
|
|
77
87
|
## Usage
|
|
78
88
|
|
|
79
|
-
|
|
80
|
-
# Run clipsy (a scissors icon appears in your menu bar)
|
|
81
|
-
clipsy
|
|
82
|
-
```
|
|
89
|
+
After running `clipsy`, the app installs as a background service and starts automatically on login. A scissors icon (✂️) appears in your menu bar.
|
|
83
90
|
|
|
84
91
|
Then just use your Mac normally. Every time you copy something, it shows up in the Clipsy menu:
|
|
85
92
|
|
|
@@ -102,19 +109,13 @@ Then just use your Mac normally. Every time you copy something, it shows up in t
|
|
|
102
109
|
└── Quit Clipsy
|
|
103
110
|
```
|
|
104
111
|
|
|
105
|
-
##
|
|
106
|
-
|
|
107
|
-
Run clipsy automatically when you log in — no terminal needed:
|
|
112
|
+
## Commands
|
|
108
113
|
|
|
109
114
|
```bash
|
|
110
|
-
# Install as
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
#
|
|
114
|
-
scripts/install_launchagent.sh status
|
|
115
|
-
|
|
116
|
-
# Remove auto-start
|
|
117
|
-
scripts/install_launchagent.sh uninstall
|
|
115
|
+
clipsy # Install and start as background service (default)
|
|
116
|
+
clipsy status # Check if running
|
|
117
|
+
clipsy uninstall # Remove from login items
|
|
118
|
+
clipsy run # Run in foreground (for debugging)
|
|
118
119
|
```
|
|
119
120
|
|
|
120
121
|
## Data Storage
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
clipsy/__init__.py,sha256=
|
|
2
|
-
clipsy/__main__.py,sha256=
|
|
1
|
+
clipsy/__init__.py,sha256=5B_3MB6DYDXcJfVxDSzafJY1_-IM-eLcNadIWCMyJTc,46
|
|
2
|
+
clipsy/__main__.py,sha256=Ipb_HlLKeG5TPKdaAd4FALgrvCX9PPw9TuMF1BH0AAQ,4962
|
|
3
3
|
clipsy/app.py,sha256=_IKuZgA-DvAE3KDsDN00S0aaMIxwBpiv_P1fpzwQFqU,7299
|
|
4
4
|
clipsy/config.py,sha256=E9NCsGMeT2H0YXYCq5xYwbd27VkksV4S9txZSMX0RmE,672
|
|
5
5
|
clipsy/models.py,sha256=i3KjD4Dcp7y_gTXkIBr2xZxFIIC6kHaMWmv6cSIDABM,563
|
|
@@ -7,9 +7,9 @@ clipsy/monitor.py,sha256=UUKZToYcZ5pzu7LfiuJ_nnE4AA-d3uLW-WdQU_XrDME,6288
|
|
|
7
7
|
clipsy/redact.py,sha256=EU2nq8oEDYLZ_I_9tX2bXACIF971a0V_ApKdvpN4VGY,7284
|
|
8
8
|
clipsy/storage.py,sha256=QNox2-spkVx_gMPSWqKgF_yj-SMpRJ4UAT6yd9l6rVY,9794
|
|
9
9
|
clipsy/utils.py,sha256=rztT20cXYJ7633q3oLwt6Fv0Ubq9hm5bPPKDkWyvxdI,2208
|
|
10
|
-
clipsy-1.
|
|
11
|
-
clipsy-1.
|
|
12
|
-
clipsy-1.
|
|
13
|
-
clipsy-1.
|
|
14
|
-
clipsy-1.
|
|
15
|
-
clipsy-1.
|
|
10
|
+
clipsy-1.7.0.dist-info/licenses/LICENSE,sha256=bZ7uVihgPnLGryi5ugUgkVrEoho0_hFAd3bVdhYXGqs,1071
|
|
11
|
+
clipsy-1.7.0.dist-info/METADATA,sha256=xO1ksDaZw867EM92OS3AsH8yakCtH1doeytarvNCAtQ,5811
|
|
12
|
+
clipsy-1.7.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
13
|
+
clipsy-1.7.0.dist-info/entry_points.txt,sha256=ISIoWU3Zj-4NEuj2pJW96LxpsvjRJJH73otv9gA9YSs,48
|
|
14
|
+
clipsy-1.7.0.dist-info/top_level.txt,sha256=trxprVJk4ZMudCshc7PD0N9iFgQO4Tq4sW5L5wLduns,7
|
|
15
|
+
clipsy-1.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|