Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1de0ad519 | ||
|
|
bb6c1cd0b4 | ||
|
|
9278f703e0 | ||
|
|
c54471ee2e |
@@ -5,39 +5,79 @@ import ShotdeckCore
|
||||
struct SettingsView: View {
|
||||
@Environment(AppModel.self) private var model
|
||||
|
||||
private let labelWidth: CGFloat = 104
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section("Hotkey") {
|
||||
LabeledContent("Capture", value: "⌥⇧2 — fixed in this version")
|
||||
Grid(alignment: .leading, horizontalSpacing: 12, verticalSpacing: 10) {
|
||||
GridRow {
|
||||
Text("Hotkey")
|
||||
.font(.headline)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.gridCellColumns(2)
|
||||
}
|
||||
Section("Folders") {
|
||||
LabeledContent("Watch folder") {
|
||||
HStack(spacing: 8) {
|
||||
Text(model.watchFolderURL.path)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.middle)
|
||||
|
||||
GridRow(alignment: .firstTextBaseline) {
|
||||
fieldLabel("Capture")
|
||||
Text("⌥⇧2 — fixed in this version")
|
||||
.foregroundStyle(.secondary)
|
||||
Button("Choose…") { model.chooseWatchFolder() }
|
||||
}
|
||||
}
|
||||
LabeledContent("Output folder") {
|
||||
HStack(spacing: 8) {
|
||||
Text(model.outboxURL.path)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.middle)
|
||||
.foregroundStyle(.secondary)
|
||||
Button("Choose…") { model.chooseOutboxFolder() }
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.frame(minHeight: 22, alignment: .leading)
|
||||
}
|
||||
|
||||
GridRow {
|
||||
Text("Folders")
|
||||
.font(.headline)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.gridCellColumns(2)
|
||||
.padding(.top, 6)
|
||||
}
|
||||
|
||||
GridRow(alignment: .center) {
|
||||
fieldLabel("Watch folder")
|
||||
folderValue(path: model.watchFolderURL.path) {
|
||||
model.chooseWatchFolder()
|
||||
}
|
||||
}
|
||||
|
||||
GridRow(alignment: .center) {
|
||||
fieldLabel("Output folder")
|
||||
folderValue(path: model.outboxURL.path) {
|
||||
model.chooseOutboxFolder()
|
||||
}
|
||||
Section {
|
||||
}
|
||||
|
||||
GridRow {
|
||||
Button("Reveal spool folder") { model.openSpoolFolder() }
|
||||
.gridCellColumns(2)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.top, 4)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.frame(width: 360)
|
||||
.padding(16)
|
||||
.frame(minWidth: 320, idealWidth: 360, maxWidth: 360, alignment: .leading)
|
||||
.controlSize(.small)
|
||||
}
|
||||
|
||||
private func fieldLabel(_ title: String) -> some View {
|
||||
Text(title)
|
||||
.lineLimit(1)
|
||||
.frame(width: labelWidth, alignment: .trailing)
|
||||
.gridColumnAlignment(.trailing)
|
||||
.frame(minHeight: 22, alignment: .trailing)
|
||||
}
|
||||
|
||||
private func folderValue(path: String, choose: @escaping () -> Void) -> some View {
|
||||
HStack(spacing: 8) {
|
||||
Text(path)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.middle)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
Button("Choose…") { choose() }
|
||||
}
|
||||
.frame(minHeight: 22)
|
||||
}
|
||||
}
|
||||
|
||||
extension AppModel: SettingsWindowPresenting {
|
||||
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
APP_BUNDLE="${ROOT}/.build/Shotdeck.app"
|
||||
STAGING="${ROOT}/.build/dmg-staging"
|
||||
DMG="${ROOT}/.build/Shotdeck.dmg"
|
||||
MOUNT_POINT="${ROOT}/.build/dmg-mnt"
|
||||
|
||||
SKIP_SIGN=0
|
||||
for arg in "$@"; do
|
||||
case "${arg}" in
|
||||
--skip-sign)
|
||||
SKIP_SIGN=1
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: ${arg}" >&2
|
||||
echo "Usage: $0 [--skip-sign]" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "==> Building Shotdeck.app"
|
||||
if [[ "${SKIP_SIGN}" -eq 1 ]]; then
|
||||
./scripts/build-app.sh --skip-sign
|
||||
else
|
||||
./scripts/build-app.sh
|
||||
fi
|
||||
|
||||
if [[ ! -d "${APP_BUNDLE}" ]]; then
|
||||
echo "App bundle not found at ${APP_BUNDLE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Staging DMG contents"
|
||||
rm -rf "${STAGING}"
|
||||
mkdir -p "${STAGING}"
|
||||
ditto "${APP_BUNDLE}" "${STAGING}/Shotdeck.app"
|
||||
ln -s /Applications "${STAGING}/Applications"
|
||||
|
||||
echo "==> Creating ${DMG}"
|
||||
mkdir -p "$(dirname "${DMG}")"
|
||||
hdiutil create -volname "Shotdeck" -srcfolder "${STAGING}" -ov -format UDZO "${DMG}"
|
||||
|
||||
MOUNTED=0
|
||||
detach_dmg() {
|
||||
if [[ "${MOUNTED}" -eq 1 ]]; then
|
||||
hdiutil detach "${MOUNT_POINT}" || hdiutil detach "${MOUNT_POINT}" -force || true
|
||||
MOUNTED=0
|
||||
fi
|
||||
}
|
||||
trap detach_dmg EXIT
|
||||
|
||||
if [[ -d "${MOUNT_POINT}" ]] && /sbin/mount | grep -F -q "${MOUNT_POINT}"; then
|
||||
hdiutil detach "${MOUNT_POINT}" || hdiutil detach "${MOUNT_POINT}" -force
|
||||
fi
|
||||
rm -rf "${MOUNT_POINT}"
|
||||
mkdir -p "${MOUNT_POINT}"
|
||||
|
||||
echo "==> Verifying ${DMG}"
|
||||
hdiutil attach "${DMG}" -nobrowse -readonly -mountpoint "${MOUNT_POINT}"
|
||||
MOUNTED=1
|
||||
|
||||
echo "==> Mount contents"
|
||||
ls -la "${MOUNT_POINT}"
|
||||
|
||||
if [[ ! -d "${MOUNT_POINT}/Shotdeck.app" ]]; then
|
||||
echo "Verification failed: Shotdeck.app missing from mounted DMG" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -L "${MOUNT_POINT}/Applications" ]]; then
|
||||
echo "Verification failed: Applications symlink missing from mounted DMG" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$(readlink "${MOUNT_POINT}/Applications")" != "/Applications" ]]; then
|
||||
echo "Verification failed: Applications does not point at /Applications" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> codesign --verify --deep"
|
||||
codesign --verify --deep --verbose=2 "${MOUNT_POINT}/Shotdeck.app"
|
||||
|
||||
echo "==> Detaching ${MOUNT_POINT}"
|
||||
hdiutil detach "${MOUNT_POINT}"
|
||||
MOUNTED=0
|
||||
trap - EXIT
|
||||
|
||||
SHA256="$(shasum -a 256 "${DMG}" | awk '{print $1}')"
|
||||
|
||||
echo
|
||||
echo "DMG path: ${DMG}"
|
||||
echo "SHA256: ${SHA256}"
|
||||
Reference in New Issue
Block a user