fix: ios toolbar alignment and release script hardening

This commit is contained in:
h3p 2026-02-16 22:12:55 +01:00
parent 9a7b086a74
commit 4a9064bfb5
7 changed files with 113 additions and 26 deletions

1
.gitignore vendored
View file

@ -4,6 +4,7 @@
# Xcode
DerivedData/
.DerivedData/
DerivedData-*/
*.xcresult
*.xcuserstate
*.xcuserdata/

View file

@ -358,7 +358,7 @@
CODE_SIGNING_ALLOWED = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 252;
CURRENT_PROJECT_VERSION = 253;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = CS727NF72U;
ENABLE_APP_SANDBOX = YES;
@ -438,7 +438,7 @@
CODE_SIGNING_ALLOWED = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 252;
CURRENT_PROJECT_VERSION = 253;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = CS727NF72U;
ENABLE_APP_SANDBOX = YES;

View file

@ -22,8 +22,8 @@
"appearance" : "dark",
"value" : {
"linear-gradient" : [
"srgb:0.11000,0.11000,0.14000,1.00000",
"srgb:0.02000,0.02000,0.04000,1.00000"
"srgb:0.10980,0.10980,0.11765,1.00000",
"srgb:0.10980,0.10980,0.11765,1.00000"
],
"orientation" : {
"start" : {

View file

@ -92,7 +92,7 @@ extension ContentView {
}
.labelsHidden()
.help("Language")
.frame(width: isIPadToolbarLayout ? 160 : 120)
.frame(width: isIPadToolbarLayout ? 160 : 98)
}
@ViewBuilder
@ -184,6 +184,42 @@ extension ContentView {
.keyboardShortcut("f", modifiers: .command)
}
@ViewBuilder
private var brainDumpControl: some View {
Button(action: {
viewModel.isBrainDumpMode.toggle()
UserDefaults.standard.set(viewModel.isBrainDumpMode, forKey: "BrainDumpModeEnabled")
}) {
Image(systemName: "note.text")
.symbolVariant(viewModel.isBrainDumpMode ? .fill : .none)
}
.help("Brain Dump Mode")
.accessibilityLabel("Brain Dump Mode")
}
@ViewBuilder
private var welcomeTourControl: some View {
Button(action: {
showWelcomeTour = true
}) {
Image(systemName: "sparkles.rectangle.stack")
}
.help("Welcome Tour")
}
@ViewBuilder
private var translucentWindowControl: some View {
Button(action: {
enableTranslucentWindow.toggle()
UserDefaults.standard.set(enableTranslucentWindow, forKey: "EnableTranslucentWindow")
NotificationCenter.default.post(name: .toggleTranslucencyRequested, object: enableTranslucentWindow)
}) {
Image(systemName: enableTranslucentWindow ? "rectangle.fill" : "rectangle")
}
.help("Toggle Translucent Window Background")
.accessibilityLabel("Translucent Window Background")
}
@ViewBuilder
private var iPadPromotedActions: some View {
if iPadPromotedActionsCount >= 1 { openFileControl }
@ -198,6 +234,18 @@ extension ContentView {
@ViewBuilder
private var moreActionsControl: some View {
Menu {
Button(action: {
showSettingsSheet = true
}) {
Label("Settings", systemImage: "gearshape")
}
Button(action: {
requestClearEditorContent()
}) {
Label("Clear Editor", systemImage: "trash")
}
Button(action: { insertTemplateForCurrentLanguage() }) {
Label("Insert Template", systemImage: "doc.badge.plus")
}
@ -261,15 +309,9 @@ extension ContentView {
@ViewBuilder
private var iOSToolbarControls: some View {
languagePickerControl
newTabControl
openFileControl
saveFileControl
findReplaceControl
activeProviderBadgeControl
clearEditorControl
settingsControl
moreActionsControl
}
@ViewBuilder
@ -288,15 +330,19 @@ extension ContentView {
@ToolbarContentBuilder
var editorToolbarContent: some ToolbarContent {
#if os(iOS)
ToolbarItemGroup(placement: .topBarTrailing) {
HStack(spacing: 14) {
if isIPadToolbarLayout {
if isIPadToolbarLayout {
ToolbarItemGroup(placement: .topBarTrailing) {
HStack(spacing: 14) {
iPadDistributedToolbarControls
} else {
iOSToolbarControls
}
.frame(maxWidth: iPadToolbarMaxWidth, alignment: .trailing)
}
} else {
ToolbarItemGroup(placement: .topBarTrailing) {
languagePickerControl
iOSToolbarControls
moreActionsControl
}
.frame(maxWidth: isIPadToolbarLayout ? iPadToolbarMaxWidth : .infinity, alignment: .trailing)
}
#else
ToolbarItemGroup(placement: .automatic) {

View file

@ -564,11 +564,11 @@ func currentEditorTheme(colorScheme: ColorScheme) -> EditorTheme {
comment: comment,
attribute: property,
variable: property,
def: keyword,
def: type,
property: property,
meta: builtin,
tag: keyword,
atom: number,
atom: builtin,
builtin: builtin,
type: type
)

View file

@ -6,7 +6,7 @@ usage() {
Run end-to-end release flow in one command.
Usage:
scripts/release_all.sh <tag> [notarized] [--date YYYY-MM-DD] [--skip-notarized] [--self-hosted] [--github-hosted] [--enterprise-selfhosted] [--dry-run]
scripts/release_all.sh <tag> [notarized] [--date YYYY-MM-DD] [--skip-notarized] [--self-hosted] [--github-hosted] [--enterprise-selfhosted] [--autostash] [--dry-run]
Examples:
scripts/release_all.sh v0.4.9
@ -15,6 +15,7 @@ Examples:
scripts/release_all.sh v0.4.9 --self-hosted
scripts/release_all.sh v0.4.9 --enterprise-selfhosted
scripts/release_all.sh v0.4.9 --github-hosted
scripts/release_all.sh v0.4.9 --autostash
scripts/release_all.sh v0.4.9 --dry-run
What it does:
@ -46,6 +47,7 @@ DATE_ARG=()
TRIGGER_NOTARIZED=1
USE_SELF_HOSTED=0
ENTERPRISE_SELF_HOSTED=0
AUTOSTASH=0
DRY_RUN=0
while [[ "${1:-}" != "" ]]; do
@ -74,6 +76,9 @@ while [[ "${1:-}" != "" ]]; do
ENTERPRISE_SELF_HOSTED=1
USE_SELF_HOSTED=1
;;
--autostash)
AUTOSTASH=1
;;
--dry-run)
DRY_RUN=1
;;
@ -91,6 +96,35 @@ if ! command -v gh >/dev/null 2>&1; then
exit 1
fi
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "This command must run inside a git repository." >&2
exit 1
fi
ROOT="$(git rev-parse --show-toplevel)"
cd "$ROOT"
AUTO_STASHED=0
cleanup_autostash() {
if [[ "$AUTO_STASHED" -eq 1 ]]; then
if git stash pop --index >/dev/null 2>&1; then
echo "Restored stashed working tree changes."
else
echo "Auto-stash restore had conflicts. Changes remain in stash list; resolve manually." >&2
fi
fi
}
trap cleanup_autostash EXIT
if [[ "$AUTOSTASH" -eq 1 ]]; then
if [[ -n "$(git status --porcelain)" ]]; then
STASH_MSG="release_all autostash ${TAG} $(date +%Y-%m-%dT%H:%M:%S)"
git stash push --include-untracked -m "$STASH_MSG" >/dev/null
AUTO_STASHED=1
echo "Auto-stashed dirty working tree before release."
fi
fi
wait_for_pre_release_ci() {
local sha="$1"
local timeout_seconds=1800
@ -133,6 +167,11 @@ wait_for_pre_release_ci() {
return 1
}
if [[ "$AUTOSTASH" -eq 0 && -n "$(git status --porcelain)" ]]; then
echo "Working tree is not clean. Commit/stash changes first, or rerun with --autostash." >&2
exit 1
fi
echo "Verifying release docs are up to date for ${TAG}..."
docs_check_cmd=(scripts/prepare_release_docs.py "$TAG" --check)
if [[ ${#DATE_ARG[@]} -gt 0 ]]; then

View file

@ -80,22 +80,23 @@ if [[ ! -f "$PBXPROJ_FILE" ]]; then
echo "Missing ${PBXPROJ_FILE}; cannot validate MARKETING_VERSION." >&2
exit 1
fi
CURRENT_VERSION="$(
MARKETING_VERSIONS="$(
if command -v rg >/dev/null 2>&1; then
rg --no-filename --only-matching 'MARKETING_VERSION = [0-9]+\.[0-9]+\.[0-9]+' "$PBXPROJ_FILE"
else
grep -Eo 'MARKETING_VERSION = [0-9]+\.[0-9]+\.[0-9]+' "$PBXPROJ_FILE"
fi \
| awk '{print $3}' \
| sort -u \
| head -n1
| sort -u
)"
if [[ -z "${CURRENT_VERSION}" ]]; then
if [[ -z "${MARKETING_VERSIONS}" ]]; then
echo "Could not read MARKETING_VERSION from ${PBXPROJ_FILE}." >&2
exit 1
fi
if [[ "$CURRENT_VERSION" != "$EXPECTED_VERSION" ]]; then
echo "Version mismatch: tag ${TAG} requires MARKETING_VERSION ${EXPECTED_VERSION}, found ${CURRENT_VERSION}." >&2
if ! printf '%s\n' "$MARKETING_VERSIONS" | grep -Fxq "$EXPECTED_VERSION"; then
echo "Version mismatch: tag ${TAG} requires MARKETING_VERSION ${EXPECTED_VERSION}." >&2
echo "Found MARKETING_VERSION values:" >&2
printf ' - %s\n' $MARKETING_VERSIONS >&2
echo "Update MARKETING_VERSION or use a matching tag before running release." >&2
exit 1
fi