abstractassistant 0.3.2__py3-none-any.whl → 0.3.4__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.
- abstractassistant/create_app_bundle.py +350 -8
- abstractassistant/ui/chat_bubble.py +15 -2
- abstractassistant/ui/history_dialog.py +575 -51
- abstractassistant/ui/qt_bubble.py +434 -8
- {abstractassistant-0.3.2.dist-info → abstractassistant-0.3.4.dist-info}/METADATA +21 -21
- {abstractassistant-0.3.2.dist-info → abstractassistant-0.3.4.dist-info}/RECORD +10 -11
- {abstractassistant-0.3.2.dist-info → abstractassistant-0.3.4.dist-info}/top_level.txt +0 -1
- setup_macos_app.py +0 -323
- {abstractassistant-0.3.2.dist-info → abstractassistant-0.3.4.dist-info}/WHEEL +0 -0
- {abstractassistant-0.3.2.dist-info → abstractassistant-0.3.4.dist-info}/entry_points.txt +0 -0
- {abstractassistant-0.3.2.dist-info → abstractassistant-0.3.4.dist-info}/licenses/LICENSE +0 -0
setup_macos_app.py
DELETED
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
macOS App Bundle Generator for AbstractAssistant.
|
|
4
|
-
|
|
5
|
-
Creates a native macOS .app bundle with Dock integration and system tray support.
|
|
6
|
-
Usage: create-app-bundle (after pip install abstractassistant)
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import os
|
|
10
|
-
import sys
|
|
11
|
-
import shutil
|
|
12
|
-
import subprocess
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
|
|
15
|
-
try:
|
|
16
|
-
from PIL import Image
|
|
17
|
-
PIL_AVAILABLE = True
|
|
18
|
-
except ImportError:
|
|
19
|
-
PIL_AVAILABLE = False
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class MacOSAppBundleGenerator:
|
|
23
|
-
"""Generates macOS app bundles for AbstractAssistant."""
|
|
24
|
-
|
|
25
|
-
def __init__(self, package_dir: Path):
|
|
26
|
-
"""Initialize the app bundle generator.
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
package_dir: Path to the abstractassistant package directory
|
|
30
|
-
"""
|
|
31
|
-
self.package_dir = package_dir
|
|
32
|
-
self.app_name = "AbstractAssistant"
|
|
33
|
-
self.app_bundle_path = Path("/Applications") / f"{self.app_name}.app"
|
|
34
|
-
|
|
35
|
-
def is_macos(self) -> bool:
|
|
36
|
-
"""Check if running on macOS."""
|
|
37
|
-
return sys.platform == "darwin"
|
|
38
|
-
|
|
39
|
-
def has_permissions(self) -> bool:
|
|
40
|
-
"""Check if we have permissions to write to /Applications."""
|
|
41
|
-
try:
|
|
42
|
-
test_file = Path("/Applications") / ".test_write_permission"
|
|
43
|
-
test_file.touch()
|
|
44
|
-
test_file.unlink()
|
|
45
|
-
return True
|
|
46
|
-
except (PermissionError, OSError):
|
|
47
|
-
return False
|
|
48
|
-
|
|
49
|
-
def create_app_bundle_structure(self) -> bool:
|
|
50
|
-
"""Create the basic app bundle directory structure."""
|
|
51
|
-
try:
|
|
52
|
-
# Create main directories
|
|
53
|
-
contents_dir = self.app_bundle_path / "Contents"
|
|
54
|
-
macos_dir = contents_dir / "MacOS"
|
|
55
|
-
resources_dir = contents_dir / "Resources"
|
|
56
|
-
|
|
57
|
-
for directory in [contents_dir, macos_dir, resources_dir]:
|
|
58
|
-
directory.mkdir(parents=True, exist_ok=True)
|
|
59
|
-
|
|
60
|
-
return True
|
|
61
|
-
except Exception as e:
|
|
62
|
-
print(f"Error creating app bundle structure: {e}")
|
|
63
|
-
return False
|
|
64
|
-
|
|
65
|
-
def generate_app_icon(self) -> bool:
|
|
66
|
-
"""Generate the app icon using the existing icon generator."""
|
|
67
|
-
try:
|
|
68
|
-
# Import the icon generator
|
|
69
|
-
sys.path.insert(0, str(self.package_dir))
|
|
70
|
-
from abstractassistant.utils.icon_generator import IconGenerator
|
|
71
|
-
|
|
72
|
-
# Generate high-resolution icon
|
|
73
|
-
generator = IconGenerator(size=512)
|
|
74
|
-
icon = generator.create_app_icon('blue', animated=False)
|
|
75
|
-
|
|
76
|
-
# Save as PNG
|
|
77
|
-
icon_path = self.app_bundle_path / "Contents" / "Resources" / "icon.png"
|
|
78
|
-
icon.save(str(icon_path))
|
|
79
|
-
|
|
80
|
-
# Create ICNS file
|
|
81
|
-
return self._create_icns_file(icon_path)
|
|
82
|
-
|
|
83
|
-
except Exception as e:
|
|
84
|
-
print(f"Error generating app icon: {e}")
|
|
85
|
-
return False
|
|
86
|
-
|
|
87
|
-
def _create_icns_file(self, png_path: Path) -> bool:
|
|
88
|
-
"""Create ICNS file from PNG using macOS iconutil."""
|
|
89
|
-
try:
|
|
90
|
-
# Create iconset directory
|
|
91
|
-
iconset_dir = png_path.parent / "temp_icons.iconset"
|
|
92
|
-
iconset_dir.mkdir(exist_ok=True)
|
|
93
|
-
|
|
94
|
-
# Load the PNG and create different sizes
|
|
95
|
-
icon = Image.open(png_path)
|
|
96
|
-
sizes = [
|
|
97
|
-
(16, 'icon_16x16.png'),
|
|
98
|
-
(32, 'icon_16x16@2x.png'),
|
|
99
|
-
(32, 'icon_32x32.png'),
|
|
100
|
-
(64, 'icon_32x32@2x.png'),
|
|
101
|
-
(128, 'icon_128x128.png'),
|
|
102
|
-
(256, 'icon_128x128@2x.png'),
|
|
103
|
-
(256, 'icon_256x256.png'),
|
|
104
|
-
(512, 'icon_256x256@2x.png'),
|
|
105
|
-
(512, 'icon_512x512.png'),
|
|
106
|
-
(1024, 'icon_512x512@2x.png')
|
|
107
|
-
]
|
|
108
|
-
|
|
109
|
-
for size, filename in sizes:
|
|
110
|
-
resized = icon.resize((size, size), Image.Resampling.LANCZOS)
|
|
111
|
-
resized.save(iconset_dir / filename)
|
|
112
|
-
|
|
113
|
-
# Convert to ICNS
|
|
114
|
-
icns_path = png_path.parent / "icon.icns"
|
|
115
|
-
result = subprocess.run([
|
|
116
|
-
'iconutil', '-c', 'icns', str(iconset_dir),
|
|
117
|
-
'-o', str(icns_path)
|
|
118
|
-
], capture_output=True, text=True)
|
|
119
|
-
|
|
120
|
-
# Clean up
|
|
121
|
-
shutil.rmtree(iconset_dir)
|
|
122
|
-
|
|
123
|
-
return result.returncode == 0
|
|
124
|
-
|
|
125
|
-
except Exception as e:
|
|
126
|
-
print(f"Error creating ICNS file: {e}")
|
|
127
|
-
return False
|
|
128
|
-
|
|
129
|
-
def create_info_plist(self) -> bool:
|
|
130
|
-
"""Create the Info.plist file."""
|
|
131
|
-
try:
|
|
132
|
-
plist_content = '''<?xml version="1.0" encoding="UTF-8"?>
|
|
133
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
134
|
-
<plist version="1.0">
|
|
135
|
-
<dict>
|
|
136
|
-
<key>CFBundleExecutable</key>
|
|
137
|
-
<string>AbstractAssistant</string>
|
|
138
|
-
<key>CFBundleIdentifier</key>
|
|
139
|
-
<string>ai.abstractcore.abstractassistant</string>
|
|
140
|
-
<key>CFBundleName</key>
|
|
141
|
-
<string>AbstractAssistant</string>
|
|
142
|
-
<key>CFBundleDisplayName</key>
|
|
143
|
-
<string>AbstractAssistant</string>
|
|
144
|
-
<key>CFBundleVersion</key>
|
|
145
|
-
<string>0.2.8</string>
|
|
146
|
-
<key>CFBundleShortVersionString</key>
|
|
147
|
-
<string>0.2.8</string>
|
|
148
|
-
<key>CFBundlePackageType</key>
|
|
149
|
-
<string>APPL</string>
|
|
150
|
-
<key>CFBundleSignature</key>
|
|
151
|
-
<string>????</string>
|
|
152
|
-
<key>CFBundleIconFile</key>
|
|
153
|
-
<string>icon.icns</string>
|
|
154
|
-
<key>LSMinimumSystemVersion</key>
|
|
155
|
-
<string>10.15</string>
|
|
156
|
-
<key>NSHighResolutionCapable</key>
|
|
157
|
-
<true/>
|
|
158
|
-
<key>NSRequiresAquaSystemAppearance</key>
|
|
159
|
-
<false/>
|
|
160
|
-
<key>LSUIElement</key>
|
|
161
|
-
<true/>
|
|
162
|
-
<key>NSAppleScriptEnabled</key>
|
|
163
|
-
<false/>
|
|
164
|
-
<key>CFBundleDocumentTypes</key>
|
|
165
|
-
<array/>
|
|
166
|
-
<key>NSPrincipalClass</key>
|
|
167
|
-
<string>NSApplication</string>
|
|
168
|
-
</dict>
|
|
169
|
-
</plist>'''
|
|
170
|
-
|
|
171
|
-
plist_path = self.app_bundle_path / "Contents" / "Info.plist"
|
|
172
|
-
plist_path.write_text(plist_content)
|
|
173
|
-
return True
|
|
174
|
-
|
|
175
|
-
except Exception as e:
|
|
176
|
-
print(f"Error creating Info.plist: {e}")
|
|
177
|
-
return False
|
|
178
|
-
|
|
179
|
-
def create_launch_script(self) -> bool:
|
|
180
|
-
"""Create the executable launch script."""
|
|
181
|
-
try:
|
|
182
|
-
script_content = '''#!/bin/bash
|
|
183
|
-
|
|
184
|
-
# AbstractAssistant macOS App Launcher
|
|
185
|
-
# This script launches the AbstractAssistant application
|
|
186
|
-
|
|
187
|
-
# Set up environment paths for GUI launch (common locations)
|
|
188
|
-
export PATH="/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:$PATH"
|
|
189
|
-
|
|
190
|
-
# Add user-specific Python paths if they exist
|
|
191
|
-
if [ -d "$HOME/.pyenv/shims" ]; then
|
|
192
|
-
export PATH="$HOME/.pyenv/shims:$PATH"
|
|
193
|
-
fi
|
|
194
|
-
|
|
195
|
-
if [ -d "$HOME/.local/bin" ]; then
|
|
196
|
-
export PATH="$HOME/.local/bin:$PATH"
|
|
197
|
-
fi
|
|
198
|
-
|
|
199
|
-
if [ -d "/opt/anaconda3/bin" ]; then
|
|
200
|
-
export PATH="/opt/anaconda3/bin:$PATH"
|
|
201
|
-
fi
|
|
202
|
-
|
|
203
|
-
if [ -d "$HOME/anaconda3/bin" ]; then
|
|
204
|
-
export PATH="$HOME/anaconda3/bin:$PATH"
|
|
205
|
-
fi
|
|
206
|
-
|
|
207
|
-
# Function to find Python with abstractassistant installed
|
|
208
|
-
find_python_with_abstractassistant() {
|
|
209
|
-
# Try specific paths first (more reliable than PATH-based search)
|
|
210
|
-
for python_path in \\
|
|
211
|
-
"$HOME/.pyenv/versions/*/bin/python3" \\
|
|
212
|
-
"$HOME/.pyenv/shims/python3" \\
|
|
213
|
-
"/usr/local/bin/python3" \\
|
|
214
|
-
"/opt/homebrew/bin/python3" \\
|
|
215
|
-
"/usr/bin/python3" \\
|
|
216
|
-
"/opt/anaconda3/bin/python" \\
|
|
217
|
-
"$HOME/anaconda3/bin/python" \\
|
|
218
|
-
"/usr/local/anaconda3/bin/python"; do
|
|
219
|
-
|
|
220
|
-
# Expand glob patterns
|
|
221
|
-
for py in $python_path; do
|
|
222
|
-
if [ -x "$py" ] && "$py" -c "import abstractassistant" 2>/dev/null; then
|
|
223
|
-
echo "$py"
|
|
224
|
-
return 0
|
|
225
|
-
fi
|
|
226
|
-
done
|
|
227
|
-
done
|
|
228
|
-
|
|
229
|
-
# Fallback to PATH-based search
|
|
230
|
-
for python_cmd in python3 python python3.12 python3.11 python3.10 python3.9; do
|
|
231
|
-
if command -v "$python_cmd" >/dev/null 2>&1; then
|
|
232
|
-
if "$python_cmd" -c "import abstractassistant" 2>/dev/null; then
|
|
233
|
-
echo "$python_cmd"
|
|
234
|
-
return 0
|
|
235
|
-
fi
|
|
236
|
-
fi
|
|
237
|
-
done
|
|
238
|
-
|
|
239
|
-
return 1
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
# Find Python with AbstractAssistant
|
|
243
|
-
PYTHON_EXEC=$(find_python_with_abstractassistant)
|
|
244
|
-
|
|
245
|
-
if [ -z "$PYTHON_EXEC" ]; then
|
|
246
|
-
osascript -e 'display dialog "AbstractAssistant not found in any Python installation.\\n\\nPlease install it with:\\npip install abstractassistant\\n\\nOr run the create-app-bundle command after installation." with title "AbstractAssistant" buttons {"OK"} default button "OK" with icon caution'
|
|
247
|
-
exit 1
|
|
248
|
-
fi
|
|
249
|
-
|
|
250
|
-
# Change to a neutral directory to avoid importing development versions
|
|
251
|
-
cd /tmp
|
|
252
|
-
|
|
253
|
-
# Launch the assistant
|
|
254
|
-
exec "$PYTHON_EXEC" -m abstractassistant.cli "$@"'''
|
|
255
|
-
|
|
256
|
-
script_path = self.app_bundle_path / "Contents" / "MacOS" / "AbstractAssistant"
|
|
257
|
-
script_path.write_text(script_content)
|
|
258
|
-
|
|
259
|
-
# Make executable
|
|
260
|
-
os.chmod(script_path, 0o755)
|
|
261
|
-
return True
|
|
262
|
-
|
|
263
|
-
except Exception as e:
|
|
264
|
-
print(f"Error creating launch script: {e}")
|
|
265
|
-
return False
|
|
266
|
-
|
|
267
|
-
def generate_app_bundle(self) -> bool:
|
|
268
|
-
"""Generate the complete macOS app bundle."""
|
|
269
|
-
if not self.is_macos():
|
|
270
|
-
print("macOS app bundle generation is only available on macOS")
|
|
271
|
-
return False
|
|
272
|
-
|
|
273
|
-
if not self.has_permissions():
|
|
274
|
-
print("Insufficient permissions to create app bundle in /Applications")
|
|
275
|
-
print("Please run with sudo or manually copy the app bundle")
|
|
276
|
-
return False
|
|
277
|
-
|
|
278
|
-
print("Creating macOS app bundle...")
|
|
279
|
-
|
|
280
|
-
# Remove existing bundle if it exists
|
|
281
|
-
if self.app_bundle_path.exists():
|
|
282
|
-
shutil.rmtree(self.app_bundle_path)
|
|
283
|
-
|
|
284
|
-
# Create bundle structure
|
|
285
|
-
if not self.create_app_bundle_structure():
|
|
286
|
-
return False
|
|
287
|
-
|
|
288
|
-
# Generate icon
|
|
289
|
-
if not self.generate_app_icon():
|
|
290
|
-
return False
|
|
291
|
-
|
|
292
|
-
# Create Info.plist
|
|
293
|
-
if not self.create_info_plist():
|
|
294
|
-
return False
|
|
295
|
-
|
|
296
|
-
# Create launch script
|
|
297
|
-
if not self.create_launch_script():
|
|
298
|
-
return False
|
|
299
|
-
|
|
300
|
-
print(f"✅ macOS app bundle created successfully!")
|
|
301
|
-
print(f" Location: {self.app_bundle_path}")
|
|
302
|
-
print(f" You can now launch AbstractAssistant from the Dock!")
|
|
303
|
-
|
|
304
|
-
return True
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
def create_macos_app_bundle():
|
|
308
|
-
"""Main function to create macOS app bundle during installation."""
|
|
309
|
-
try:
|
|
310
|
-
# Find the package directory
|
|
311
|
-
package_dir = Path(__file__).parent
|
|
312
|
-
|
|
313
|
-
generator = MacOSAppBundleGenerator(package_dir)
|
|
314
|
-
return generator.generate_app_bundle()
|
|
315
|
-
|
|
316
|
-
except Exception as e:
|
|
317
|
-
print(f"Error creating macOS app bundle: {e}")
|
|
318
|
-
return False
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if __name__ == "__main__":
|
|
322
|
-
success = create_macos_app_bundle()
|
|
323
|
-
sys.exit(0 if success else 1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|