Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1de0ad519 | ||
|
|
bb6c1cd0b4 | ||
|
|
9278f703e0 | ||
|
|
c54471ee2e |
@@ -5,39 +5,79 @@ import ShotdeckCore
|
|||||||
struct SettingsView: View {
|
struct SettingsView: View {
|
||||||
@Environment(AppModel.self) private var model
|
@Environment(AppModel.self) private var model
|
||||||
|
|
||||||
|
private let labelWidth: CGFloat = 104
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Grid(alignment: .leading, horizontalSpacing: 12, verticalSpacing: 10) {
|
||||||
Section("Hotkey") {
|
GridRow {
|
||||||
LabeledContent("Capture", value: "⌥⇧2 — fixed in this version")
|
Text("Hotkey")
|
||||||
|
.font(.headline)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.gridCellColumns(2)
|
||||||
}
|
}
|
||||||
Section("Folders") {
|
|
||||||
LabeledContent("Watch folder") {
|
GridRow(alignment: .firstTextBaseline) {
|
||||||
HStack(spacing: 8) {
|
fieldLabel("Capture")
|
||||||
Text(model.watchFolderURL.path)
|
Text("⌥⇧2 — fixed in this version")
|
||||||
.lineLimit(1)
|
|
||||||
.truncationMode(.middle)
|
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
Button("Choose…") { model.chooseWatchFolder() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LabeledContent("Output folder") {
|
|
||||||
HStack(spacing: 8) {
|
|
||||||
Text(model.outboxURL.path)
|
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.truncationMode(.middle)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.foregroundStyle(.secondary)
|
.frame(minHeight: 22, alignment: .leading)
|
||||||
Button("Choose…") { model.chooseOutboxFolder() }
|
}
|
||||||
|
|
||||||
|
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() }
|
Button("Reveal spool folder") { model.openSpoolFolder() }
|
||||||
|
.gridCellColumns(2)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.padding(.top, 4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding(16)
|
||||||
.frame(width: 360)
|
.frame(minWidth: 320, idealWidth: 360, maxWidth: 360, alignment: .leading)
|
||||||
.controlSize(.small)
|
.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 {
|
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