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