@leejungkiin/awkit 1.7.0 → 1.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/awk.js +576 -84
- package/core/CLAUDE.md +1 -1
- package/core/GEMINI.md +148 -167
- package/core/GEMINI.md.bak +149 -116
- package/core/skill-runtime-manifest.json +3 -0
- package/docs/Claude Fable 5.md +3826 -0
- package/docs/android_kotlin_system_instruction.md +210 -0
- package/docs/brainstorm_ponytail_integration.md +146 -0
- package/docs/brainstorm_smart_setup.md +113 -0
- package/docs/deep-research-report (1).md +293 -0
- package/docs/history/GEMINI.v1.md +135 -0
- package/docs/history/brainstorm_antigravity_unified_architecture.v1.md +105 -0
- package/docs/history/implementation_plan.v1.md +58 -0
- package/package.json +4 -1
- package/scripts/artifact-storage.js +130 -0
- package/scripts/automation-gate.js +40 -7
- package/scripts/claude-plan.js +76 -0
- package/scripts/dependency-manager.js +210 -0
- package/scripts/exec-rtk.js +11 -5
- package/scripts/i18n-helper.js +381 -0
- package/scripts/multi-model-pipeline.js +144 -0
- package/skill-packs/mobile-ios/pack.json +4 -2
- package/skill-packs/reverse-engineering/pack.json +1 -0
- package/skills/CATALOG.md +20 -0
- package/skills/GEMINI.md +9 -1
- package/skills/TRIGGER_INDEX.md +10 -0
- package/skills/ai-music/SKILL.md +275 -0
- package/skills/android-re-analyzer/SKILL.md +238 -0
- package/skills/android-re-analyzer/references/api-extraction-patterns.md +119 -0
- package/skills/android-re-analyzer/references/call-flow-analysis.md +176 -0
- package/skills/android-re-analyzer/references/fernflower-usage.md +115 -0
- package/skills/android-re-analyzer/references/jadx-usage.md +116 -0
- package/skills/android-re-analyzer/references/setup-guide.md +221 -0
- package/skills/android-re-analyzer/scripts/check-deps.sh +129 -0
- package/skills/android-re-analyzer/scripts/decompile.sh +375 -0
- package/skills/android-re-analyzer/scripts/find-api-calls.sh +118 -0
- package/skills/android-re-analyzer/scripts/install-dep.sh +448 -0
- package/skills/animal-island-ui-style/SKILL.md +1450 -0
- package/skills/app-store-review-agent/SKILL.md +164 -0
- package/skills/app-store-review-agent/references/guidelines/README.md +154 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/ai_apps.md +37 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/all_apps.md +50 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/crypto_finance.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/games.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/health_fitness.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/kids.md +27 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/macos.md +38 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/social_ugc.md +32 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/subscription_iap.md +34 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/vpn.md +18 -0
- package/skills/app-store-review-agent/references/rules/design/minimum_functionality.md +96 -0
- package/skills/app-store-review-agent/references/rules/design/sign_in_with_apple.md +54 -0
- package/skills/app-store-review-agent/references/rules/entitlements/unused_entitlements.md +83 -0
- package/skills/app-store-review-agent/references/rules/metadata/accurate_metadata.md +54 -0
- package/skills/app-store-review-agent/references/rules/metadata/apple_trademark.md +99 -0
- package/skills/app-store-review-agent/references/rules/metadata/china_storefront.md +72 -0
- package/skills/app-store-review-agent/references/rules/metadata/competitor_terms.md +56 -0
- package/skills/app-store-review-agent/references/rules/metadata/subscription_metadata.md +81 -0
- package/skills/app-store-review-agent/references/rules/privacy/privacy_manifest.md +84 -0
- package/skills/app-store-review-agent/references/rules/privacy/unnecessary_data.md +60 -0
- package/skills/app-store-review-agent/references/rules/subscription/misleading_pricing.md +63 -0
- package/skills/app-store-review-agent/references/rules/subscription/missing_tos_pp.md +54 -0
- package/skills/awf-ponytail/SKILL.md +91 -0
- package/skills/awf-ponytail-review/SKILL.md +67 -0
- package/skills/awf-session-restore/SKILL.md +3 -3
- package/skills/brainstorm-agent/SKILL.md +11 -2
- package/skills/brainstorm-agent/templates/brief-template.md +8 -0
- package/skills/claude-planner/SKILL.md +47 -0
- package/skills/code-review/SKILL.md +87 -0
- package/skills/expo-game-development/SKILL.md +163 -0
- package/skills/flutter/LICENSE.txt +202 -0
- package/skills/flutter/SKILL.md +127 -0
- package/skills/flutter-project-creater/LICENSE.txt +202 -0
- package/skills/flutter-project-creater/SKILL.md +106 -0
- package/skills/game-developer/SKILL.md +163 -0
- package/skills/game-developer/references/ecs-patterns.md +501 -0
- package/skills/game-developer/references/multiplayer-networking.md +475 -0
- package/skills/game-developer/references/performance-optimization.md +422 -0
- package/skills/game-developer/references/unity-patterns.md +271 -0
- package/skills/game-developer/references/unreal-cpp.md +352 -0
- package/skills/generate-gui-assets/SKILL.md +305 -0
- package/skills/generate-gui-assets/agents/openai.yaml +4 -0
- package/skills/generate-gui-assets/references/catalog-schema.md +58 -0
- package/skills/generate-gui-assets/references/extraction-techniques.md +21 -0
- package/skills/generate-gui-assets/references/prompt-patterns.md +58 -0
- package/skills/generate-gui-assets/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
- package/skills/generate-gui-assets/scripts/build_gui_contact_sheet.py +51 -0
- package/skills/generate-gui-assets/scripts/clean_chroma_edges.py +262 -0
- package/skills/generate-gui-assets/scripts/copy_approved_icons.py +64 -0
- package/skills/generate-gui-assets/scripts/prepare_gui_asset_run.py +91 -0
- package/skills/generate-gui-assets/scripts/suggest_grid_options.py +63 -0
- package/skills/generate-gui-assets/scripts/validate_gui_catalog.py +50 -0
- package/skills/godot-game-development/SKILL.md +142 -0
- package/skills/hatch-pet/LICENSE.txt +201 -0
- package/skills/hatch-pet/SKILL.md +420 -0
- package/skills/hatch-pet/agents/openai.yaml +4 -0
- package/skills/hatch-pet/references/animation-rows.md +29 -0
- package/skills/hatch-pet/references/codex-pet-contract.md +35 -0
- package/skills/hatch-pet/references/qa-rubric.md +60 -0
- package/skills/hatch-pet/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
- package/skills/hatch-pet/scripts/clean_chroma_edges.py +262 -0
- package/skills/hatch-pet/scripts/compose_atlas.py +150 -0
- package/skills/hatch-pet/scripts/derive_running_left_from_running_right.py +143 -0
- package/skills/hatch-pet/scripts/extract_strip_frames.py +323 -0
- package/skills/hatch-pet/scripts/finalize_pet_run.py +382 -0
- package/skills/hatch-pet/scripts/generate_pet_images.py +287 -0
- package/skills/hatch-pet/scripts/inspect_frames.py +246 -0
- package/skills/hatch-pet/scripts/make_contact_sheet.py +96 -0
- package/skills/hatch-pet/scripts/package_custom_pet.py +108 -0
- package/skills/hatch-pet/scripts/pet_job_status.py +117 -0
- package/skills/hatch-pet/scripts/prepare_pet_run.py +673 -0
- package/skills/hatch-pet/scripts/queue_pet_repairs.py +172 -0
- package/skills/hatch-pet/scripts/record_imagegen_result.py +250 -0
- package/skills/hatch-pet/scripts/render_animation_videos.py +134 -0
- package/skills/hatch-pet/scripts/render_animation_videos.sh +5 -0
- package/skills/hatch-pet/scripts/validate_atlas.py +139 -0
- package/skills/i18n-orchestrator/SKILL.md +37 -0
- package/skills/ios-simulator-skill/SKILL.md +390 -0
- package/skills/ios-simulator-skill/scripts/accessibility_audit.py +300 -0
- package/skills/ios-simulator-skill/scripts/app_launcher.py +326 -0
- package/skills/ios-simulator-skill/scripts/app_state_capture.py +400 -0
- package/skills/ios-simulator-skill/scripts/appearance.py +385 -0
- package/skills/ios-simulator-skill/scripts/build_and_test.py +348 -0
- package/skills/ios-simulator-skill/scripts/clipboard.py +103 -0
- package/skills/ios-simulator-skill/scripts/common/__init__.py +61 -0
- package/skills/ios-simulator-skill/scripts/common/cache_utils.py +289 -0
- package/skills/ios-simulator-skill/scripts/common/device_utils.py +462 -0
- package/skills/ios-simulator-skill/scripts/common/env_config.py +35 -0
- package/skills/ios-simulator-skill/scripts/common/hang_pipeline.py +862 -0
- package/skills/ios-simulator-skill/scripts/common/hang_sessions.py +490 -0
- package/skills/ios-simulator-skill/scripts/common/idb_utils.py +180 -0
- package/skills/ios-simulator-skill/scripts/common/screenshot_utils.py +338 -0
- package/skills/ios-simulator-skill/scripts/container.py +668 -0
- package/skills/ios-simulator-skill/scripts/gesture.py +394 -0
- package/skills/ios-simulator-skill/scripts/hang_watcher.py +1533 -0
- package/skills/ios-simulator-skill/scripts/keyboard.py +391 -0
- package/skills/ios-simulator-skill/scripts/localization_audit.py +483 -0
- package/skills/ios-simulator-skill/scripts/location.py +467 -0
- package/skills/ios-simulator-skill/scripts/log_monitor.py +493 -0
- package/skills/ios-simulator-skill/scripts/model_inspector.py +645 -0
- package/skills/ios-simulator-skill/scripts/navigator.py +461 -0
- package/skills/ios-simulator-skill/scripts/privacy_manager.py +310 -0
- package/skills/ios-simulator-skill/scripts/push_notification.py +240 -0
- package/skills/ios-simulator-skill/scripts/screen_mapper.py +296 -0
- package/skills/ios-simulator-skill/scripts/sim_health_check.sh +245 -0
- package/skills/ios-simulator-skill/scripts/sim_list.py +299 -0
- package/skills/ios-simulator-skill/scripts/simctl_boot.py +312 -0
- package/skills/ios-simulator-skill/scripts/simctl_create.py +316 -0
- package/skills/ios-simulator-skill/scripts/simctl_delete.py +357 -0
- package/skills/ios-simulator-skill/scripts/simctl_erase.py +351 -0
- package/skills/ios-simulator-skill/scripts/simctl_shutdown.py +290 -0
- package/skills/ios-simulator-skill/scripts/simulator_selector.py +375 -0
- package/skills/ios-simulator-skill/scripts/status_bar.py +250 -0
- package/skills/ios-simulator-skill/scripts/test_recorder.py +323 -0
- package/skills/ios-simulator-skill/scripts/visual_diff.py +235 -0
- package/skills/ios-simulator-skill/scripts/xcode/__init__.py +13 -0
- package/skills/ios-simulator-skill/scripts/xcode/builder.py +397 -0
- package/skills/ios-simulator-skill/scripts/xcode/cache.py +204 -0
- package/skills/ios-simulator-skill/scripts/xcode/config.py +178 -0
- package/skills/ios-simulator-skill/scripts/xcode/reporter.py +343 -0
- package/skills/ios-simulator-skill/scripts/xcode/xcresult.py +451 -0
- package/skills/ios-visual-qa-strategist/SKILL.md +111 -0
- package/skills/ios-visual-qa-strategist/agents/openai.yaml +4 -0
- package/skills/ios-visual-qa-strategist/references/ios-tool-selection.md +61 -0
- package/skills/ios-visual-qa-strategist/references/minimal-capture-policy.md +56 -0
- package/skills/ios-visual-qa-strategist/references/visual-reasoning-heuristics.md +53 -0
- package/skills/orchestrator/SKILL.md +0 -20
- package/skills/persistent-storage/SKILL.md +55 -0
- package/skills/short-maker/SKILL.md +23 -0
- package/skills/short-maker/scripts/effects.js +56 -0
- package/skills/short-maker/scripts/shortmaker-bridge.js +332 -0
- package/skills/short-maker/scripts/videomix.js +601 -0
- package/skills/short-maker/templates/hyperframes/cinematic-character.template.html +172 -0
- package/skills/short-maker/templates/hyperframes/index.template.html +194 -0
- package/skills/smali-to-kotlin/SKILL.md +128 -0
- package/skills/smali-to-kotlin/examples/getting-started/tech-stack.md +58 -0
- package/skills/smali-to-kotlin/examples/pipeline/data-ui-parity.md +118 -0
- package/skills/smali-to-kotlin/examples/pipeline/scanner-and-bootstrap.md +106 -0
- package/skills/smali-to-kotlin/library-patterns.md +189 -0
- package/skills/smali-to-kotlin/phase-0-discovery.md +128 -0
- package/skills/smali-to-kotlin/phase-1-architecture.md +166 -0
- package/skills/smali-to-kotlin/phase-2-blueprint-ui.md +347 -0
- package/skills/smali-to-kotlin/phase-2-blueprint.md +228 -0
- package/skills/smali-to-kotlin/phase-3-build.md +248 -0
- package/skills/smali-to-kotlin/phase-3-logic-build.md +268 -0
- package/skills/smali-to-kotlin/smali-reading-guide.md +310 -0
- package/skills/smali-to-kotlin/templates/app-map.md +101 -0
- package/skills/smali-to-kotlin/templates/architecture.md +142 -0
- package/skills/smali-to-kotlin/templates/blueprint.md +145 -0
- package/skills/spec-gate/SKILL.md +6 -2
- package/skills/symphony-enforcer/SKILL.md +8 -0
- package/skills/symphony-enforcer/examples/mindful-stop.md +2 -0
- package/skills/symphony-enforcer/examples/three-phase.md +16 -0
- package/skills/symphony-enforcer/examples/trigger-points.md +7 -1
- package/skills/unity-game-development/SKILL.md +231 -0
- package/skills/verification-gate/SKILL.md +4 -2
- package/skills/video-edit/SKILL.md +36 -0
- package/skills/video-edit/scripts/video_edit.py +324 -0
- package/templates/setup-mapping.json +48 -0
- package/templates/specs/design-template.md +161 -71
- package/templates/specs/requirements-template.md +65 -133
- package/templates/specs/task-spec-template.xml +3 -0
- package/workflows/_uncategorized/critic.md +40 -0
- package/workflows/_uncategorized/git-rebase-flow.md +81 -0
- package/workflows/_uncategorized/image-gen.md +118 -0
- package/workflows/_uncategorized/multi-model-pipeline.md +60 -0
- package/workflows/_uncategorized/pixel-gen.md +86 -0
- package/workflows/_uncategorized/pixel-setup.md +90 -0
- package/workflows/_uncategorized/ponytail-review.md +59 -0
- package/workflows/_uncategorized/reverse-android-build.md +222 -0
- package/workflows/_uncategorized/reverse-android-design.md +139 -0
- package/workflows/_uncategorized/reverse-android-discover.md +150 -0
- package/workflows/_uncategorized/reverse-android-scan.md +158 -0
- package/workflows/_uncategorized/reverse-android.md +143 -0
- package/workflows/_uncategorized/reverse-ios-build.md +240 -0
- package/workflows/_uncategorized/reverse-ios-design.md +112 -0
- package/workflows/_uncategorized/reverse-ios-discover.md +120 -0
- package/workflows/_uncategorized/reverse-ios-scan.md +155 -0
- package/workflows/_uncategorized/reverse-ios.md +152 -0
- package/workflows/_uncategorized/safety-router.md +34 -0
- package/workflows/_uncategorized/teach.md +89 -0
- package/workflows/_uncategorized/verify-ui.md +53 -0
- package/workflows/_uncategorized/visualize-screenshots.md +34 -0
- package/workflows/ads/ads-analyst.md +201 -0
- package/workflows/ads/ads-audit.md +106 -0
- package/workflows/ads/ads-optimize.md +97 -0
- package/workflows/ads/ads-targeting.md +241 -0
- package/workflows/ads/adsExpert.md +160 -0
- package/workflows/ads/smali-ads-config.md +400 -0
- package/workflows/ads/smali-ads-flow.md +331 -0
- package/workflows/ads/smali-ads-interstitial.md +377 -0
- package/workflows/ads/smali-ads-native.md +382 -0
- package/workflows/context/teach.md +89 -0
- package/workflows/gitnexus.md +8 -8
- package/workflows/lifecycle/brainstorm.md +43 -0
- package/workflows/lifecycle/code.md +5 -0
- package/workflows/lifecycle/init.md +23 -5
- package/workflows/lifecycle/multi-model-pipeline.md +60 -0
- package/workflows/quality/ponytail-review.md +59 -0
- package/workflows/roles/critic.md +40 -0
- package/workflows/roles/safety-router.md +34 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
iOS Simulator Appearance Manager
|
|
4
|
+
|
|
5
|
+
Control dark mode, dynamic type size, locale, and region.
|
|
6
|
+
Wraps xcrun simctl ui and defaults write for appearance testing.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python scripts/appearance.py --theme dark
|
|
10
|
+
python scripts/appearance.py --text-size AX3
|
|
11
|
+
python scripts/appearance.py --locale ar --region SA --bundle-id com.myapp
|
|
12
|
+
python scripts/appearance.py --reset
|
|
13
|
+
|
|
14
|
+
RTL locales: ar, he, fa, ur, yi (app must restart to reflow layout).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
|
|
22
|
+
from common import resolve_udid
|
|
23
|
+
|
|
24
|
+
# === CONSTANTS ===
|
|
25
|
+
|
|
26
|
+
# Map friendly size aliases to xcrun simctl content_size tokens
|
|
27
|
+
TEXT_SIZE_MAP: dict[str, str] = {
|
|
28
|
+
"XS": "extra-small",
|
|
29
|
+
"S": "small",
|
|
30
|
+
"M": "medium",
|
|
31
|
+
"L": "large",
|
|
32
|
+
"XL": "extra-large",
|
|
33
|
+
"XXL": "extra-extra-large",
|
|
34
|
+
"XXXL": "extra-extra-extra-large",
|
|
35
|
+
"AX1": "accessibility-medium",
|
|
36
|
+
"AX2": "accessibility-large",
|
|
37
|
+
"AX3": "accessibility-extra-large",
|
|
38
|
+
"AX4": "accessibility-extra-extra-large",
|
|
39
|
+
"AX5": "accessibility-extra-extra-extra-large",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Locales that require RTL layout direction
|
|
43
|
+
RTL_LOCALES: frozenset[str] = frozenset({"ar", "he", "fa", "ur", "yi"})
|
|
44
|
+
|
|
45
|
+
# Default appearance values used by --reset
|
|
46
|
+
DEFAULT_THEME = "light"
|
|
47
|
+
DEFAULT_TEXT_SIZE = "M"
|
|
48
|
+
DEFAULT_LOCALE = "en"
|
|
49
|
+
DEFAULT_REGION = "US"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# === APPEARANCE MANAGER ===
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class AppearanceManager:
|
|
56
|
+
"""Manages iOS simulator appearance: theme, dynamic type, and locale."""
|
|
57
|
+
|
|
58
|
+
def __init__(self, udid: str | None = None):
|
|
59
|
+
"""Initialize appearance manager.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
udid: Optional device UDID (auto-detects booted simulator if None)
|
|
63
|
+
"""
|
|
64
|
+
self.udid = udid
|
|
65
|
+
|
|
66
|
+
# === PUBLIC API ===
|
|
67
|
+
|
|
68
|
+
def set_theme(self, theme: str) -> tuple[bool, str]:
|
|
69
|
+
"""Switch simulator between light and dark appearance.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
theme: 'light' or 'dark'
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
(success, message)
|
|
76
|
+
"""
|
|
77
|
+
cmd = ["xcrun", "simctl", "ui", self.udid, "appearance", theme]
|
|
78
|
+
return self._run(cmd, f"Theme set: {theme}")
|
|
79
|
+
|
|
80
|
+
def set_text_size(self, alias: str) -> tuple[bool, str]:
|
|
81
|
+
"""Set dynamic type content size.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
alias: Friendly alias (XS, S, M, L, XL, XXL, XXXL, AX1-AX5)
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
(success, message)
|
|
88
|
+
"""
|
|
89
|
+
token = TEXT_SIZE_MAP.get(alias.upper())
|
|
90
|
+
if not token:
|
|
91
|
+
valid = ", ".join(TEXT_SIZE_MAP.keys())
|
|
92
|
+
return False, f"Unknown text size '{alias}'. Valid: {valid}"
|
|
93
|
+
|
|
94
|
+
cmd = ["xcrun", "simctl", "ui", self.udid, "content_size", token]
|
|
95
|
+
return self._run(cmd, f"Text size set: {alias} ({token})")
|
|
96
|
+
|
|
97
|
+
def set_locale(
|
|
98
|
+
self,
|
|
99
|
+
locale: str,
|
|
100
|
+
region: str | None = None,
|
|
101
|
+
bundle_id: str | None = None,
|
|
102
|
+
) -> tuple[bool, str]:
|
|
103
|
+
"""Write AppleLanguages and AppleLocale to simulator global defaults.
|
|
104
|
+
|
|
105
|
+
Optionally restarts a specific app via bundle ID so the locale takes
|
|
106
|
+
effect immediately. Without --bundle-id the change applies on next
|
|
107
|
+
cold app launch.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
locale: BCP-47 language code (e.g. 'en', 'ar', 'de')
|
|
111
|
+
region: ISO 3166-1 alpha-2 region code (e.g. 'US', 'SA', 'IE')
|
|
112
|
+
bundle_id: Optional app bundle ID — triggers terminate + launch
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
(success, message)
|
|
116
|
+
"""
|
|
117
|
+
apple_locale = f"{locale}_{region}" if region else locale
|
|
118
|
+
|
|
119
|
+
# Write AppleLanguages array
|
|
120
|
+
lang_cmd = [
|
|
121
|
+
"xcrun",
|
|
122
|
+
"simctl",
|
|
123
|
+
"spawn",
|
|
124
|
+
self.udid,
|
|
125
|
+
"defaults",
|
|
126
|
+
"write",
|
|
127
|
+
"-g",
|
|
128
|
+
"AppleLanguages",
|
|
129
|
+
"-array",
|
|
130
|
+
locale,
|
|
131
|
+
]
|
|
132
|
+
ok, msg = self._run(lang_cmd, "")
|
|
133
|
+
if not ok:
|
|
134
|
+
return False, f"Failed to write AppleLanguages: {msg}"
|
|
135
|
+
|
|
136
|
+
# Write AppleLocale string
|
|
137
|
+
locale_cmd = [
|
|
138
|
+
"xcrun",
|
|
139
|
+
"simctl",
|
|
140
|
+
"spawn",
|
|
141
|
+
self.udid,
|
|
142
|
+
"defaults",
|
|
143
|
+
"write",
|
|
144
|
+
"-g",
|
|
145
|
+
"AppleLocale",
|
|
146
|
+
"-string",
|
|
147
|
+
apple_locale,
|
|
148
|
+
]
|
|
149
|
+
ok, msg = self._run(locale_cmd, "")
|
|
150
|
+
if not ok:
|
|
151
|
+
return False, f"Failed to write AppleLocale: {msg}"
|
|
152
|
+
|
|
153
|
+
is_rtl = locale in RTL_LOCALES
|
|
154
|
+
rtl_note = " [RTL layout]" if is_rtl else ""
|
|
155
|
+
summary = f"Locale set: {apple_locale}{rtl_note}"
|
|
156
|
+
|
|
157
|
+
if bundle_id:
|
|
158
|
+
restart_ok, restart_msg = self._restart_app(bundle_id)
|
|
159
|
+
if restart_ok:
|
|
160
|
+
summary += f" — app restarted: {bundle_id}"
|
|
161
|
+
else:
|
|
162
|
+
summary += f" — locale written but app restart failed: {restart_msg}"
|
|
163
|
+
else:
|
|
164
|
+
summary += " — restart app to apply"
|
|
165
|
+
|
|
166
|
+
return True, summary
|
|
167
|
+
|
|
168
|
+
def reset(self) -> tuple[bool, str]:
|
|
169
|
+
"""Reset theme, text size, and locale to system defaults.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
(success, message)
|
|
173
|
+
"""
|
|
174
|
+
results: list[str] = []
|
|
175
|
+
errors: list[str] = []
|
|
176
|
+
|
|
177
|
+
ok, msg = self.set_theme(DEFAULT_THEME)
|
|
178
|
+
(results if ok else errors).append(msg)
|
|
179
|
+
|
|
180
|
+
ok, msg = self.set_text_size(DEFAULT_TEXT_SIZE)
|
|
181
|
+
(results if ok else errors).append(msg)
|
|
182
|
+
|
|
183
|
+
ok, msg = self.set_locale(DEFAULT_LOCALE, DEFAULT_REGION)
|
|
184
|
+
(results if ok else errors).append(msg)
|
|
185
|
+
|
|
186
|
+
if errors:
|
|
187
|
+
return False, f"Reset partial — errors: {'; '.join(errors)}"
|
|
188
|
+
|
|
189
|
+
return True, "Appearance reset to defaults (light / M / en_US)"
|
|
190
|
+
|
|
191
|
+
# === PRIVATE HELPERS ===
|
|
192
|
+
|
|
193
|
+
def _run(self, cmd: list[str], success_message: str) -> tuple[bool, str]:
|
|
194
|
+
"""Run subprocess command and return (success, message).
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
cmd: Command list (never shell=True)
|
|
198
|
+
success_message: Human-readable success summary
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
(success, message)
|
|
202
|
+
"""
|
|
203
|
+
try:
|
|
204
|
+
subprocess.run(
|
|
205
|
+
cmd,
|
|
206
|
+
capture_output=True,
|
|
207
|
+
text=True,
|
|
208
|
+
check=True,
|
|
209
|
+
)
|
|
210
|
+
return True, success_message
|
|
211
|
+
except subprocess.CalledProcessError as error:
|
|
212
|
+
stderr = error.stderr.strip() if error.stderr else "unknown error"
|
|
213
|
+
return False, stderr
|
|
214
|
+
|
|
215
|
+
def _restart_app(self, bundle_id: str) -> tuple[bool, str]:
|
|
216
|
+
"""Terminate then launch an app by bundle ID.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
bundle_id: App bundle ID
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
(success, message)
|
|
223
|
+
"""
|
|
224
|
+
terminate_cmd = ["xcrun", "simctl", "terminate", self.udid, bundle_id]
|
|
225
|
+
# Terminate may fail if app is not running — that is acceptable
|
|
226
|
+
subprocess.run(terminate_cmd, capture_output=True, check=False)
|
|
227
|
+
|
|
228
|
+
launch_cmd = ["xcrun", "simctl", "launch", self.udid, bundle_id]
|
|
229
|
+
try:
|
|
230
|
+
subprocess.run(launch_cmd, capture_output=True, text=True, check=True)
|
|
231
|
+
return True, f"Launched {bundle_id}"
|
|
232
|
+
except subprocess.CalledProcessError as error:
|
|
233
|
+
stderr = error.stderr.strip() if error.stderr else "launch failed"
|
|
234
|
+
return False, stderr
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# === CLI ===
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def main() -> None:
|
|
241
|
+
"""Main entry point."""
|
|
242
|
+
parser = argparse.ArgumentParser(
|
|
243
|
+
description=(
|
|
244
|
+
"iOS Simulator Appearance Manager — control dark mode, "
|
|
245
|
+
"dynamic type, locale, and region."
|
|
246
|
+
),
|
|
247
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
248
|
+
epilog="""
|
|
249
|
+
Examples:
|
|
250
|
+
python scripts/appearance.py --theme dark
|
|
251
|
+
python scripts/appearance.py --text-size AX3
|
|
252
|
+
python scripts/appearance.py --locale ar --region SA
|
|
253
|
+
python scripts/appearance.py --locale de --region DE --bundle-id com.myapp
|
|
254
|
+
python scripts/appearance.py --reset
|
|
255
|
+
|
|
256
|
+
RTL locales (ar, he, fa, ur, yi): app must restart to reflow layout.
|
|
257
|
+
Text sizes: XS S M L XL XXL XXXL AX1 AX2 AX3 AX4 AX5
|
|
258
|
+
""",
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
parser.add_argument(
|
|
262
|
+
"--theme",
|
|
263
|
+
choices=["light", "dark"],
|
|
264
|
+
help="Set light or dark appearance",
|
|
265
|
+
)
|
|
266
|
+
parser.add_argument(
|
|
267
|
+
"--text-size",
|
|
268
|
+
choices=list(TEXT_SIZE_MAP.keys()),
|
|
269
|
+
metavar="{" + ",".join(TEXT_SIZE_MAP.keys()) + "}",
|
|
270
|
+
help="Set dynamic type size (XS smallest, AX5 largest)",
|
|
271
|
+
)
|
|
272
|
+
parser.add_argument(
|
|
273
|
+
"--locale",
|
|
274
|
+
metavar="CODE",
|
|
275
|
+
help="Set locale language code (e.g. en, de, ar, ja)",
|
|
276
|
+
)
|
|
277
|
+
parser.add_argument(
|
|
278
|
+
"--region",
|
|
279
|
+
metavar="CODE",
|
|
280
|
+
help="Set region code (e.g. US, IE, SA). Used with --locale.",
|
|
281
|
+
)
|
|
282
|
+
parser.add_argument(
|
|
283
|
+
"--bundle-id",
|
|
284
|
+
metavar="BUNDLE_ID",
|
|
285
|
+
help="App bundle ID to terminate+relaunch after locale change",
|
|
286
|
+
)
|
|
287
|
+
parser.add_argument(
|
|
288
|
+
"--reset",
|
|
289
|
+
action="store_true",
|
|
290
|
+
help="Reset theme, text size, and locale to system defaults",
|
|
291
|
+
)
|
|
292
|
+
parser.add_argument(
|
|
293
|
+
"--udid",
|
|
294
|
+
help="Device UDID (auto-detects booted simulator if not provided)",
|
|
295
|
+
)
|
|
296
|
+
parser.add_argument(
|
|
297
|
+
"--json",
|
|
298
|
+
action="store_true",
|
|
299
|
+
help="Output results as JSON",
|
|
300
|
+
)
|
|
301
|
+
parser.add_argument(
|
|
302
|
+
"--verbose",
|
|
303
|
+
action="store_true",
|
|
304
|
+
help="Show detailed output",
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
args = parser.parse_args()
|
|
308
|
+
|
|
309
|
+
# Guard: require at least one action
|
|
310
|
+
if not any([args.theme, args.text_size, args.locale, args.reset]):
|
|
311
|
+
parser.print_help()
|
|
312
|
+
sys.exit(1)
|
|
313
|
+
|
|
314
|
+
# Guard: --reset is incompatible with explicit appearance flags
|
|
315
|
+
if args.reset and any([args.theme, args.text_size, args.locale]):
|
|
316
|
+
print(
|
|
317
|
+
"Error: --reset cannot be combined with --theme, --text-size, or --locale",
|
|
318
|
+
file=sys.stderr,
|
|
319
|
+
)
|
|
320
|
+
sys.exit(1)
|
|
321
|
+
|
|
322
|
+
# Guard: --region without --locale is a no-op
|
|
323
|
+
if args.region and not args.locale:
|
|
324
|
+
print("Error: --region requires --locale", file=sys.stderr)
|
|
325
|
+
sys.exit(1)
|
|
326
|
+
|
|
327
|
+
# Guard: --bundle-id without --locale is ambiguous
|
|
328
|
+
if args.bundle_id and not args.locale:
|
|
329
|
+
print("Error: --bundle-id requires --locale", file=sys.stderr)
|
|
330
|
+
sys.exit(1)
|
|
331
|
+
|
|
332
|
+
try:
|
|
333
|
+
udid = resolve_udid(args.udid)
|
|
334
|
+
except RuntimeError as error:
|
|
335
|
+
print(f"Error: {error}", file=sys.stderr)
|
|
336
|
+
sys.exit(1)
|
|
337
|
+
|
|
338
|
+
manager = AppearanceManager(udid=udid)
|
|
339
|
+
device_label = udid if udid else "booted"
|
|
340
|
+
|
|
341
|
+
# Collect all requested operations
|
|
342
|
+
operations: list[tuple[str, tuple[bool, str]]] = []
|
|
343
|
+
|
|
344
|
+
if args.reset:
|
|
345
|
+
operations.append(("reset", manager.reset()))
|
|
346
|
+
else:
|
|
347
|
+
if args.theme:
|
|
348
|
+
operations.append(("theme", manager.set_theme(args.theme)))
|
|
349
|
+
|
|
350
|
+
if args.text_size:
|
|
351
|
+
operations.append(("text_size", manager.set_text_size(args.text_size)))
|
|
352
|
+
|
|
353
|
+
if args.locale:
|
|
354
|
+
operations.append(
|
|
355
|
+
(
|
|
356
|
+
"locale",
|
|
357
|
+
manager.set_locale(args.locale, args.region, args.bundle_id),
|
|
358
|
+
)
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
overall_success = all(ok for _, (ok, _) in operations)
|
|
362
|
+
|
|
363
|
+
if args.json:
|
|
364
|
+
results = {action: {"success": ok, "message": msg} for action, (ok, msg) in operations}
|
|
365
|
+
payload = {
|
|
366
|
+
"success": overall_success,
|
|
367
|
+
"udid": device_label,
|
|
368
|
+
"results": results,
|
|
369
|
+
}
|
|
370
|
+
print(json.dumps(payload))
|
|
371
|
+
elif args.verbose:
|
|
372
|
+
print(f"Device: {device_label}")
|
|
373
|
+
for action, (ok, msg) in operations:
|
|
374
|
+
status = "OK" if ok else "FAIL"
|
|
375
|
+
print(f" [{status}] {action}: {msg}")
|
|
376
|
+
else:
|
|
377
|
+
for _, (_ok, msg) in operations:
|
|
378
|
+
if msg:
|
|
379
|
+
print(msg)
|
|
380
|
+
|
|
381
|
+
sys.exit(0 if overall_success else 1)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
if __name__ == "__main__":
|
|
385
|
+
main()
|