diff --git a/README.md b/README.md index 6076ddd..ace3109 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,96 @@ Introducing Notetaker, an open source Granola alternative for on-device AI meeti - 100% free (bring your own Deepgram & OpenAI API keys) - 100% data privacy (data stored on device) -- 100% open source (please contribute) \ No newline at end of file +- 100% open source (please contribute) + +## Features + +Implemented: + +- Recording mic & system audio +- Live transcript using Deepgram +- Ability to also write down additional notes +- AI generated enhanced notes +- Copy functionality +- Meeting deletion functionality +- Meeting search functionality +- Abilty to edit system prompt +- Use your own API key + +Todo: + +- Add license +- Auto updates +- Auto migration of meeting files (incase of format changes) + +Later: + +- Cool recording indicator (dancing bars) +- Connecting to your Google calendar +- Different note templates +- AI chat for asking questions about a meeting +- Ability to use different models +- Ability to use different STT providers +- Integrations for email, Slack, Notion, etc. + +## Local Development + +Open the project in Xcode + +## Releasing a New Version + +Follow these steps to create a new release with auto-updates: + +### Prerequisites + + + +- Homebrew packages: `brew install create-dmg sparkle` +- Make scripts executable: `chmod +x scripts/update_version.sh scripts/build_release.sh` + +### Release Process + +1. **Update the version number:** + + ```bash + # For bug fixes (1.0 → 1.0.1): + ./scripts/update_version.sh patch + + # For new features (1.0 → 1.1): + ./scripts/update_version.sh minor + + # For major changes (1.0 → 2.0): + ./scripts/update_version.sh major + + # For custom version: + ./scripts/update_version.sh custom 1.2.0 + ``` + +2. **Build the release:** + + ```bash + ./scripts/build_release.sh + ``` + + This will: + + - Clean build the app in Release mode + - Create a signed DMG file + - Generate the appcast.xml for auto-updates + +3. **Create GitHub Release:** + + - Go to [GitHub Releases](https://github.com/owengretzinger/notetaker/releases) + - Click "Create a new release" + - Tag: `v1.0.1` (match the version number) + - Title: `Notetaker v1.0.1` + - Upload the DMG from `releases/` folder + - Write release notes describing changes + +4. **Update appcast:** + + ```bash + git add appcast.xml + git commit -m "Update appcast for v1.0.1" + git push + ``` diff --git a/appcast.xml b/appcast.xml new file mode 100644 index 0000000..2045c93 --- /dev/null +++ b/appcast.xml @@ -0,0 +1,14 @@ + + + + Notetaker + + 1.0 + Fri, 11 Jul 2025 08:36:54 -0400 + 1 + 1.0 + 15.0 + + + + \ No newline at end of file diff --git a/notetaker.xcodeproj/project.pbxproj b/notetaker.xcodeproj/project.pbxproj index a1320eb..38abfa4 100644 --- a/notetaker.xcodeproj/project.pbxproj +++ b/notetaker.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ CC5718382E2020490042BA88 /* OpenAI in Frameworks */ = {isa = PBXBuildFile; productRef = CC5718372E2020490042BA88 /* OpenAI */; }; + CCBA53E72E212F1C00469D84 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = CCBA53E62E212F1C00469D84 /* Sparkle */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -41,6 +42,7 @@ buildActionMask = 2147483647; files = ( CC5718382E2020490042BA88 /* OpenAI in Frameworks */, + CCBA53E72E212F1C00469D84 /* Sparkle in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -84,6 +86,7 @@ name = notetaker; packageProductDependencies = ( CC5718372E2020490042BA88 /* OpenAI */, + CCBA53E62E212F1C00469D84 /* Sparkle */, ); productName = notetaker; productReference = CC5718182E201D8D0042BA88 /* notetaker.app */; @@ -115,6 +118,7 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( CC5718362E2020490042BA88 /* XCRemoteSwiftPackageReference "OpenAI" */, + CCBA53E52E212F1C00469D84 /* XCRemoteSwiftPackageReference "Sparkle" */, ); preferredProjectObjectVersion = 77; productRefGroup = CC5718192E201D8D0042BA88 /* Products */; @@ -359,6 +363,14 @@ minimumVersion = 0.4.4; }; }; + CCBA53E52E212F1C00469D84 /* XCRemoteSwiftPackageReference "Sparkle" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sparkle-project/Sparkle.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.7.1; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -367,6 +379,11 @@ package = CC5718362E2020490042BA88 /* XCRemoteSwiftPackageReference "OpenAI" */; productName = OpenAI; }; + CCBA53E62E212F1C00469D84 /* Sparkle */ = { + isa = XCSwiftPackageProductDependency; + package = CCBA53E52E212F1C00469D84 /* XCRemoteSwiftPackageReference "Sparkle" */; + productName = Sparkle; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = CC5718102E201D8D0042BA88 /* Project object */; diff --git a/notetaker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/notetaker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a5cd505..e76a851 100644 --- a/notetaker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/notetaker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "c351aabe13067917247f7926404d096603b95663248b51124280feb61a2edf30", + "originHash" : "e8774f1c4a54f0b9bb959e13d276fae95c54472a479606f1fd28800bfbcb6da5", "pins" : [ { "identity" : "openai", @@ -10,6 +10,15 @@ "version" : "0.4.4" } }, + { + "identity" : "sparkle", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sparkle-project/Sparkle.git", + "state" : { + "revision" : "df074165274afaa39539c05d57b0832620775b11", + "version" : "2.7.1" + } + }, { "identity" : "swift-http-types", "kind" : "remoteSourceControl", diff --git a/notetaker/Info.plist b/notetaker/Info.plist index f8182fc..73737b7 100644 --- a/notetaker/Info.plist +++ b/notetaker/Info.plist @@ -6,5 +6,7 @@ Notetaker needs access to capture system audio for transcription. NSScreenCaptureUsageDescription Notetaker needs screen recording permission to capture system audio for transcription. + SUFeedURL + https://raw.githubusercontent.com/owengretzinger/notetaker/main/appcast.xml diff --git a/notetaker/notetakerApp.swift b/notetaker/notetakerApp.swift index cfa0174..cc18257 100644 --- a/notetaker/notetakerApp.swift +++ b/notetaker/notetakerApp.swift @@ -6,14 +6,37 @@ // import SwiftUI +import Sparkle @main struct notetakerApp: App { + private let updaterController: SPUStandardUpdaterController + + init() { + updaterController = SPUStandardUpdaterController(updaterDelegate: nil, userDriverDelegate: nil) + } + var body: some Scene { WindowGroup { ContentView() .frame(minWidth: 800, minHeight: 600) } .windowResizability(.contentSize) + .commands { + CommandGroup(after: .appInfo) { + CheckForUpdatesView(updater: updaterController.updater) + } + } + } +} + +struct CheckForUpdatesView: View { + let updater: SPUUpdater + + var body: some View { + Button("Check for Updates...") { + updater.checkForUpdates() + } + .keyboardShortcut("u", modifiers: .command) } } diff --git a/releases/Notetaker-1.0.dmg b/releases/Notetaker-1.0.dmg new file mode 100644 index 0000000..1a8c41e Binary files /dev/null and b/releases/Notetaker-1.0.dmg differ diff --git a/scripts/build_release.sh b/scripts/build_release.sh new file mode 100755 index 0000000..78fdf23 --- /dev/null +++ b/scripts/build_release.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +# Build and Release Script for Notetaker +# This script builds the app, creates a DMG, and generates the appcast + +set -e # Exit on any error + +# Configuration +APP_NAME="Notetaker" +BUNDLE_ID="owen.notetaker" +VERSION=$(grep -m1 "MARKETING_VERSION" notetaker.xcodeproj/project.pbxproj | sed 's/.*= \(.*\);/\1/') + +if [ -z "$VERSION" ]; then + echo "❌ Could not determine version from project file" + echo " Make sure notetaker.xcodeproj/project.pbxproj exists and contains MARKETING_VERSION" + exit 1 +fi +BUILD_DIR="$(pwd)/build" +RELEASES_DIR="$(pwd)/releases" +DMG_NAME="${APP_NAME}-${VERSION}.dmg" + +echo "🚀 Building ${APP_NAME} v${VERSION}..." + +# Clean and build +echo "📦 Building app..." +xcodebuild -project notetaker.xcodeproj -scheme notetaker -configuration Release -derivedDataPath "${BUILD_DIR}" clean build + +# Find the built app +APP_PATH="${BUILD_DIR}/Build/Products/Release/${APP_NAME}.app" + +if [ ! -d "$APP_PATH" ]; then + echo "❌ App not found at $APP_PATH" + exit 1 +fi + +echo "✅ App built successfully at $APP_PATH" + +# Create DMG +echo "📀 Creating DMG..." +mkdir -p "$RELEASES_DIR" + +# Remove old DMG if it exists +if [ -f "$RELEASES_DIR/$DMG_NAME" ]; then + rm "$RELEASES_DIR/$DMG_NAME" +fi + +# Create DMG using create-dmg +create-dmg \ + --volname "$APP_NAME" \ + --window-pos 200 120 \ + --window-size 800 400 \ + --icon-size 100 \ + --icon "$APP_NAME.app" 200 190 \ + --hide-extension "$APP_NAME.app" \ + --app-drop-link 600 185 \ + "$RELEASES_DIR/$DMG_NAME" \ + "$APP_PATH" + +echo "✅ DMG created: $RELEASES_DIR/$DMG_NAME" + +# Generate appcast +echo "📡 Generating appcast..." +/opt/homebrew/Caskroom/sparkle/2.7.1/bin/generate_appcast "$RELEASES_DIR" \ + --download-url-prefix "https://github.com/owengretzinger/notetaker/releases/download/v${VERSION}/" \ + -o "appcast.xml" + +echo "✅ Appcast generated: appcast.xml" + +# Show file sizes +echo "" +echo "📊 Release Summary:" +echo " Version: $VERSION" +echo " DMG: $DMG_NAME ($(du -h "$RELEASES_DIR/$DMG_NAME" | cut -f1))" +echo " Location: $RELEASES_DIR/$DMG_NAME" +echo "" +echo "🎉 Release ready! Next steps:" +echo " 1. Test the DMG on another Mac" +echo " 2. Create a GitHub release with tag v${VERSION}" +echo " 3. Upload the DMG to the GitHub release" +echo " 4. Commit and push the appcast.xml file" +echo " 5. Your users will get auto-update notifications!" \ No newline at end of file diff --git a/scripts/update_version.sh b/scripts/update_version.sh new file mode 100755 index 0000000..6e3a1bf --- /dev/null +++ b/scripts/update_version.sh @@ -0,0 +1,110 @@ +#!/bin/bash + +# Version Update Script for Notetaker +# Usage: ./update_version.sh [major|minor|patch|build] [custom_version] + +set -e + +PROJECT_FILE="notetaker.xcodeproj/project.pbxproj" +CURRENT_MARKETING_VERSION=$(grep -m1 "MARKETING_VERSION" "$PROJECT_FILE" | sed 's/.*= \(.*\);/\1/') +CURRENT_BUILD_VERSION=$(grep -m1 "CURRENT_PROJECT_VERSION" "$PROJECT_FILE" | sed 's/.*= \(.*\);/\1/') + +if [ -z "$CURRENT_MARKETING_VERSION" ] || [ -z "$CURRENT_BUILD_VERSION" ]; then + echo "❌ Could not determine current version from project file" + echo " Make sure $PROJECT_FILE exists and contains MARKETING_VERSION and CURRENT_PROJECT_VERSION" + exit 1 +fi + +echo "📋 Current Version Info:" +echo " Marketing Version: $CURRENT_MARKETING_VERSION" +echo " Build Version: $CURRENT_BUILD_VERSION" +echo "" + +# Function to increment version +increment_version() { + local version=$1 + local type=$2 + + IFS='.' read -ra VERSION_PARTS <<< "$version" + local major=${VERSION_PARTS[0]:-0} + local minor=${VERSION_PARTS[1]:-0} + local patch=${VERSION_PARTS[2]:-0} + + case $type in + "major") + major=$((major + 1)) + minor=0 + patch=0 + ;; + "minor") + minor=$((minor + 1)) + patch=0 + ;; + "patch") + patch=$((patch + 1)) + ;; + *) + echo "❌ Invalid increment type: $type" + exit 1 + ;; + esac + + echo "${major}.${minor}.${patch}" +} + +# Function to update version in project file +update_project_version() { + local new_marketing_version=$1 + local new_build_version=$2 + + # Update marketing version + sed -i '' "s/MARKETING_VERSION = .*;/MARKETING_VERSION = $new_marketing_version;/g" "$PROJECT_FILE" + + # Update build version + sed -i '' "s/CURRENT_PROJECT_VERSION = .*;/CURRENT_PROJECT_VERSION = $new_build_version;/g" "$PROJECT_FILE" + + echo "✅ Updated project file:" + echo " Marketing Version: $CURRENT_MARKETING_VERSION → $new_marketing_version" + echo " Build Version: $CURRENT_BUILD_VERSION → $new_build_version" +} + +# Main logic +case $1 in + "major"|"minor"|"patch") + NEW_MARKETING_VERSION=$(increment_version "$CURRENT_MARKETING_VERSION" "$1") + NEW_BUILD_VERSION=$((CURRENT_BUILD_VERSION + 1)) + update_project_version "$NEW_MARKETING_VERSION" "$NEW_BUILD_VERSION" + ;; + "build") + NEW_BUILD_VERSION=$((CURRENT_BUILD_VERSION + 1)) + update_project_version "$CURRENT_MARKETING_VERSION" "$NEW_BUILD_VERSION" + ;; + "custom") + if [ -z "$2" ]; then + echo "❌ Please provide a custom version number" + echo "Usage: ./update_version.sh custom 1.2.0" + exit 1 + fi + NEW_BUILD_VERSION=$((CURRENT_BUILD_VERSION + 1)) + update_project_version "$2" "$NEW_BUILD_VERSION" + ;; + *) + echo "🔢 Version Update Script" + echo "" + echo "Usage:" + echo " ./update_version.sh major # 1.0 → 2.0" + echo " ./update_version.sh minor # 1.0 → 1.1" + echo " ./update_version.sh patch # 1.0 → 1.0.1" + echo " ./update_version.sh build # Keep version, increment build" + echo " ./update_version.sh custom 1.2.0 # Set specific version" + echo "" + echo "Current version: $CURRENT_MARKETING_VERSION (build $CURRENT_BUILD_VERSION)" + exit 0 + ;; +esac + +echo "" +echo "🎯 Next steps:" +echo " 1. Test the app to make sure everything works" +echo " 2. Run ./build_release.sh to create a release" +echo " 3. The new version will be: $(grep -m1 "MARKETING_VERSION" "$PROJECT_FILE" | sed 's/.*= \(.*\);/\1/')" \ No newline at end of file