Compare commits

...

3 Commits

6 changed files with 136 additions and 14 deletions

3
.gitignore vendored
View File

@@ -81,3 +81,6 @@ build/
# Mac... files
**/.DS_Store
# Releases
releases/

View File

@@ -511,20 +511,14 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = CommandNotch/Resources/CommandNotch.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = G698BP272N;
INFOPLIST_FILE = CommandNotch/Resources/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 0.0.3;
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.app;
PRODUCT_NAME = CommandNotch;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
};
name = Debug;
@@ -616,20 +610,14 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = CommandNotch/Resources/CommandNotch.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = G698BP272N;
INFOPLIST_FILE = CommandNotch/Resources/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 0.0.3;
PRODUCT_BUNDLE_IDENTIFIER = com.commandnotch.app;
PRODUCT_NAME = CommandNotch;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
};
name = Release;

View File

@@ -88,6 +88,7 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
super.init()
terminalView.terminalDelegate = self
installOsc52ClipboardHandler()
let font = NSFont.monospacedSystemFont(ofSize: fontSize, weight: .regular)
terminalView.font = font
@@ -153,6 +154,23 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
return ProcessInfo.processInfo.environment["SHELL"] ?? "/bin/zsh"
}
private func installOsc52ClipboardHandler() {
let maxPayloadSize = 1_048_576 // 1 MB
terminalView.getTerminal().registerOscHandler(code: 52) { [weak self] data in
guard data.count >= 2,
data[data.startIndex] == UInt8(ascii: "c"),
data[data.startIndex + 1] == UInt8(ascii: ";") else { return }
let base64 = Data(data[(data.startIndex + 2)...])
guard let content = Data(base64Encoded: base64),
content.count <= maxPayloadSize,
let string = String(data: content, encoding: .utf8) else { return }
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(string, forType: .string)
}
}
private func installCommandArrowMonitor() {
keyEventMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
guard let self else { return event }
@@ -200,7 +218,29 @@ class TerminalSession: NSObject, ObservableObject, LocalProcessDelegate, @precon
// MARK: - LocalProcessDelegate
nonisolated func processTerminated(_ source: LocalProcess, exitCode: Int32?) {
Task { @MainActor in self.isRunning = false }
Task { @MainActor in
self.isRunning = false
self.resetTerminalModes()
}
}
private func resetTerminalModes() {
let resetSequences: [[UInt8]] = [
Array("\u{1b}[?9l".utf8),
Array("\u{1b}[?1000l".utf8),
Array("\u{1b}[?1002l".utf8),
Array("\u{1b}[?1003l".utf8),
Array("\u{1b}[?1006l".utf8),
Array("\u{1b}[?1015l".utf8),
Array("\u{1b}[?2004l".utf8),
Array("\u{1b}[?1l".utf8),
Array("\u{1b}[?1049l".utf8),
Array("\u{1b}[?25h".utf8),
]
for seq in resetSequences {
terminalView.feed(byteArray: seq[...])
}
}
nonisolated func dataReceived(slice: ArraySlice<UInt8>) {

View File

@@ -63,6 +63,7 @@ Click the preview above to watch the demo recording.
- macOS 14 or later
- Xcode 16 or later
- Homebrew `xcodegen`
- Homebrew `create-dmg` for release `.dmg` packaging
### Build
@@ -81,6 +82,33 @@ DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \
xcodebuild build -project CommandNotch.xcodeproj -scheme CommandNotch -destination 'platform=macOS'
```
### Build a release `.dmg`
Use `create-dmg` to build the styled Finder installer window with the usual drag-to-`Applications` layout.
Install the packaging dependency once:
```bash
brew install create-dmg
```
Then build from the `app/` directory:
```bash
./scripts/build-release-dmg.sh
```
That produces:
- `releases/CommandNotch YYYY-MM-DD HH-MM-SS/CommandNotch.app`
- `releases/CommandNotch YYYY-MM-DD HH-MM-SS/CommandNotch.dmg`
Notes:
- The script regenerates the Xcode project, archives the Release build, then packages the archived app into a styled `.dmg`.
- The archive is written to `/tmp` and is only used as the source for the exported `.app`.
- If you want a distributable build signed with a specific identity, make sure your Xcode signing settings are configured before running the archive step.
## Project Layout
```text

63
scripts/build-release-dmg.sh Executable file
View File

@@ -0,0 +1,63 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
PROJECT_DIR="$APP_ROOT/CommandNotch"
if ! command -v xcodegen >/dev/null 2>&1; then
echo "error: xcodegen is required. Install it with: brew install xcodegen" >&2
exit 1
fi
if ! command -v create-dmg >/dev/null 2>&1; then
echo "error: create-dmg is required. Install it with: brew install create-dmg" >&2
exit 1
fi
timestamp="$(date '+%Y-%m-%d %H-%M-%S')"
release_dir="$APP_ROOT/releases/CommandNotch $timestamp"
archive_path="/tmp/CommandNotch-$timestamp.xcarchive"
staging_dir="$(mktemp -d)"
app_path="$release_dir/CommandNotch.app"
dmg_path="$release_dir/CommandNotch.dmg"
cleanup() {
rm -rf "$staging_dir"
}
trap cleanup EXIT
mkdir -p "$release_dir"
cd "$PROJECT_DIR"
xcodegen generate --spec project.yml
DEVELOPER_DIR="${DEVELOPER_DIR:-/Applications/Xcode.app/Contents/Developer}" \
xcodebuild archive \
-project CommandNotch.xcodeproj \
-scheme CommandNotch \
-configuration Release \
-destination 'generic/platform=macOS' \
-archivePath "$archive_path"
ditto "$archive_path/Products/Applications/CommandNotch.app" "$app_path"
ditto "$app_path" "$staging_dir/CommandNotch.app"
ln -s /Applications "$staging_dir/Applications"
create-dmg \
--volname "CommandNotch" \
--window-pos 200 120 \
--window-size 720 420 \
--icon-size 128 \
--icon "CommandNotch.app" 180 210 \
--icon "Applications" 540 210 \
--hide-extension "CommandNotch.app" \
--app-drop-link 540 210 \
"$dmg_path" \
"$staging_dir"
echo "Created:"
echo " $app_path"
echo " $dmg_path"