app: fix status item icons
@ -22,24 +22,33 @@
|
||||
// user about what this is doing, and ideally use Touch ID.
|
||||
// or add an alias in the current shell environment,
|
||||
// which wouldn't require any special privileges
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
createSymlinkWithAuthorization();
|
||||
});
|
||||
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// createSymlinkWithAuthorization();
|
||||
// });
|
||||
|
||||
// show status menu
|
||||
NSMenu *menu = [[NSMenu alloc] init];
|
||||
|
||||
NSMenuItem *openSettingsItem = [[NSMenuItem alloc] initWithTitle:@"Settings..." action:@selector(openSettingsWindow) keyEquivalent:@""];
|
||||
[menu addItem:openSettingsItem];
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
[menu addItemWithTitle:@"Quit Ollama" action:@selector(quit) keyEquivalent:@"q"];
|
||||
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
|
||||
[self.statusItem addObserver:self forKeyPath:@"button.effectiveAppearance" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionInitial context:nil];
|
||||
|
||||
self.statusItem.menu = menu;
|
||||
NSImage *statusImage = [NSImage imageNamed:@"icon"];
|
||||
[self showIcon];
|
||||
}
|
||||
|
||||
-(void) showIcon {
|
||||
NSAppearance* appearance = self.statusItem.button.effectiveAppearance;
|
||||
NSString* appearanceName = (NSString*)(appearance.name);
|
||||
NSString* iconName = [[appearanceName lowercaseString] containsString:@"dark"] ? @"iconDark" : @"icon";
|
||||
NSImage* statusImage = [NSImage imageNamed:iconName];
|
||||
[statusImage setTemplate:YES];
|
||||
self.statusItem.button.image = statusImage;
|
||||
}
|
||||
|
||||
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
|
||||
[self showIcon];
|
||||
}
|
||||
|
||||
- (void)openSettingsWindow {
|
||||
if (!self.settingsWindow) {
|
||||
// Create the settings window centered on the screen
|
||||
@ -66,57 +75,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - NSToolbarDelegate
|
||||
|
||||
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSToolbarItemIdentifier)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag {
|
||||
NSToolbarItem *toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
|
||||
|
||||
if ([itemIdentifier isEqualToString:@"General"]) {
|
||||
toolbarItem.label = @"General";
|
||||
toolbarItem.paletteLabel = @"General";
|
||||
toolbarItem.toolTip = @"General Settings";
|
||||
toolbarItem.image = [NSImage imageWithSystemSymbolName:@"gear" accessibilityDescription:nil]; // Monochrome symbol
|
||||
toolbarItem.target = self;
|
||||
toolbarItem.action = @selector(switchTabs:);
|
||||
}
|
||||
|
||||
if ([itemIdentifier isEqualToString:@"Networking"]) {
|
||||
toolbarItem.label = @"Networking";
|
||||
toolbarItem.paletteLabel = @"Networking";
|
||||
toolbarItem.toolTip = @"Networking Settings";
|
||||
toolbarItem.image = [NSImage imageWithSystemSymbolName:@"network" accessibilityDescription:nil]; // Monochrome symbol
|
||||
toolbarItem.target = self;
|
||||
toolbarItem.action = @selector(switchTabs:);
|
||||
}
|
||||
// Create other items with their respective images and selectors here...
|
||||
|
||||
return toolbarItem;
|
||||
}
|
||||
|
||||
- (NSArray<NSToolbarItemIdentifier> *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar {
|
||||
return @[
|
||||
NSToolbarFlexibleSpaceItemIdentifier,
|
||||
@"General",
|
||||
@"Networking",
|
||||
NSToolbarFlexibleSpaceItemIdentifier
|
||||
];
|
||||
}
|
||||
|
||||
- (NSArray<NSToolbarItemIdentifier> *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar {
|
||||
return @[
|
||||
NSToolbarFlexibleSpaceItemIdentifier,
|
||||
@"General",
|
||||
@"Networking",
|
||||
NSToolbarFlexibleSpaceItemIdentifier
|
||||
];
|
||||
}
|
||||
|
||||
- (void)switchTabs:(NSToolbarItem *)sender {
|
||||
// Your code to switch tabs based on the sender's identifier
|
||||
}
|
||||
|
||||
- (void)quit {
|
||||
Quit();
|
||||
[NSApp stop:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -132,12 +92,13 @@ int askToMoveToApplications() {
|
||||
[alert setInformativeText:@"Ollama works best when run from the Applications directory."];
|
||||
[alert addButtonWithTitle:@"Move to Applications"];
|
||||
[alert addButtonWithTitle:@"Don't move"];
|
||||
|
||||
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
|
||||
if ([alert runModal] != NSAlertFirstButtonReturn) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// move to applications
|
||||
NSString *applicationsPath = @"/Applications";
|
||||
NSString *newPath = [applicationsPath stringByAppendingPathComponent:@"Ollama.app"];
|
||||
|
@ -33,21 +33,22 @@ func run() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resources := filepath.Join(filepath.Dir(exe), "..", "Resources")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var done chan int
|
||||
|
||||
done, err = SpawnServer(ctx, filepath.Join(resources, "ollama"))
|
||||
done, err = SpawnServer(ctx, filepath.Join(filepath.Dir(exe), "..", "Resources", "ollama"))
|
||||
if err != nil {
|
||||
slog.Error(fmt.Sprintf("Failed to spawn ollama server %s", err))
|
||||
done = make(chan int, 1)
|
||||
done <- 1
|
||||
}
|
||||
|
||||
// Run the native app
|
||||
// Run the native macOS app
|
||||
// Note: this will block until the app is closed
|
||||
C.run()
|
||||
|
||||
slog.Info("ollama macOS app closed")
|
||||
|
||||
cancel()
|
||||
slog.Info("Waiting for ollama server to shutdown...")
|
||||
if done != nil {
|
||||
|
@ -38,4 +38,4 @@ void killOtherInstances() {
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,10 +13,10 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/jmorganca/ollama/app/lifecycle"
|
||||
"github.com/jmorganca/ollama/app/store"
|
||||
"github.com/jmorganca/ollama/app/tray"
|
||||
"github.com/jmorganca/ollama/app/updater"
|
||||
"github.com/ollama/ollama/app/lifecycle"
|
||||
"github.com/ollama/ollama/app/store"
|
||||
"github.com/ollama/ollama/app/tray"
|
||||
"github.com/ollama/ollama/app/updater"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
Before Width: | Height: | Size: 363 B After Width: | Height: | Size: 382 B |
Before Width: | Height: | Size: 668 B After Width: | Height: | Size: 691 B |
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 382 B |
Before Width: | Height: | Size: 768 B After Width: | Height: | Size: 721 B |
@ -1,92 +0,0 @@
|
||||
package lifecycle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/ollama/ollama/app/store"
|
||||
"github.com/ollama/ollama/app/tray"
|
||||
)
|
||||
|
||||
func Run() {
|
||||
InitLogging()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var done chan int
|
||||
|
||||
t, err := tray.NewTray()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to start: %s", err)
|
||||
}
|
||||
callbacks := t.GetCallbacks()
|
||||
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
slog.Debug("starting callback loop")
|
||||
for {
|
||||
select {
|
||||
case <-callbacks.Quit:
|
||||
slog.Debug("quit called")
|
||||
t.Quit()
|
||||
case <-signals:
|
||||
slog.Debug("shutting down due to signal")
|
||||
t.Quit()
|
||||
case <-callbacks.Update:
|
||||
err := DoUpgrade(cancel, done)
|
||||
if err != nil {
|
||||
slog.Warn(fmt.Sprintf("upgrade attempt failed: %s", err))
|
||||
}
|
||||
case <-callbacks.ShowLogs:
|
||||
ShowLogs()
|
||||
case <-callbacks.DoFirstUse:
|
||||
err := GetStarted()
|
||||
if err != nil {
|
||||
slog.Warn(fmt.Sprintf("Failed to launch getting started shell: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Are we first use?
|
||||
if !store.GetFirstTimeRun() {
|
||||
slog.Debug("First time run")
|
||||
err = t.DisplayFirstUseNotification()
|
||||
if err != nil {
|
||||
slog.Debug(fmt.Sprintf("XXX failed to display first use notification %v", err))
|
||||
}
|
||||
store.SetFirstTimeRun(true)
|
||||
} else {
|
||||
slog.Debug("Not first time, skipping first run notification")
|
||||
}
|
||||
|
||||
if IsServerRunning(ctx) {
|
||||
slog.Info("Detected another instance of ollama running, exiting")
|
||||
os.Exit(1)
|
||||
} else {
|
||||
done, err = SpawnServer(ctx, CLIName)
|
||||
if err != nil {
|
||||
// TODO - should we retry in a backoff loop?
|
||||
// TODO - should we pop up a warning and maybe add a menu item to view application logs?
|
||||
slog.Error(fmt.Sprintf("Failed to spawn ollama server %s", err))
|
||||
done = make(chan int, 1)
|
||||
done <- 1
|
||||
}
|
||||
}
|
||||
|
||||
StartBackgroundUpdaterChecker(ctx, t.UpdateAvailable)
|
||||
|
||||
t.Run()
|
||||
cancel()
|
||||
slog.Info("Waiting for ollama server to shutdown...")
|
||||
if done != nil {
|
||||
<-done
|
||||
}
|
||||
slog.Info("Ollama app exiting")
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
//go:build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
@ -1,25 +0,0 @@
|
||||
# Set your variables here.
|
||||
REPO="jmorganca/ollama"
|
||||
|
||||
# Check if VERSION is set
|
||||
if [[ -z "${VERSION}" ]]; then
|
||||
echo "VERSION is not set. Please set the VERSION environment variable."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OS=$(go env GOOS)
|
||||
|
||||
./script/build_${OS}.sh
|
||||
|
||||
# Create a new tag if it doesn't exist.
|
||||
if ! git rev-parse v$VERSION >/dev/null 2>&1; then
|
||||
git tag v$VERSION
|
||||
fi
|
||||
|
||||
git push origin v$VERSION
|
||||
|
||||
# Create a new release.
|
||||
gh release create -p v$VERSION -t v$VERSION
|
||||
|
||||
# Upload the zip file.
|
||||
gh release upload v$VERSION ./dist/* --clobber
|
@ -2,6 +2,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
rm -rf $TMPDIR/Ollama.app
|
||||
cp -R app/darwin/Ollama.app $TMPDIR/Ollama.app
|
||||
mkdir -p $TMPDIR/Ollama.app/Contents/Resources $TMPDIR/Ollama.app/Contents/MacOS
|
||||
go build -o $TMPDIR/Ollama.app/Contents/Resources/ollama .
|
||||
|