Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a770e059d | |||
| b4027f260d | |||
| 58c407d16b | |||
| e973efe2b3 | |||
| 447214ded4 | |||
| f2945061ca | |||
| d3b668bbfe | |||
| a0f708859d | |||
| 60c882000f | |||
| b91561d6df | |||
| 82da6641bb | |||
| 22cd854c37 | |||
| 5c85e34b41 |
32
.github/workflows/build.yml
vendored
Normal file
32
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Build
|
||||
on:
|
||||
workflow_call:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
arm64_ventura:
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- run: |
|
||||
brew fetch --force --bottle-tag=arm64_ventura sdl2
|
||||
brew install $(brew --cache --bottle-tag=arm64_ventura sdl2)
|
||||
sudo mkdir -p /opt/homebrew/lib
|
||||
sudo ln -s /usr/local/lib/libSDL2.a /opt/homebrew/lib/libSDL2.a
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: cheetah
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ggerganov/whisper.cpp
|
||||
ref: v1.3.0
|
||||
path: whisper.cpp
|
||||
- run: |
|
||||
cd cheetah
|
||||
xcodebuild -scheme Cheetah -configuration Release -destination generic/platform=macOS -derivedDataPath build
|
||||
cd build/Build/Products/Release
|
||||
zip -r Cheetah.zip Cheetah.app
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Cheetah
|
||||
path: cheetah/build/Build/Products/Release/Cheetah.zip
|
||||
32
.github/workflows/release.yml
vendored
Normal file
32
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*'
|
||||
- 'v*.*.*'
|
||||
jobs:
|
||||
build:
|
||||
uses: ./.github/workflows/build.yml
|
||||
release:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: Cheetah
|
||||
- uses: actions/create-release@v1
|
||||
id: create_release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
draft: true
|
||||
- uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./Cheetah.zip
|
||||
asset_name: Cheetah.zip
|
||||
asset_content_type: application/zip
|
||||
@ -35,6 +35,8 @@
|
||||
37AE7AC629A6E9C400C45FF6 /* stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37AE7AC529A6E9C400C45FF6 /* stream.cpp */; };
|
||||
37AE7AC929A6F7F200C45FF6 /* stream.h in Headers */ = {isa = PBXBuildFile; fileRef = 37AE7AC729A6EC2F00C45FF6 /* stream.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
37AE7ACA29A70CE900C45FF6 /* whisper.h in Headers */ = {isa = PBXBuildFile; fileRef = 37AE7AB129A5AAD400C45FF6 /* whisper.h */; };
|
||||
37B2997D29F9756F00971690 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 37B2997C29F9756F00971690 /* Sparkle */; };
|
||||
37B2997F29F9757700971690 /* Sparkle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B2997E29F9757700971690 /* Sparkle.swift */; };
|
||||
37B3A50629CE15AC0029821F /* OpenAIEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B3A4FD29CE15AC0029821F /* OpenAIEndpoint.swift */; };
|
||||
37B3A50729CE15AC0029821F /* OpenAISwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B3A4FE29CE15AC0029821F /* OpenAISwift.swift */; };
|
||||
37B3A51C29CE16330029821F /* ImageGeneration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37B3A51629CE16330029821F /* ImageGeneration.swift */; };
|
||||
@ -166,6 +168,7 @@
|
||||
37AE7ABF29A6E96A00C45FF6 /* common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = common.cpp; path = ../../whisper.cpp/examples/common.cpp; sourceTree = "<group>"; };
|
||||
37AE7AC529A6E9C400C45FF6 /* stream.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = stream.cpp; sourceTree = "<group>"; };
|
||||
37AE7AC729A6EC2F00C45FF6 /* stream.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stream.h; sourceTree = "<group>"; };
|
||||
37B2997E29F9757700971690 /* Sparkle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sparkle.swift; sourceTree = "<group>"; };
|
||||
37B3A4FD29CE15AC0029821F /* OpenAIEndpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenAIEndpoint.swift; sourceTree = "<group>"; };
|
||||
37B3A4FE29CE15AC0029821F /* OpenAISwift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenAISwift.swift; sourceTree = "<group>"; };
|
||||
37B3A51629CE16330029821F /* ImageGeneration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageGeneration.swift; sourceTree = "<group>"; };
|
||||
@ -198,6 +201,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
37B2997D29F9756F00971690 /* Sparkle in Frameworks */,
|
||||
37B4C27729BD7D9F00E83465 /* CheetahIPC.framework in Frameworks */,
|
||||
37AE7A9529A5A9C200C45FF6 /* LibWhisper.framework in Frameworks */,
|
||||
);
|
||||
@ -273,6 +277,7 @@
|
||||
376437B929A854F500297AC6 /* ConversationAnalyzer.swift */,
|
||||
37951C0D29BCD71000C61AC5 /* ModelDownloader.swift */,
|
||||
37B4C27929BD9F8700E83465 /* BrowserExtension.swift */,
|
||||
37B2997E29F9757700971690 /* Sparkle.swift */,
|
||||
37AE7A7629A5A8B400C45FF6 /* Assets.xcassets */,
|
||||
37AE7A7B29A5A8B400C45FF6 /* Cheetah.entitlements */,
|
||||
37AE7A7829A5A8B400C45FF6 /* Preview Content */,
|
||||
@ -429,6 +434,7 @@
|
||||
);
|
||||
name = Cheetah;
|
||||
packageProductDependencies = (
|
||||
37B2997C29F9756F00971690 /* Sparkle */,
|
||||
);
|
||||
productName = Cheetah;
|
||||
productReference = 37AE7A6F29A5A8B300C45FF6 /* Cheetah.app */;
|
||||
@ -506,6 +512,7 @@
|
||||
);
|
||||
mainGroup = 37AE7A6629A5A8B300C45FF6;
|
||||
packageReferences = (
|
||||
37B2997B29F9756F00971690 /* XCRemoteSwiftPackageReference "Sparkle" */,
|
||||
);
|
||||
productRefGroup = 37AE7A7029A5A8B300C45FF6 /* Products */;
|
||||
projectDirPath = "";
|
||||
@ -568,6 +575,7 @@
|
||||
37951C0E29BCD71000C61AC5 /* ModelDownloader.swift in Sources */,
|
||||
37AE7A7529A5A8B300C45FF6 /* ContentView.swift in Sources */,
|
||||
37B3A51C29CE16330029821F /* ImageGeneration.swift in Sources */,
|
||||
37B2997F29F9757700971690 /* Sparkle.swift in Sources */,
|
||||
37B3A51E29CE16330029821F /* Command.swift in Sources */,
|
||||
376437BA29A854F500297AC6 /* ConversationAnalyzer.swift in Sources */,
|
||||
37B4C27D29C1202C00E83465 /* NSTextFieldWrapper.swift in Sources */,
|
||||
@ -635,7 +643,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = ExtensionHelper/ExtensionHelper.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 5JL49Y835V;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@ -651,7 +659,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = ExtensionHelper/ExtensionHelper.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 5JL49Y835V;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@ -666,6 +674,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = arm64;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
@ -715,7 +724,6 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.1;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
@ -726,6 +734,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = arm64;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
@ -770,7 +779,6 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.1;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
@ -786,9 +794,9 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Cheetah/Cheetah.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 4;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Cheetah/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 5JL49Y835V;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@ -799,7 +807,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
MARKETING_VERSION = 1.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.phrack.Cheetah;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@ -816,9 +824,9 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Cheetah/Cheetah.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 4;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Cheetah/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 5JL49Y835V;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@ -829,7 +837,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
MARKETING_VERSION = 1.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.phrack.Cheetah;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
@ -845,11 +853,15 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5JL49Y835V;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
/opt/homebrew/include/SDL2,
|
||||
/usr/local/include/SDL2,
|
||||
);
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -859,13 +871,23 @@
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
OTHER_CFLAGS = (
|
||||
"-I/opt/homebrew/include/SDL2",
|
||||
"-D_THREAD_SAFE",
|
||||
"-DGGML_USE_ACCELERATE",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-L/opt/homebrew/lib",
|
||||
"-lSDL2",
|
||||
/opt/homebrew/lib/libSDL2.a,
|
||||
"-l",
|
||||
iconv,
|
||||
"-weak_framework",
|
||||
CoreHaptics,
|
||||
"-weak_framework",
|
||||
GameController,
|
||||
"-weak_framework",
|
||||
ForceFeedback,
|
||||
"-weak_framework",
|
||||
Carbon,
|
||||
"-weak_framework",
|
||||
AppKit,
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.phrack.LibWhisper;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
@ -886,11 +908,15 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5JL49Y835V;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
/opt/homebrew/include/SDL2,
|
||||
/usr/local/include/SDL2,
|
||||
);
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -900,13 +926,23 @@
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
OTHER_CFLAGS = (
|
||||
"-I/opt/homebrew/include/SDL2",
|
||||
"-D_THREAD_SAFE",
|
||||
"-DGGML_USE_ACCELERATE",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-L/opt/homebrew/lib",
|
||||
"-lSDL2",
|
||||
/opt/homebrew/lib/libSDL2.a,
|
||||
"-l",
|
||||
iconv,
|
||||
"-weak_framework",
|
||||
CoreHaptics,
|
||||
"-weak_framework",
|
||||
GameController,
|
||||
"-weak_framework",
|
||||
ForceFeedback,
|
||||
"-weak_framework",
|
||||
Carbon,
|
||||
"-weak_framework",
|
||||
AppKit,
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.phrack.LibWhisper;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
@ -926,7 +962,7 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5JL49Y835V;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
@ -958,7 +994,7 @@
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5JL49Y835V;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
@ -1030,6 +1066,25 @@
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
37B2997B29F9756F00971690 /* XCRemoteSwiftPackageReference "Sparkle" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/sparkle-project/Sparkle";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 2.0.0;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
37B2997C29F9756F00971690 /* Sparkle */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 37B2997B29F9756F00971690 /* XCRemoteSwiftPackageReference "Sparkle" */;
|
||||
productName = Sparkle;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 37AE7A6729A5A8B300C45FF6 /* Project object */;
|
||||
}
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "sparkle",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/sparkle-project/Sparkle",
|
||||
"state" : {
|
||||
"revision" : "7907f058bcef1132c9b4af6c049cac598330a5f9",
|
||||
"version" : "2.4.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
@ -2,6 +2,7 @@ import SwiftUI
|
||||
import Combine
|
||||
import LibWhisper
|
||||
import CheetahIPC
|
||||
import Sparkle
|
||||
|
||||
enum AnswerRequest {
|
||||
case none
|
||||
@ -24,6 +25,7 @@ class AppViewModel: ObservableObject {
|
||||
|
||||
@Published var analyzer: ConversationAnalyzer?
|
||||
@Published var answerRequest = AnswerRequest.none
|
||||
@Published var errorDescription: String?
|
||||
|
||||
@Published var transcript: String?
|
||||
@Published var answer: String?
|
||||
@ -44,6 +46,8 @@ struct CheetahApp: App {
|
||||
|
||||
var extensionState = BrowserExtensionState()
|
||||
|
||||
let updaterController = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil)
|
||||
|
||||
func start() async {
|
||||
viewModel.devices = try! CaptureDevice.devices
|
||||
|
||||
@ -67,35 +71,40 @@ struct CheetahApp: App {
|
||||
// Install manifest needed for the browser extension to talk to ExtensionHelper
|
||||
_ = try? installNativeMessagingManifest()
|
||||
|
||||
do {
|
||||
for try await request in viewModel.$answerRequest.receive(on: RunLoop.main).values {
|
||||
if let analyzer = viewModel.analyzer {
|
||||
switch request {
|
||||
case .answerQuestion:
|
||||
try await analyzer.answer()
|
||||
viewModel.answer = analyzer.context[.answer]
|
||||
viewModel.codeAnswer = analyzer.context[.codeAnswer]
|
||||
viewModel.answerRequest = .none
|
||||
while true {
|
||||
do {
|
||||
for try await request in viewModel.$answerRequest.receive(on: RunLoop.main).values {
|
||||
if let analyzer = viewModel.analyzer {
|
||||
switch request {
|
||||
case .answerQuestion:
|
||||
try await analyzer.answer()
|
||||
viewModel.answer = analyzer.context[.answer]
|
||||
viewModel.codeAnswer = analyzer.context[.codeAnswer]
|
||||
viewModel.answerRequest = .none
|
||||
|
||||
case .refineAnswer(let selection):
|
||||
try await analyzer.answer(refine: true, selection: selection)
|
||||
viewModel.answer = analyzer.context[.answer]
|
||||
viewModel.codeAnswer = analyzer.context[.codeAnswer]
|
||||
viewModel.answerRequest = .none
|
||||
case .refineAnswer(let selection):
|
||||
try await analyzer.answer(refine: true, selection: selection)
|
||||
viewModel.answer = analyzer.context[.answer]
|
||||
viewModel.codeAnswer = analyzer.context[.codeAnswer]
|
||||
viewModel.answerRequest = .none
|
||||
|
||||
case .analyzeCode:
|
||||
try await analyzer.analyzeCode(extensionState: extensionState)
|
||||
viewModel.answer = analyzer.context[.answer]
|
||||
viewModel.answerRequest = .none
|
||||
case .analyzeCode:
|
||||
try await analyzer.analyzeCode(extensionState: extensionState)
|
||||
viewModel.answer = analyzer.context[.answer]
|
||||
viewModel.answerRequest = .none
|
||||
|
||||
case .none:
|
||||
break
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch let error as ErrorResult {
|
||||
viewModel.errorDescription = error.message
|
||||
viewModel.answerRequest = .none
|
||||
} catch {
|
||||
viewModel.errorDescription = error.localizedDescription
|
||||
viewModel.answerRequest = .none
|
||||
}
|
||||
} catch {
|
||||
viewModel.answerRequest = .none
|
||||
//TODO: handle error
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,6 +120,37 @@ struct CheetahApp: App {
|
||||
}
|
||||
.windowResizability(.contentSize)
|
||||
.windowStyle(.hiddenTitleBar)
|
||||
.commands {
|
||||
CommandGroup(after: .appInfo) {
|
||||
CheckForUpdatesView(updater: updaterController.updater)
|
||||
}
|
||||
CommandGroup(replacing: .appSettings) {
|
||||
Button(action: {
|
||||
viewModel.authToken = nil
|
||||
resetAfterSettingsChanged()
|
||||
}) {
|
||||
Text("Change API Key…")
|
||||
}
|
||||
Button(action: {
|
||||
if viewModel.useGPT4 == true {
|
||||
viewModel.useGPT4 = false
|
||||
} else {
|
||||
viewModel.useGPT4 = true
|
||||
}
|
||||
resetAfterSettingsChanged()
|
||||
}) {
|
||||
Text("Use GPT-4")
|
||||
if viewModel.useGPT4 == true {
|
||||
Image(systemName: "checkmark")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resetAfterSettingsChanged() {
|
||||
viewModel.selectedDevice = nil
|
||||
viewModel.analyzer = nil
|
||||
}
|
||||
|
||||
func setCaptureDevice(_ device: CaptureDevice?) {
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
<dict>
|
||||
<key>SUFeedURL</key>
|
||||
<string>https://cheetah-sw-update.leetcodemafia.com/appcast.xml</string>
|
||||
<key>SUPublicEDKey</key>
|
||||
<string>30J/+sJRxhziFhK63Xe6OY5kjwF5tG7klmntA0XIGNM=</string>
|
||||
<key>SUEnableSystemProfiling</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -77,6 +77,8 @@ class OpenAIExecutor {
|
||||
let text = result.choices?.first?.text
|
||||
if let text = text {
|
||||
log(completion: text)
|
||||
} else if let error = result.error {
|
||||
throw error
|
||||
}
|
||||
return text
|
||||
}
|
||||
@ -87,6 +89,8 @@ class OpenAIExecutor {
|
||||
let content = result.choices?.first?.message.content
|
||||
if let content = content {
|
||||
log(completion: content)
|
||||
} else if let error = result.error {
|
||||
throw error
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
32
Cheetah/Sparkle.swift
Normal file
32
Cheetah/Sparkle.swift
Normal file
@ -0,0 +1,32 @@
|
||||
import SwiftUI
|
||||
import Sparkle
|
||||
|
||||
// This view model class publishes when new updates can be checked by the user
|
||||
final class CheckForUpdatesViewModel: ObservableObject {
|
||||
@Published var canCheckForUpdates = false
|
||||
|
||||
init(updater: SPUUpdater) {
|
||||
updater.publisher(for: \.canCheckForUpdates)
|
||||
.assign(to: &$canCheckForUpdates)
|
||||
}
|
||||
}
|
||||
|
||||
// This is the view for the Check for Updates menu item
|
||||
// Note this intermediate view is necessary for the disabled state on the menu item to work properly before Monterey.
|
||||
// See https://stackoverflow.com/questions/68553092/menu-not-updating-swiftui-bug for more info
|
||||
struct CheckForUpdatesView: View {
|
||||
@ObservedObject private var checkForUpdatesViewModel: CheckForUpdatesViewModel
|
||||
private let updater: SPUUpdater
|
||||
|
||||
init(updater: SPUUpdater) {
|
||||
self.updater = updater
|
||||
|
||||
// Create our view model for our CheckForUpdatesView
|
||||
self.checkForUpdatesViewModel = CheckForUpdatesViewModel(updater: updater)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Button("Check for Updates…", action: updater.checkForUpdates)
|
||||
.disabled(!checkForUpdatesViewModel.canCheckForUpdates)
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@ struct AuthTokenView: View {
|
||||
}
|
||||
.privacySensitive()
|
||||
.frame(width: 300)
|
||||
Toggle("Use GPT-4 (access required)", isOn: $toggleValue)
|
||||
Toggle("Use GPT-4 (API access required)", isOn: $toggleValue)
|
||||
Button("Save") {
|
||||
storedToken = tokenValue
|
||||
useGPT4 = toggleValue
|
||||
@ -30,7 +30,7 @@ struct AuthTokenView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct APIKeyView_Previews: PreviewProvider {
|
||||
struct AuthTokenView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
return AuthTokenView(
|
||||
storedToken: Binding.constant(nil),
|
||||
|
||||
@ -7,6 +7,9 @@ struct CoachView: View {
|
||||
@State var answer: String
|
||||
@State var answerSelection = NSRange()
|
||||
|
||||
@State var showError = false
|
||||
@State var errorDescription = ""
|
||||
|
||||
init(viewModel: AppViewModel) {
|
||||
self.viewModel = viewModel
|
||||
self.answer = viewModel.answer ?? ""
|
||||
@ -55,6 +58,17 @@ struct CoachView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.onReceive(viewModel.$errorDescription) {
|
||||
if let error = $0 {
|
||||
self.showError = true
|
||||
self.errorDescription = error
|
||||
}
|
||||
}
|
||||
.alert(errorDescription, isPresented: $showError) {
|
||||
Button("OK", role: .cancel) {
|
||||
self.showError = false
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
if let transcript = viewModel.transcript {
|
||||
@ -64,21 +78,26 @@ struct CoachView: View {
|
||||
.font(.footnote.italic())
|
||||
}
|
||||
ScrollView {
|
||||
NSTextFieldWrapper(text: $answer, selectedRange: $answerSelection)
|
||||
.onChange(of: viewModel.answer) {
|
||||
if let newAnswer = $0 {
|
||||
self.answer = newAnswer
|
||||
if answer != "" {
|
||||
NSTextFieldWrapper(text: $answer, selectedRange: $answerSelection)
|
||||
.onChange(of: viewModel.answer) {
|
||||
if let newAnswer = $0 {
|
||||
self.answer = newAnswer
|
||||
}
|
||||
}
|
||||
}
|
||||
if let solution = viewModel.codeAnswer {
|
||||
HStack {
|
||||
Text(solution)
|
||||
.textSelection(.enabled)
|
||||
.font(.footnote)
|
||||
.monospaced()
|
||||
.lineSpacing(1.2)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxHeight: 600)
|
||||
if let solution = viewModel.codeAnswer {
|
||||
Text(solution)
|
||||
.textSelection(.enabled)
|
||||
.font(.footnote)
|
||||
.monospaced()
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ struct ContentView: View {
|
||||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
if viewModel.authToken != nil {
|
||||
if viewModel.authToken?.isEmpty == false {
|
||||
VStack(spacing: 16) {
|
||||
switch viewModel.downloadState {
|
||||
case .pending:
|
||||
@ -37,7 +37,7 @@ struct ContentView_Previews: PreviewProvider {
|
||||
let viewModel = AppViewModel()
|
||||
viewModel.devices = [CaptureDevice(id: 0, name: "Audio Loopback Device")]
|
||||
viewModel.buttonsAlwaysEnabled = true
|
||||
viewModel.authToken = ""
|
||||
viewModel.authToken = "x"
|
||||
viewModel.downloadState = .completed
|
||||
viewModel.transcript = "So how would we break this app down into components?"
|
||||
viewModel.answer = """
|
||||
@ -52,7 +52,7 @@ Props: message
|
||||
|
||||
• App Component: Renders the Header, Content, and Footer components
|
||||
"""
|
||||
return ContentView(viewModel: viewModel)
|
||||
return ContentView(viewModel: viewModel)
|
||||
.previewLayout(.fixed(width: 300, height: 500))
|
||||
.previewDisplayName("Cheetah")
|
||||
}
|
||||
|
||||
134
LICENSE-3RD-PARTY
Normal file
134
LICENSE-3RD-PARTY
Normal file
@ -0,0 +1,134 @@
|
||||
Copyright (c) 2006-2013 Andy Matuschak.
|
||||
Copyright (c) 2009-2013 Elgato Systems GmbH.
|
||||
Copyright (c) 2011-2014 Kornel Lesiński.
|
||||
Copyright (c) 2015-2017 Mayur Pawashe.
|
||||
Copyright (c) 2014 C.W. Betts.
|
||||
Copyright (c) 2014 Petroules Corporation.
|
||||
Copyright (c) 2014 Big Nerd Ranch.
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
=================
|
||||
EXTERNAL LICENSES
|
||||
=================
|
||||
|
||||
bspatch.c and bsdiff.c, from bsdiff 4.3 <http://www.daemonology.net/bsdiff/>:
|
||||
|
||||
Copyright 2003-2005 Colin Percival
|
||||
All rights reserved
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted providing that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
--
|
||||
|
||||
sais.c and sais.c, from sais-lite (2010/08/07) <https://sites.google.com/site/yuta256/sais>:
|
||||
|
||||
The sais-lite copyright is as follows:
|
||||
|
||||
Copyright (c) 2008-2010 Yuta Mori All Rights Reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--
|
||||
|
||||
Portable C implementation of Ed25519, from https://github.com/orlp/ed25519
|
||||
|
||||
Copyright (c) 2015 Orson Peters <orsonpeters@gmail.com>
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty. In no event will the
|
||||
authors be held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial
|
||||
applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim that you wrote the
|
||||
original software. If you use this software in a product, an acknowledgment in the product
|
||||
documentation would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as
|
||||
being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
--
|
||||
|
||||
SUSignatureVerifier.m:
|
||||
|
||||
Copyright (c) 2011 Mark Hamlin.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted providing that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
@ -12,6 +12,11 @@ public struct OpenAI<T: Payload>: Codable {
|
||||
public let choices: [T]?
|
||||
public let usage: UsageResult?
|
||||
public let data: [T]?
|
||||
public let error: ErrorResult?
|
||||
}
|
||||
|
||||
public struct ErrorResult: Codable, Error {
|
||||
public let message: String
|
||||
}
|
||||
|
||||
public struct TextResult: Payload {
|
||||
|
||||
@ -9,6 +9,15 @@ public enum OpenAIError: Error {
|
||||
case decodingError(error: Error)
|
||||
}
|
||||
|
||||
extension OpenAIError: LocalizedError {
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .genericError(let error), .decodingError(let error):
|
||||
return error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OpenAISwift {
|
||||
fileprivate(set) var token: String?
|
||||
fileprivate let config: Config
|
||||
@ -132,6 +141,9 @@ extension OpenAISwift {
|
||||
let res = try JSONDecoder().decode(OpenAI<MessageResult>.self, from: success)
|
||||
completionHandler(.success(res))
|
||||
} catch {
|
||||
if let resp = String(data: success, encoding: .utf8) {
|
||||
print("Failed to decode response:\n", resp)
|
||||
}
|
||||
completionHandler(.failure(.decodingError(error: error)))
|
||||
}
|
||||
case .failure(let failure):
|
||||
|
||||
12
README.md
12
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
Cheetah is an AI-powered macOS app designed to assist users during remote software engineering interviews by providing real-time, discreet coaching and live coding platform integration.
|
||||
|
||||

|
||||
[Quick demo video (1:28)](https://user-images.githubusercontent.com/106342593/229961889-489e2b36-f3e6-453a-9784-f160bc1c4f8d.mp4)
|
||||
|
||||
<img src="https://github.com/leetcode-mafia/cheetah/raw/91cc5b89864fe28476a7e2062ede2c8322c17896/cheetah.jpg" alt="Screenshot">
|
||||
|
||||
@ -17,6 +17,16 @@ Whisper runs locally on your system, utilizing Georgi Gerganov's [whisper.cpp](h
|
||||
|
||||
## Getting started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Requires macOS 13.1 or later.
|
||||
|
||||
SDL2 must be installed or the app will crash on launch:
|
||||
|
||||
```shell
|
||||
brew install sdl2
|
||||
```
|
||||
|
||||
### Audio driver setup
|
||||
|
||||
For the best results, ensure the audio input captures both sides of the conversation.
|
||||
|
||||
Reference in New Issue
Block a user