From 2e26f98687c191d811ddbd2bd4737b594d4aa6d2 Mon Sep 17 00:00:00 2001 From: csh Date: Wed, 1 Apr 2026 13:11:10 +0800 Subject: [PATCH] :wrench: chore(thirdparty): generalize skills sync pipeline replace the superpowers-only workflow with a manifest-driven pipeline sync ui-ux-pro-max into codex/skills and document source lists fix the prettier path test so the Python suite passes on Windows --- .gitea/ci/sync_superpowers.sh | 77 - .gitea/ci/sync_thirdparty_skills.sh | 211 ++ .gitea/ci/thirdparty_skills.json | 26 + .gitea/ci/update_thirdparty_skills.sh | 184 ++ .gitea/ci/update_thirdparty_superpowers.sh | 159 -- ...owers.yml => update-thirdparty-skills.yml} | 23 +- SKILLS.md | 9 +- codex/skills/.sources/ui-ux-pro-max.list | 1 + codex/skills/ui-ux-pro-max/SKILL.md | 362 ++++ codex/skills/ui-ux-pro-max/data/_sync_all.py | 414 ++++ .../ui-ux-pro-max/data/app-interface.csv | 31 + codex/skills/ui-ux-pro-max/data/charts.csv | 26 + codex/skills/ui-ux-pro-max/data/colors.csv | 162 ++ codex/skills/ui-ux-pro-max/data/design.csv | 1776 +++++++++++++++ codex/skills/ui-ux-pro-max/data/draft.csv | 1779 +++++++++++++++ .../ui-ux-pro-max/data/google-fonts.csv | 1924 +++++++++++++++++ codex/skills/ui-ux-pro-max/data/icons.csv | 106 + codex/skills/ui-ux-pro-max/data/landing.csv | 35 + codex/skills/ui-ux-pro-max/data/products.csv | 162 ++ .../ui-ux-pro-max/data/react-performance.csv | 45 + .../ui-ux-pro-max/data/stacks/angular.csv | 51 + .../ui-ux-pro-max/data/stacks/astro.csv | 54 + .../ui-ux-pro-max/data/stacks/flutter.csv | 53 + .../data/stacks/html-tailwind.csv | 56 + .../data/stacks/jetpack-compose.csv | 53 + .../ui-ux-pro-max/data/stacks/laravel.csv | 51 + .../ui-ux-pro-max/data/stacks/nextjs.csv | 53 + .../ui-ux-pro-max/data/stacks/nuxt-ui.csv | 51 + .../ui-ux-pro-max/data/stacks/nuxtjs.csv | 59 + .../data/stacks/react-native.csv | 52 + .../ui-ux-pro-max/data/stacks/react.csv | 54 + .../ui-ux-pro-max/data/stacks/shadcn.csv | 61 + .../ui-ux-pro-max/data/stacks/svelte.csv | 54 + .../ui-ux-pro-max/data/stacks/swiftui.csv | 51 + .../ui-ux-pro-max/data/stacks/threejs.csv | 54 + .../skills/ui-ux-pro-max/data/stacks/vue.csv | 50 + codex/skills/ui-ux-pro-max/data/styles.csv | 85 + .../skills/ui-ux-pro-max/data/typography.csv | 74 + .../ui-ux-pro-max/data/ui-reasoning.csv | 162 ++ .../ui-ux-pro-max/data/ux-guidelines.csv | 100 + codex/skills/ui-ux-pro-max/scripts/core.py | 262 +++ .../ui-ux-pro-max/scripts/design_system.py | 1067 +++++++++ codex/skills/ui-ux-pro-max/scripts/search.py | 114 + tests/README.md | 3 +- tests/test_format_md_action.py | 26 +- tests/test_superpowers_list_sync.py | 35 - tests/test_superpowers_workflows.py | 93 - tests/test_thirdparty_skills_pipeline.py | 85 + 48 files changed, 10086 insertions(+), 389 deletions(-) delete mode 100644 .gitea/ci/sync_superpowers.sh create mode 100644 .gitea/ci/sync_thirdparty_skills.sh create mode 100644 .gitea/ci/thirdparty_skills.json create mode 100644 .gitea/ci/update_thirdparty_skills.sh delete mode 100644 .gitea/ci/update_thirdparty_superpowers.sh rename .gitea/workflows/{update-thirdparty-superpowers.yml => update-thirdparty-skills.yml} (86%) create mode 100644 codex/skills/.sources/ui-ux-pro-max.list create mode 100644 codex/skills/ui-ux-pro-max/SKILL.md create mode 100644 codex/skills/ui-ux-pro-max/data/_sync_all.py create mode 100644 codex/skills/ui-ux-pro-max/data/app-interface.csv create mode 100644 codex/skills/ui-ux-pro-max/data/charts.csv create mode 100644 codex/skills/ui-ux-pro-max/data/colors.csv create mode 100644 codex/skills/ui-ux-pro-max/data/design.csv create mode 100644 codex/skills/ui-ux-pro-max/data/draft.csv create mode 100644 codex/skills/ui-ux-pro-max/data/google-fonts.csv create mode 100644 codex/skills/ui-ux-pro-max/data/icons.csv create mode 100644 codex/skills/ui-ux-pro-max/data/landing.csv create mode 100644 codex/skills/ui-ux-pro-max/data/products.csv create mode 100644 codex/skills/ui-ux-pro-max/data/react-performance.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/angular.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/astro.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/flutter.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/laravel.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/nextjs.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/react-native.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/react.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/shadcn.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/svelte.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/swiftui.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/threejs.csv create mode 100644 codex/skills/ui-ux-pro-max/data/stacks/vue.csv create mode 100644 codex/skills/ui-ux-pro-max/data/styles.csv create mode 100644 codex/skills/ui-ux-pro-max/data/typography.csv create mode 100644 codex/skills/ui-ux-pro-max/data/ui-reasoning.csv create mode 100644 codex/skills/ui-ux-pro-max/data/ux-guidelines.csv create mode 100644 codex/skills/ui-ux-pro-max/scripts/core.py create mode 100644 codex/skills/ui-ux-pro-max/scripts/design_system.py create mode 100644 codex/skills/ui-ux-pro-max/scripts/search.py delete mode 100644 tests/test_superpowers_list_sync.py delete mode 100644 tests/test_superpowers_workflows.py create mode 100644 tests/test_thirdparty_skills_pipeline.py diff --git a/.gitea/ci/sync_superpowers.sh b/.gitea/ci/sync_superpowers.sh deleted file mode 100644 index 7a3716a..0000000 --- a/.gitea/ci/sync_superpowers.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -REPO_DIR="${REPO_DIR:-$(pwd)}" -SUPERPOWERS_BRANCH="${SUPERPOWERS_BRANCH:-thirdparty/skill}" -SUPERPOWERS_DIR="${SUPERPOWERS_DIR:-superpowers}" -SUPERPOWERS_LIST="${SUPERPOWERS_LIST:-codex/skills/.sources/superpowers.list}" -TARGET_BRANCH="${TARGET_BRANCH:-main}" -COMMIT_AUTHOR_NAME="${COMMIT_AUTHOR_NAME:-ci[bot]}" -COMMIT_AUTHOR_EMAIL="${COMMIT_AUTHOR_EMAIL:-ci-bot@local}" - -cd "$REPO_DIR" - -git config user.name "$COMMIT_AUTHOR_NAME" -git config user.email "$COMMIT_AUTHOR_EMAIL" - -git fetch origin "$SUPERPOWERS_BRANCH" -git fetch origin "$TARGET_BRANCH" - -tmp_dir="$(mktemp -d)" -cleanup() { - rm -rf "$tmp_dir" -} -trap cleanup EXIT - -git archive --format=tar "origin/${SUPERPOWERS_BRANCH}" "${SUPERPOWERS_DIR}/skills" | tar -xf - -C "$tmp_dir" - -tmp_skills_dir="$tmp_dir/${SUPERPOWERS_DIR}/skills" -if [ ! -d "$tmp_skills_dir" ]; then - echo "ERROR: ${SUPERPOWERS_DIR}/skills not found in ${SUPERPOWERS_BRANCH}" >&2 - exit 1 -fi - -git checkout -B "$TARGET_BRANCH" "origin/$TARGET_BRANCH" - -mkdir -p "$(dirname "$SUPERPOWERS_LIST")" - -old_list="$SUPERPOWERS_LIST" -if [ -f "$old_list" ]; then - while IFS= read -r name; do - [ -n "$name" ] || continue - rm -rf "codex/skills/$name" - done < "$old_list" -fi - -names=() -for dir in "$tmp_skills_dir"/*; do - [ -d "$dir" ] || continue - name="$(basename "$dir")" - - if [ -d "codex/skills/$name" ] && ! grep -qx "$name" "$old_list" 2>/dev/null; then - echo "ERROR: skill name conflict: $name" >&2 - exit 1 - fi - - rm -rf "codex/skills/$name" - cp -R "$dir" "codex/skills/$name" - names+=("$name") -done - -printf "%s\n" "${names[@]}" | sort > "$SUPERPOWERS_LIST" - -git add codex/skills "$SUPERPOWERS_LIST" - -if git diff --cached --quiet; then - echo "No changes to sync." - exit 0 -fi - -git commit -m ":package: deps(skills): sync superpowers" - -TOKEN="${WORKFLOW:-}" -if [ -n "$TOKEN" ] && [ -n "${GITHUB_SERVER_URL:-}" ] && [ -n "${GITHUB_REPOSITORY:-}" ]; then - git remote set-url origin "https://oauth2:${TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git" -fi - -git push origin "$TARGET_BRANCH" diff --git a/.gitea/ci/sync_thirdparty_skills.sh b/.gitea/ci/sync_thirdparty_skills.sh new file mode 100644 index 0000000..6f51ef0 --- /dev/null +++ b/.gitea/ci/sync_thirdparty_skills.sh @@ -0,0 +1,211 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_DIR="${REPO_DIR:-$(pwd)}" +THIRDPARTY_BRANCH="${THIRDPARTY_BRANCH:-thirdparty/skill}" +TARGET_BRANCH="${TARGET_BRANCH:-main}" +MANIFEST_PATH="${MANIFEST_PATH:-.gitea/ci/thirdparty_skills.json}" +COMMIT_AUTHOR_NAME="${COMMIT_AUTHOR_NAME:-ci[bot]}" +COMMIT_AUTHOR_EMAIL="${COMMIT_AUTHOR_EMAIL:-ci-bot@local}" + +emit_sources_tsv() { + python3 - "$MANIFEST_PATH" <<'PY' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as fh: + data = json.load(fh) + +for entry in data["sources"]: + print( + "\t".join( + [ + entry["id"], + entry["snapshot_dir"], + entry["sync_mode"], + entry["source_list"], + entry.get("skills_subdir", ""), + entry.get("output_name", entry["id"]), + entry.get("platform_config", ""), + entry.get("template_root", ""), + entry.get("data_dir", ""), + entry.get("scripts_dir", ""), + ] + ) + ) +PY +} + +render_codex_skill() { + local snapshot_root="$1" + local output_dir="$2" + local platform_config_rel="$3" + local template_root_rel="$4" + local data_dir_rel="$5" + local scripts_dir_rel="$6" + + python3 - "$snapshot_root" "$output_dir" "$platform_config_rel" "$template_root_rel" "$data_dir_rel" "$scripts_dir_rel" <<'PY' +import json +import pathlib +import shutil +import sys + +snapshot_root = pathlib.Path(sys.argv[1]) +output_dir = pathlib.Path(sys.argv[2]) +platform_config_path = snapshot_root / sys.argv[3] +template_root = snapshot_root / sys.argv[4] +data_dir = snapshot_root / sys.argv[5] +scripts_dir = snapshot_root / sys.argv[6] + +config = json.loads(platform_config_path.read_text(encoding="utf-8")) +skill_template = (template_root / "base" / "skill-content.md").read_text(encoding="utf-8") +quick_reference = "" +if config.get("sections", {}).get("quickReference"): + quick_reference = "\n" + (template_root / "base" / "quick-reference.md").read_text(encoding="utf-8") + +def render_frontmatter(frontmatter): + if not frontmatter: + return "" + + lines = ["---"] + for key, value in frontmatter.items(): + if any(ch in value for ch in ':"\n'): + value = value.replace('"', '\\"') + lines.append(f'{key}: "{value}"') + else: + lines.append(f"{key}: {value}") + lines.extend(["---", ""]) + return "\n".join(lines) + +content = skill_template +content = content.replace("{{TITLE}}", config["title"]) +content = content.replace("{{DESCRIPTION}}", config["description"]) +content = content.replace("{{SCRIPT_PATH}}", config["scriptPath"]) +content = content.replace("{{SKILL_OR_WORKFLOW}}", config["skillOrWorkflow"]) +content = content.replace("{{QUICK_REFERENCE}}", quick_reference) + +if output_dir.exists(): + shutil.rmtree(output_dir) +output_dir.mkdir(parents=True, exist_ok=True) + +(output_dir / "SKILL.md").write_text( + render_frontmatter(config.get("frontmatter")) + content, + encoding="utf-8", +) +if data_dir.exists(): + shutil.copytree(data_dir, output_dir / "data") +if scripts_dir.exists(): + shutil.copytree(scripts_dir, output_dir / "scripts") +PY +} + +tracked_skill_exists() { + local name="$1" + if git ls-files --error-unmatch -- "codex/skills/$name" >/dev/null 2>&1; then + return 0 + fi + return 1 +} + +cd "$REPO_DIR" + +git config user.name "$COMMIT_AUTHOR_NAME" +git config user.email "$COMMIT_AUTHOR_EMAIL" + +git fetch origin "$THIRDPARTY_BRANCH" +git fetch origin "$TARGET_BRANCH" + +tmp_dir="$(mktemp -d)" +cleanup() { + rm -rf "$tmp_dir" +} +trap cleanup EXIT + +git checkout -B "$TARGET_BRANCH" "origin/$TARGET_BRANCH" + +mkdir -p "codex/skills/.sources" + +while IFS=$'\t' read -r source_id snapshot_dir sync_mode source_list skills_subdir output_name platform_config template_root data_dir scripts_dir; do + [ -n "$source_id" ] || continue + + if [ -f "$source_list" ]; then + while IFS= read -r name; do + [ -n "$name" ] || continue + rm -rf "codex/skills/$name" + done < "$source_list" + fi +done < <(emit_sources_tsv) + +declare -A owners=() +while IFS=$'\t' read -r source_id snapshot_dir sync_mode source_list skills_subdir output_name platform_config template_root data_dir scripts_dir; do + [ -n "$source_id" ] || continue + + git archive --format=tar "origin/${THIRDPARTY_BRANCH}" "$snapshot_dir" | tar -xf - -C "$tmp_dir" + snapshot_root="$tmp_dir/$snapshot_dir" + + names=() + case "$sync_mode" in + copy_skill_dirs) + source_skills_dir="$snapshot_root/$skills_subdir" + if [ ! -d "$source_skills_dir" ]; then + echo "ERROR: $skills_subdir not found in snapshot $snapshot_dir" >&2 + exit 1 + fi + + for dir in "$source_skills_dir"/*; do + [ -d "$dir" ] || continue + name="$(basename "$dir")" + if [ -n "${owners[$name]:-}" ] && [ "${owners[$name]}" != "$source_id" ]; then + echo "ERROR: duplicate third-party skill name: $name" >&2 + exit 1 + fi + if [ -d "codex/skills/$name" ] && tracked_skill_exists "$name"; then + echo "ERROR: skill name conflict with tracked skill: $name" >&2 + exit 1 + fi + + rm -rf "codex/skills/$name" + cp -R "$dir" "codex/skills/$name" + names+=("$name") + owners["$name"]="$source_id" + done + ;; + render_codex_skill) + name="$output_name" + if [ -n "${owners[$name]:-}" ] && [ "${owners[$name]}" != "$source_id" ]; then + echo "ERROR: duplicate third-party skill name: $name" >&2 + exit 1 + fi + if [ -d "codex/skills/$name" ] && tracked_skill_exists "$name"; then + echo "ERROR: skill name conflict with tracked skill: $name" >&2 + exit 1 + fi + + render_codex_skill "$snapshot_root" "codex/skills/$name" "$platform_config" "$template_root" "$data_dir" "$scripts_dir" + names+=("$name") + owners["$name"]="$source_id" + ;; + *) + echo "ERROR: unsupported sync mode: $sync_mode" >&2 + exit 1 + ;; + esac + + printf "%s\n" "${names[@]}" | sort > "$source_list" +done < <(emit_sources_tsv) + +git add codex/skills + +if git diff --cached --quiet; then + echo "No third-party skills to sync." + exit 0 +fi + +git commit -m ":package: deps(skills): sync thirdparty skills" + +TOKEN="${WORKFLOW:-}" +if [ -n "$TOKEN" ] && [ -n "${GITHUB_SERVER_URL:-}" ] && [ -n "${GITHUB_REPOSITORY:-}" ]; then + git remote set-url origin "https://oauth2:${TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git" +fi + +git push origin "$TARGET_BRANCH" diff --git a/.gitea/ci/thirdparty_skills.json b/.gitea/ci/thirdparty_skills.json new file mode 100644 index 0000000..0196f10 --- /dev/null +++ b/.gitea/ci/thirdparty_skills.json @@ -0,0 +1,26 @@ +{ + "sources": [ + { + "id": "superpowers", + "upstream_repo": "https://github.com/obra/superpowers.git", + "upstream_ref": "main", + "snapshot_dir": "superpowers", + "sync_mode": "copy_skill_dirs", + "source_list": "codex/skills/.sources/superpowers.list", + "skills_subdir": "skills" + }, + { + "id": "ui-ux-pro-max", + "upstream_repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git", + "upstream_ref": "main", + "snapshot_dir": "ui-ux-pro-max", + "sync_mode": "render_codex_skill", + "source_list": "codex/skills/.sources/ui-ux-pro-max.list", + "output_name": "ui-ux-pro-max", + "platform_config": "src/ui-ux-pro-max/templates/platforms/codex.json", + "template_root": "src/ui-ux-pro-max/templates", + "data_dir": "src/ui-ux-pro-max/data", + "scripts_dir": "src/ui-ux-pro-max/scripts" + } + ] +} diff --git a/.gitea/ci/update_thirdparty_skills.sh b/.gitea/ci/update_thirdparty_skills.sh new file mode 100644 index 0000000..b207e6d --- /dev/null +++ b/.gitea/ci/update_thirdparty_skills.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_DIR="${REPO_DIR:-$(pwd)}" +TARGET_BRANCH="${TARGET_BRANCH:-thirdparty/skill}" +MANIFEST_PATH="${MANIFEST_PATH:-.gitea/ci/thirdparty_skills.json}" +COMMIT_AUTHOR_NAME="${COMMIT_AUTHOR_NAME:-ci[bot]}" +COMMIT_AUTHOR_EMAIL="${COMMIT_AUTHOR_EMAIL:-ci-bot@local}" + +retry_cmd() { + local retries="$1" + shift + local delay="$1" + shift + + local attempt=1 + while true; do + if "$@"; then + return 0 + fi + if [ "$attempt" -ge "$retries" ]; then + return 1 + fi + echo "Retry ($attempt/$retries): $*" >&2 + sleep "$delay" + attempt=$((attempt + 1)) + done +} + +github_owner_repo() { + case "$1" in + https://github.com/*) + echo "$1" | sed -E 's#^https://github.com/([^/]+/[^/.]+)(\.git)?$#\1#' + ;; + http://github.com/*) + echo "$1" | sed -E 's#^http://github.com/([^/]+/[^/.]+)(\.git)?$#\1#' + ;; + git@github.com:*) + echo "$1" | sed -E 's#^git@github.com:([^/]+/[^/.]+)(\.git)?$#\1#' + ;; + *) + return 1 + ;; + esac +} + +resolve_latest_sha() { + local repo="$1" + local ref="$2" + local tmp_json="$3" + local gh_repo="$4" + local sha="" + + if [ -n "$gh_repo" ]; then + local api_url="https://api.github.com/repos/${gh_repo}/commits/${ref}" + if retry_cmd 3 2 curl -fsSL --retry 3 --retry-delay 2 "$api_url" -o "$tmp_json"; then + sha="$(sed -n 's/^[[:space:]]*"sha":[[:space:]]*"\([0-9a-f]\{40\}\)".*/\1/p' "$tmp_json" | head -n 1)" + if [ -n "$sha" ]; then + echo "$sha" + return 0 + fi + fi + fi + + sha="$(retry_cmd 3 2 git -c http.version=HTTP/1.1 ls-remote "$repo" "refs/heads/$ref" | awk 'NR==1 {print $1}')" + if [ -n "$sha" ]; then + echo "$sha" + return 0 + fi + + return 1 +} + +emit_sources_tsv() { + python3 - "$MANIFEST_PATH" <<'PY' +import json +import sys + +with open(sys.argv[1], encoding="utf-8") as fh: + data = json.load(fh) + +for entry in data["sources"]: + print( + "\t".join( + [ + entry["id"], + entry["upstream_repo"], + entry.get("upstream_ref", "main"), + entry["snapshot_dir"], + entry["sync_mode"], + ] + ) + ) +PY +} + +cd "$REPO_DIR" + +git config user.name "$COMMIT_AUTHOR_NAME" +git config user.email "$COMMIT_AUTHOR_EMAIL" + +git fetch origin "$TARGET_BRANCH" +git checkout -B "$TARGET_BRANCH" "origin/$TARGET_BRANCH" + +tmp_dir="$(mktemp -d)" +cleanup() { + rm -rf "$tmp_dir" +} +trap cleanup EXIT + +changed=0 +while IFS=$'\t' read -r source_id upstream_repo upstream_ref snapshot_dir sync_mode; do + [ -n "$source_id" ] || continue + + gh_repo="" + if gh_repo="$(github_owner_repo "$upstream_repo" 2>/dev/null)"; then + : + fi + + latest_sha="$(resolve_latest_sha "$upstream_repo" "$upstream_ref" "$tmp_dir/${source_id}-latest.json" "$gh_repo" || true)" + if [ -z "$latest_sha" ]; then + echo "ERROR: failed to resolve upstream ref for ${source_id}: $upstream_repo $upstream_ref" >&2 + exit 1 + fi + + current_sha="" + if [ -f "$snapshot_dir/SOURCE.md" ]; then + current_sha="$(sed -n 's/^- Ref:[[:space:]]*//p' "$snapshot_dir/SOURCE.md" | head -n 1)" + fi + + if [ "$latest_sha" = "$current_sha" ]; then + echo "Third-party snapshot is up to date for ${source_id}: $latest_sha" + continue + fi + + rm -rf "$snapshot_dir" + mkdir -p "$snapshot_dir" + + snapshot_loaded=0 + if [ -n "$gh_repo" ]; then + tar_url="https://codeload.github.com/${gh_repo}/tar.gz/${latest_sha}" + if retry_cmd 3 2 curl -fsSL --retry 3 --retry-delay 2 "$tar_url" -o "$tmp_dir/${source_id}.tar.gz"; then + tar -xzf "$tmp_dir/${source_id}.tar.gz" -C "$snapshot_dir" --strip-components=1 + snapshot_loaded=1 + fi + fi + + if [ "$snapshot_loaded" -eq 0 ]; then + upstream_dir="$tmp_dir/${source_id}-upstream" + git init "$upstream_dir" >/dev/null + git -C "$upstream_dir" remote add origin "$upstream_repo" + retry_cmd 3 2 git -C "$upstream_dir" fetch --depth 1 origin "$latest_sha" + git -C "$upstream_dir" checkout --detach FETCH_HEAD + git -C "$upstream_dir" archive --format=tar HEAD | tar -xf - -C "$snapshot_dir" + fi + + snapshot_date="$(date -u +%Y-%m-%d)" + cat > "$snapshot_dir/SOURCE.md" <&2 - sleep "$delay" - attempt=$((attempt + 1)) - done -} - -github_owner_repo() { - case "$1" in - https://github.com/*) - echo "$1" | sed -E 's#^https://github.com/([^/]+/[^/.]+)(\.git)?$#\1#' - ;; - http://github.com/*) - echo "$1" | sed -E 's#^http://github.com/([^/]+/[^/.]+)(\.git)?$#\1#' - ;; - git@github.com:*) - echo "$1" | sed -E 's#^git@github.com:([^/]+/[^/.]+)(\.git)?$#\1#' - ;; - *) - return 1 - ;; - esac -} - -resolve_latest_sha() { - local repo="$1" - local ref="$2" - local tmp_json="$3" - local gh_repo="$4" - local sha="" - - # Prefer GitHub API when possible to avoid git+gnutls handshake failures. - if [ -n "$gh_repo" ]; then - local api_url="https://api.github.com/repos/${gh_repo}/commits/${ref}" - if retry_cmd 3 2 curl -fsSL --retry 3 --retry-delay 2 "$api_url" -o "$tmp_json"; then - sha="$(sed -n 's/^[[:space:]]*"sha":[[:space:]]*"\([0-9a-f]\{40\}\)".*/\1/p' "$tmp_json" | head -n 1)" - if [ -n "$sha" ]; then - echo "$sha" - return 0 - fi - fi - fi - - # Fallback to git transport. - sha="$(retry_cmd 3 2 git -c http.version=HTTP/1.1 ls-remote "$repo" "refs/heads/$ref" | awk 'NR==1 {print $1}')" - if [ -n "$sha" ]; then - echo "$sha" - return 0 - fi - return 1 -} - -cd "$REPO_DIR" - -git config user.name "$COMMIT_AUTHOR_NAME" -git config user.email "$COMMIT_AUTHOR_EMAIL" - -git fetch origin "$TARGET_BRANCH" -git checkout -B "$TARGET_BRANCH" "origin/$TARGET_BRANCH" - -tmp_dir="$(mktemp -d)" -cleanup() { - rm -rf "$tmp_dir" -} -trap cleanup EXIT - -gh_repo="" -if gh_repo="$(github_owner_repo "$UPSTREAM_REPO" 2>/dev/null)"; then - : -fi - -latest_sha="$(resolve_latest_sha "$UPSTREAM_REPO" "$UPSTREAM_REF" "$tmp_dir/latest.json" "$gh_repo" || true)" -if [ -z "$latest_sha" ]; then - echo "ERROR: failed to resolve upstream ref: $UPSTREAM_REPO $UPSTREAM_REF" >&2 - exit 1 -fi - -current_sha="" -if [ -f "$SOURCE_FILE" ]; then - current_sha="$(sed -n 's/^- Ref:[[:space:]]*//p' "$SOURCE_FILE" | head -n 1)" -fi - -if [ "$latest_sha" = "$current_sha" ]; then - echo "Third-party snapshot is up to date: $latest_sha" - exit 0 -fi - -rm -rf "$SNAPSHOT_DIR" -mkdir -p "$SNAPSHOT_DIR" - -snapshot_loaded=0 - -if [ -n "$gh_repo" ]; then - tar_url="https://codeload.github.com/${gh_repo}/tar.gz/${latest_sha}" - if retry_cmd 3 2 curl -fsSL --retry 3 --retry-delay 2 "$tar_url" -o "$tmp_dir/upstream.tar.gz"; then - tar -xzf "$tmp_dir/upstream.tar.gz" -C "$SNAPSHOT_DIR" --strip-components=1 - snapshot_loaded=1 - fi -fi - -if [ "$snapshot_loaded" -eq 0 ]; then - upstream_dir="$tmp_dir/upstream" - git init "$upstream_dir" >/dev/null - git -C "$upstream_dir" remote add origin "$UPSTREAM_REPO" - retry_cmd 3 2 git -C "$upstream_dir" fetch --depth 1 origin "$latest_sha" - git -C "$upstream_dir" checkout --detach FETCH_HEAD - git -C "$upstream_dir" archive --format=tar HEAD | tar -xf - -C "$SNAPSHOT_DIR" -fi - -snapshot_date="$(date -u +%Y-%m-%d)" -cat > "$SOURCE_FILE" <" fi - TARGET_BRANCH="$THIRDPARTY_BRANCH" bash .gitea/ci/update_thirdparty_superpowers.sh + export MANIFEST_PATH="$MANIFEST_PATH" + TARGET_BRANCH="$THIRDPARTY_BRANCH" bash .gitea/ci/update_thirdparty_skills.sh git fetch origin "$THIRDPARTY_BRANCH" after_ref="$(git rev-parse "origin/$THIRDPARTY_BRANCH")" @@ -103,9 +105,8 @@ jobs: git checkout -B main origin/main TARGET_BRANCH="main" \ - SUPERPOWERS_BRANCH="$THIRDPARTY_BRANCH" \ - SUPERPOWERS_DIR="$SUPERPOWERS_DIR" \ - SUPERPOWERS_LIST="$SUPERPOWERS_LIST" \ - bash .gitea/ci/sync_superpowers.sh + THIRDPARTY_BRANCH="$THIRDPARTY_BRANCH" \ + MANIFEST_PATH="$MANIFEST_PATH" \ + bash .gitea/ci/sync_thirdparty_skills.sh echo "๐ŸŽ‰ Update and sync finished." diff --git a/SKILLS.md b/SKILLS.md index 62e29da..9050e42 100644 --- a/SKILLS.md +++ b/SKILLS.md @@ -138,7 +138,7 @@ python docs/standards/playbook/scripts/playbook.py -config playbook.toml ## 8. ๆœฌ Playbook ๅŽŸ็”Ÿ skills ไฝไบŽ `codex/skills/`๏ผˆPlaybook ่‡ช็ปดๆŠค้ƒจๅˆ†๏ผ‰๏ผŒๅฝ“ๅ‰ๅ…ฑ 4 ไธชใ€‚ -็ฌฌไธ‰ๆ–น superpowers ๅˆ—่กจ่ง็ฌฌ 9 ่Š‚ใ€‚ +็ฌฌไธ‰ๆ–น skills ๆฅๆบ่ง็ฌฌ 9 ่Š‚ใ€‚ ### ่ฏญ่จ€็‰นๅฎš Skills @@ -156,9 +156,12 @@ python docs/standards/playbook/scripts/playbook.py -config playbook.toml --- -## 9. Third-party Skills (superpowers) +## 9. Third-party Skills -ๆฅๆบ๏ผš`codex/skills/.sources/superpowers.list`๏ผˆ็ฌฌไธ‰ๆ–นๆฅๆบๆธ…ๅ•๏ผ‰ใ€‚ +ๆฅๆบ๏ผš`codex/skills/.sources/`๏ผˆ็ฌฌไธ‰ๆ–นๆฅๆบๆธ…ๅ•็›ฎๅฝ•๏ผ‰ใ€‚ + +- `superpowers.list` +- `ui-ux-pro-max.list` --- diff --git a/codex/skills/.sources/ui-ux-pro-max.list b/codex/skills/.sources/ui-ux-pro-max.list new file mode 100644 index 0000000..1f0db1d --- /dev/null +++ b/codex/skills/.sources/ui-ux-pro-max.list @@ -0,0 +1 @@ +ui-ux-pro-max diff --git a/codex/skills/ui-ux-pro-max/SKILL.md b/codex/skills/ui-ux-pro-max/SKILL.md new file mode 100644 index 0000000..664437b --- /dev/null +++ b/codex/skills/ui-ux-pro-max/SKILL.md @@ -0,0 +1,362 @@ +--- +name: ui-ux-pro-max +description: UI/UX design intelligence with searchable database +--- +# ui-ux-pro-max + +Comprehensive design guide for web and mobile applications. Contains 67 styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 16 technology stacks. Searchable database with priority-based recommendations. + +# Prerequisites + +Check if Python is installed: + +```bash +python3 --version || python --version +``` + +If Python is not installed, install it based on user's OS: + +**macOS:** +```bash +brew install python3 +``` + +**Ubuntu/Debian:** +```bash +sudo apt update && sudo apt install python3 +``` + +**Windows:** +```powershell +winget install Python.Python.3.12 +``` + +--- + +## How to Use This Skill + +Use this skill when the user requests any of the following: + +| Scenario | Trigger Examples | Start From | +|----------|-----------------|------------| +| **New project / page** | "ๅšไธ€ไธช landing page"ใ€"Build a dashboard" | Step 1 โ†’ Step 2 (design system) | +| **New component** | "Create a pricing card"ใ€"Add a modal" | Step 3 (domain search: style, ux) | +| **Choose style / color / font** | "What style fits a fintech app?"ใ€"ๆŽจ่้…่‰ฒ" | Step 2 (design system) | +| **Review existing UI** | "Review this page for UX issues"ใ€"ๆฃ€ๆŸฅๆ— ้šœ็ข" | Quick Reference checklist above | +| **Fix a UI bug** | "Button hover is broken"ใ€"Layout shifts on load" | Quick Reference โ†’ relevant section | +| **Improve / optimize** | "Make this faster"ใ€"Improve mobile experience" | Step 3 (domain search: ux, react) | +| **Implement dark mode** | "Add dark mode support" | Step 3 (domain: style "dark mode") | +| **Add charts / data viz** | "Add an analytics dashboard chart" | Step 3 (domain: chart) | +| **Stack best practices** | "React performance tips"ใ€"SwiftUI navigation" | Step 4 (stack search) | + +Follow this workflow: + +### Step 1: Analyze User Requirements + +Extract key information from user request: +- **Product type**: Entertainment (social, video, music, gaming), Tool (scanner, editor, converter), Productivity (task manager, notes, calendar), or hybrid +- **Target audience**: C-end consumer users; consider age group, usage context (commute, leisure, work) +- **Style keywords**: playful, vibrant, minimal, dark mode, content-first, immersive, etc. +- **Stack**: React Native (this project's only tech stack) + +### Step 2: Generate Design System (REQUIRED) + +**Always start with `--design-system`** to get comprehensive recommendations with reasoning: + +```bash +python3 skills/ui-ux-pro-max/scripts/search.py " " --design-system [-p "Project Name"] +``` + +This command: +1. Searches domains in parallel (product, style, color, landing, typography) +2. Applies reasoning rules from `ui-reasoning.csv` to select best matches +3. Returns complete design system: pattern, style, colors, typography, effects +4. Includes anti-patterns to avoid + +**Example:** +```bash +python3 skills/ui-ux-pro-max/scripts/search.py "beauty spa wellness service" --design-system -p "Serenity Spa" +``` + +### Step 2b: Persist Design System (Master + Overrides Pattern) + +To save the design system for **hierarchical retrieval across sessions**, add `--persist`: + +```bash +python3 skills/ui-ux-pro-max/scripts/search.py "" --design-system --persist -p "Project Name" +``` + +This creates: +- `design-system/MASTER.md` โ€” Global Source of Truth with all design rules +- `design-system/pages/` โ€” Folder for page-specific overrides + +**With page-specific override:** +```bash +python3 skills/ui-ux-pro-max/scripts/search.py "" --design-system --persist -p "Project Name" --page "dashboard" +``` + +This also creates: +- `design-system/pages/dashboard.md` โ€” Page-specific deviations from Master + +**How hierarchical retrieval works:** +1. When building a specific page (e.g., "Checkout"), first check `design-system/pages/checkout.md` +2. If the page file exists, its rules **override** the Master file +3. If not, use `design-system/MASTER.md` exclusively + +**Context-aware retrieval prompt:** +``` +I am building the [Page Name] page. Please read design-system/MASTER.md. +Also check if design-system/pages/[page-name].md exists. +If the page file exists, prioritize its rules. +If not, use the Master rules exclusively. +Now, generate the code... +``` + +### Step 3: Supplement with Detailed Searches (as needed) + +After getting the design system, use domain searches to get additional details: + +```bash +python3 skills/ui-ux-pro-max/scripts/search.py "" --domain [-n ] +``` + +**When to use detailed searches:** + +| Need | Domain | Example | +|------|--------|---------| +| Product type patterns | `product` | `--domain product "entertainment social"` | +| More style options | `style` | `--domain style "glassmorphism dark"` | +| Color palettes | `color` | `--domain color "entertainment vibrant"` | +| Font pairings | `typography` | `--domain typography "playful modern"` | +| Chart recommendations | `chart` | `--domain chart "real-time dashboard"` | +| UX best practices | `ux` | `--domain ux "animation accessibility"` | +| Landing structure | `landing` | `--domain landing "hero social-proof"` | +| React Native perf | `react` | `--domain react "rerender memo list"` | +| App interface a11y | `web` | `--domain web "accessibilityLabel touch safe-areas"` | +| AI prompt / CSS keywords | `prompt` | `--domain prompt "minimalism"` | + +### Step 4: Stack Guidelines (React Native) + +Get React Native implementation-specific best practices: + +```bash +python3 skills/ui-ux-pro-max/scripts/search.py "" --stack react-native +``` + +--- + +## Search Reference + +### Available Domains + +| Domain | Use For | Example Keywords | +|--------|---------|------------------| +| `product` | Product type recommendations | SaaS, e-commerce, portfolio, healthcare, beauty, service | +| `style` | UI styles, colors, effects | glassmorphism, minimalism, dark mode, brutalism | +| `typography` | Font pairings, Google Fonts | elegant, playful, professional, modern | +| `color` | Color palettes by product type | saas, ecommerce, healthcare, beauty, fintech, service | +| `landing` | Page structure, CTA strategies | hero, hero-centric, testimonial, pricing, social-proof | +| `chart` | Chart types, library recommendations | trend, comparison, timeline, funnel, pie | +| `ux` | Best practices, anti-patterns | animation, accessibility, z-index, loading | +| `react` | React/Next.js performance | waterfall, bundle, suspense, memo, rerender, cache | +| `web` | App interface guidelines (iOS/Android/React Native) | accessibilityLabel, touch targets, safe areas, Dynamic Type | +| `prompt` | AI prompts, CSS keywords | (style name) | + +### Available Stacks + +| Stack | Focus | +|-------|-------| +| `react-native` | Components, Navigation, Lists | + +--- + +## Example Workflow + +**User request:** "Make an AI search homepageใ€‚" + +### Step 1: Analyze Requirements +- Product type: Tool (AI search engine) +- Target audience: C-end users looking for fast, intelligent search +- Style keywords: modern, minimal, content-first, dark mode +- Stack: React Native + +### Step 2: Generate Design System (REQUIRED) + +```bash +python3 skills/ui-ux-pro-max/scripts/search.py "AI search tool modern minimal" --design-system -p "AI Search" +``` + +**Output:** Complete design system with pattern, style, colors, typography, effects, and anti-patterns. + +### Step 3: Supplement with Detailed Searches (as needed) + +```bash +# Get style options for a modern tool product +python3 skills/ui-ux-pro-max/scripts/search.py "minimalism dark mode" --domain style + +# Get UX best practices for search interaction and loading +python3 skills/ui-ux-pro-max/scripts/search.py "search loading animation" --domain ux +``` + +### Step 4: Stack Guidelines + +```bash +python3 skills/ui-ux-pro-max/scripts/search.py "list performance navigation" --stack react-native +``` + +**Then:** Synthesize design system + detailed searches and implement the design. + +--- + +## Output Formats + +The `--design-system` flag supports two output formats: + +```bash +# ASCII box (default) - best for terminal display +python3 skills/ui-ux-pro-max/scripts/search.py "fintech crypto" --design-system + +# Markdown - best for documentation +python3 skills/ui-ux-pro-max/scripts/search.py "fintech crypto" --design-system -f markdown +``` + +--- + +## Tips for Better Results + +### Query Strategy + +- Use **multi-dimensional keywords** โ€” combine product + industry + tone + density: `"entertainment social vibrant content-dense"` not just `"app"` +- Try different keywords for the same need: `"playful neon"` โ†’ `"vibrant dark"` โ†’ `"content-first minimal"` +- Use `--design-system` first for full recommendations, then `--domain` to deep-dive any dimension you're unsure about +- Always add `--stack react-native` for implementation-specific guidance + +### Common Sticking Points + +| Problem | What to Do | +|---------|------------| +| Can't decide on style/color | Re-run `--design-system` with different keywords | +| Dark mode contrast issues | Quick Reference ยง6: `color-dark-mode` + `color-accessible-pairs` | +| Animations feel unnatural | Quick Reference ยง7: `spring-physics` + `easing` + `exit-faster-than-enter` | +| Form UX is poor | Quick Reference ยง8: `inline-validation` + `error-clarity` + `focus-management` | +| Navigation feels confusing | Quick Reference ยง9: `nav-hierarchy` + `bottom-nav-limit` + `back-behavior` | +| Layout breaks on small screens | Quick Reference ยง5: `mobile-first` + `breakpoint-consistency` | +| Performance / jank | Quick Reference ยง3: `virtualize-lists` + `main-thread-budget` + `debounce-throttle` | + +### Pre-Delivery Checklist + +- Run `--domain ux "animation accessibility z-index loading"` as a UX validation pass before implementation +- Run through Quick Reference **ยง1โ€“ยง3** (CRITICAL + HIGH) as a final review +- Test on 375px (small phone) and landscape orientation +- Verify behavior with **reduced-motion** enabled and **Dynamic Type** at largest size +- Check dark mode contrast independently (don't assume light mode values work) +- Confirm all touch targets โ‰ฅ44pt and no content hidden behind safe areas + +--- + +## Common Rules for Professional UI + +These are frequently overlooked issues that make UI look unprofessional: +Scope notice: The rules below are for App UI (iOS/Android/React Native/Flutter), not desktop-web interaction patterns. + +### Icons & Visual Elements + +- ้ป˜่ฎคๅ›พๆ ‡ๅบ“ไฝฟ็”จ **Phosphor (`@phosphor-icons/react`)**ใ€‚`src/ui-ux-pro-max/data/icons.csv` ไธญๅˆ—ๅ‡บ็š„ๅชๆ˜ฏๅธธ็”จๆŽจ่ๅ›พๆ ‡๏ผŒไธๆ˜ฏๅฎŒๆ•ด้›†ๅˆใ€‚ +- ๅฝ“ๆŽจ่่กจไธญๆ‰พไธๅˆฐๅˆ้€‚็š„ๅ›พๆ ‡ๆ—ถ๏ผš + - **ไผ˜ๅ…ˆ็ปง็ปญไปŽ Phosphor ็š„ๅฎŒๆ•ดๅ›พๆ ‡้›†ไธญ้€‰ๆ‹ฉไปปไฝ•่ฏญไน‰ๆ›ด่ดดๅˆ‡็š„ๅ›พๆ ‡**๏ผ› + - ๅฆ‚ๆžœ Phosphor ไนŸๆฒกๆœ‰็†ๆƒณ้€‰้กน๏ผŒๅฏไปฅไฝฟ็”จ **Heroicons (`@heroicons/react`)** ไฝœไธบๅค‡้€‰๏ผŒๆณจๆ„ไฟๆŒ้ฃŽๆ ผไธ€่‡ด๏ผˆ็บฟๆ€ง/ๅกซๅ……ใ€็ฌ”็”ป็ฒ—็ป†ใ€ๅœ†่ง’้ฃŽๆ ผ๏ผ‰ใ€‚ + +| Rule | Standard | Avoid | Why It Matters | +|------|----------|--------|----------------| +| **No Emoji as Structural Icons** | Use vector-based icons (e.g., Phosphor `@phosphor-icons/react`, Heroicons `@heroicons/react`, react-native-vector-icons, @expo/vector-icons). | Using emojis (๐ŸŽจ ๐Ÿš€ โš™๏ธ) for navigation, settings, or system controls. | Emojis are font-dependent, inconsistent across platforms, and cannot be controlled via design tokens. | +| **Vector-Only Assets** | Use SVG or platform vector icons that scale cleanly and support theming. | Raster PNG icons that blur or pixelate. | Ensures scalability, crisp rendering, and dark/light mode adaptability. | +| **Stable Interaction States** | Use color, opacity, or elevation transitions for press states without changing layout bounds. | Layout-shifting transforms that move surrounding content or trigger visual jitter. | Prevents unstable interactions and preserves smooth motion/perceived quality on mobile. | +| **Correct Brand Logos** | Use official brand assets and follow their usage guidelines (spacing, color, clear space). | Guessing logo paths, recoloring unofficially, or modifying proportions. | Prevents brand misuse and ensures legal/platform compliance. | +| **Consistent Icon Sizing** | Define icon sizes as design tokens (e.g., icon-sm, icon-md = 24pt, icon-lg). | Mixing arbitrary values like 20pt / 24pt / 28pt randomly. | Maintains rhythm and visual hierarchy across the interface. | +| **Stroke Consistency** | Use a consistent stroke width within the same visual layer (e.g., 1.5px or 2px). | Mixing thick and thin stroke styles arbitrarily. | Inconsistent strokes reduce perceived polish and cohesion. | +| **Filled vs Outline Discipline** | Use one icon style per hierarchy level. | Mixing filled and outline icons at the same hierarchy level. | Maintains semantic clarity and stylistic coherence. | +| **Touch Target Minimum** | Minimum 44ร—44pt interactive area (use hitSlop if icon is smaller). | Small icons without expanded tap area. | Meets accessibility and platform usability standards. | +| **Icon Alignment** | Align icons to text baseline and maintain consistent padding. | Misaligned icons or inconsistent spacing around them. | Prevents subtle visual imbalance that reduces perceived quality. | +| **Icon Contrast** | Follow WCAG contrast standards: 4.5:1 for small elements, 3:1 minimum for larger UI glyphs. | Low-contrast icons that blend into the background. | Ensures accessibility in both light and dark modes. | + + +### Interaction (App) + +| Rule | Do | Don't | +|------|----|----- | +| **Tap feedback** | Provide clear pressed feedback (ripple/opacity/elevation) within 80-150ms | No visual response on tap | +| **Animation timing** | Keep micro-interactions around 150-300ms with platform-native easing | Instant transitions or slow animations (>500ms) | +| **Accessibility focus** | Ensure screen reader focus order matches visual order and labels are descriptive | Unlabeled controls or confusing focus traversal | +| **Disabled state clarity** | Use disabled semantics (`disabled`/native disabled props), reduced emphasis, and no tap action | Controls that look tappable but do nothing | +| **Touch target minimum** | Keep tap areas >=44x44pt (iOS) or >=48x48dp (Android), expand hit area when icon is smaller | Tiny tap targets or icon-only hit areas without padding | +| **Gesture conflict prevention** | Keep one primary gesture per region and avoid nested tap/drag conflicts | Overlapping gestures causing accidental actions | +| **Semantic native controls** | Prefer native interactive primitives (`Button`, `Pressable`, platform equivalents) with proper accessibility roles | Generic containers used as primary controls without semantics | + +### Light/Dark Mode Contrast + +| Rule | Do | Don't | +|------|----|----- | +| **Surface readability (light)** | Keep cards/surfaces clearly separated from background with sufficient opacity/elevation | Overly transparent surfaces that blur hierarchy | +| **Text contrast (light)** | Maintain body text contrast >=4.5:1 against light surfaces | Low-contrast gray body text | +| **Text contrast (dark)** | Maintain primary text contrast >=4.5:1 and secondary text >=3:1 on dark surfaces | Dark mode text that blends into background | +| **Border and divider visibility** | Ensure separators are visible in both themes (not just light mode) | Theme-specific borders disappearing in one mode | +| **State contrast parity** | Keep pressed/focused/disabled states equally distinguishable in light and dark themes | Defining interaction states for one theme only | +| **Token-driven theming** | Use semantic color tokens mapped per theme across app surfaces/text/icons | Hardcoded per-screen hex values | +| **Scrim and modal legibility** | Use a modal scrim strong enough to isolate foreground content (typically 40-60% black) | Weak scrim that leaves background visually competing | + +### Layout & Spacing + +| Rule | Do | Don't | +|------|----|----- | +| **Safe-area compliance** | Respect top/bottom safe areas for all fixed headers, tab bars, and CTA bars | Placing fixed UI under notch, status bar, or gesture area | +| **System bar clearance** | Add spacing for status/navigation bars and gesture home indicator | Let tappable content collide with OS chrome | +| **Consistent content width** | Keep predictable content width per device class (phone/tablet) | Mixing arbitrary widths between screens | +| **8dp spacing rhythm** | Use a consistent 4/8dp spacing system for padding/gaps/section spacing | Random spacing increments with no rhythm | +| **Readable text measure** | Keep long-form text readable on large devices (avoid edge-to-edge paragraphs on tablets) | Full-width long text that hurts readability | +| **Section spacing hierarchy** | Define clear vertical rhythm tiers (e.g., 16/24/32/48) by hierarchy | Similar UI levels with inconsistent spacing | +| **Adaptive gutters by breakpoint** | Increase horizontal insets on larger widths and in landscape | Same narrow gutter on all device sizes/orientations | +| **Scroll and fixed element coexistence** | Add bottom/top content insets so lists are not hidden behind fixed bars | Scroll content obscured by sticky headers/footers | + +--- + +## Pre-Delivery Checklist + +Before delivering UI code, verify these items: +Scope notice: This checklist is for App UI (iOS/Android/React Native/Flutter). + +### Visual Quality +- [ ] No emojis used as icons (use SVG instead) +- [ ] All icons come from a consistent icon family and style +- [ ] Official brand assets are used with correct proportions and clear space +- [ ] Pressed-state visuals do not shift layout bounds or cause jitter +- [ ] Semantic theme tokens are used consistently (no ad-hoc per-screen hardcoded colors) + +### Interaction +- [ ] All tappable elements provide clear pressed feedback (ripple/opacity/elevation) +- [ ] Touch targets meet minimum size (>=44x44pt iOS, >=48x48dp Android) +- [ ] Micro-interaction timing stays in the 150-300ms range with native-feeling easing +- [ ] Disabled states are visually clear and non-interactive +- [ ] Screen reader focus order matches visual order, and interactive labels are descriptive +- [ ] Gesture regions avoid nested/conflicting interactions (tap/drag/back-swipe conflicts) + +### Light/Dark Mode +- [ ] Primary text contrast >=4.5:1 in both light and dark mode +- [ ] Secondary text contrast >=3:1 in both light and dark mode +- [ ] Dividers/borders and interaction states are distinguishable in both modes +- [ ] Modal/drawer scrim opacity is strong enough to preserve foreground legibility (typically 40-60% black) +- [ ] Both themes are tested before delivery (not inferred from a single theme) + +### Layout +- [ ] Safe areas are respected for headers, tab bars, and bottom CTA bars +- [ ] Scroll content is not hidden behind fixed/sticky bars +- [ ] Verified on small phone, large phone, and tablet (portrait + landscape) +- [ ] Horizontal insets/gutters adapt correctly by device size and orientation +- [ ] 4/8dp spacing rhythm is maintained across component, section, and page levels +- [ ] Long-form text measure remains readable on larger devices (no edge-to-edge paragraphs) + +### Accessibility +- [ ] All meaningful images/icons have accessibility labels +- [ ] Form fields have labels, hints, and clear error messages +- [ ] Color is not the only indicator +- [ ] Reduced motion and dynamic text size are supported without layout breakage +- [ ] Accessibility traits/roles/states (selected, disabled, expanded) are announced correctly \ No newline at end of file diff --git a/codex/skills/ui-ux-pro-max/data/_sync_all.py b/codex/skills/ui-ux-pro-max/data/_sync_all.py new file mode 100644 index 0000000..37f7c3a --- /dev/null +++ b/codex/skills/ui-ux-pro-max/data/_sync_all.py @@ -0,0 +1,414 @@ +#!/usr/bin/env python3 +""" +Sync colors.csv and ui-reasoning.csv with the updated products.csv (161 entries). +- Remove deleted product types +- Rename mismatched entries +- Add new entries for missing product types +- Keep colors.csv aligned 1:1 with products.csv +- Renumber everything +""" +import csv, os, json + +BASE = os.path.dirname(os.path.abspath(__file__)) + +# โ”€โ”€โ”€ Color derivation helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +def h2r(h): + h = h.lstrip("#") + return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) + +def r2h(r, g, b): + return f"#{max(0,min(255,int(r))):02X}{max(0,min(255,int(g))):02X}{max(0,min(255,int(b))):02X}" + +def lum(h): + r, g, b = [x/255.0 for x in h2r(h)] + r, g, b = [(x/12.92 if x<=0.03928 else ((x+0.055)/1.055)**2.4) for x in (r, g, b)] + return 0.2126*r + 0.7152*g + 0.0722*b + +def is_dark(bg): + return lum(bg) < 0.18 + +def on_color(bg): + return "#FFFFFF" if lum(bg) < 0.4 else "#0F172A" + +def blend(a, b, f=0.15): + ra, ga, ba = h2r(a) + rb, gb, bb = h2r(b) + return r2h(ra+(rb-ra)*f, ga+(gb-ga)*f, ba+(bb-ba)*f) + +def shift(h, n): + r, g, b = h2r(h) + return r2h(r+n, g+n, b+n) + +def derive_row(pt, pri, sec, acc, bg, notes=""): + """Generate full 16-token color row from 4 base colors.""" + dark = is_dark(bg) + fg = "#FFFFFF" if dark else "#0F172A" + on_pri = on_color(pri) + on_sec = on_color(sec) + on_acc = on_color(acc) + card = shift(bg, 10) if dark else "#FFFFFF" + card_fg = "#FFFFFF" if dark else "#0F172A" + muted = blend(bg, pri, 0.08) if dark else blend("#FFFFFF", pri, 0.06) + muted_fg = "#94A3B8" if dark else "#64748B" + border = f"rgba(255,255,255,0.08)" if dark else blend("#FFFFFF", pri, 0.12) + destr = "#DC2626" + on_destr = "#FFFFFF" + ring = pri + return [pt, pri, on_pri, sec, on_sec, acc, on_acc, bg, fg, card, card_fg, muted, muted_fg, border, destr, on_destr, ring, notes] + +# โ”€โ”€โ”€ Rename maps โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +COLOR_RENAMES = { + "Quantum Computing": "Quantum Computing Interface", + "Biohacking / Longevity": "Biohacking / Longevity App", + "Autonomous Systems": "Autonomous Drone Fleet Manager", + "Generative AI Art": "Generative Art Platform", + "Spatial / Vision OS": "Spatial Computing OS / App", + "Climate Tech": "Sustainable Energy / Climate Tech", +} +UI_RENAMES = { + "Architecture/Interior": "Architecture / Interior", + "Autonomous Drone Fleet": "Autonomous Drone Fleet Manager", + "B2B SaaS Enterprise": "B2B Service", + "Biohacking/Longevity App": "Biohacking / Longevity App", + "Biotech/Life Sciences": "Biotech / Life Sciences", + "Developer Tool/IDE": "Developer Tool / IDE", + "Education": "Educational App", + "Fintech (Banking)": "Fintech/Crypto", + "Government/Public": "Government/Public Service", + "Home Services": "Home Services (Plumber/Electrician)", + "Micro-Credentials/Badges": "Micro-Credentials/Badges Platform", + "Music/Entertainment": "Music Streaming", + "Quantum Computing": "Quantum Computing Interface", + "Real Estate": "Real Estate/Property", + "Remote Work/Collaboration": "Remote Work/Collaboration Tool", + "Restaurant/Food": "Restaurant/Food Service", + "SaaS Dashboard": "Analytics Dashboard", + "Space Tech/Aerospace": "Space Tech / Aerospace", + "Spatial Computing OS": "Spatial Computing OS / App", + "Startup Landing": "Micro SaaS", + "Sustainable Energy/Climate": "Sustainable Energy / Climate Tech", + "Travel/Tourism": "Travel/Tourism Agency", + "Wellness/Mental Health": "Mental Health App", +} + +REMOVE_TYPES = { + "Service Landing Page", "Sustainability/ESG Platform", + "Cleaning Service", "Coffee Shop", + "Consulting Firm", "Conference/Webinar Platform", +} + +# โ”€โ”€โ”€ New color definitions: (primary, secondary, accent, bg, notes) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +# Grouped by category for clarity. Each tuple generates a full 16-token row. +NEW_COLORS = { + # โ”€โ”€ Old #97-#116 that never got colors โ”€โ”€ + "Todo & Task Manager": ("#2563EB","#3B82F6","#059669","#F8FAFC","Functional blue + progress green"), + "Personal Finance Tracker": ("#1E40AF","#3B82F6","#059669","#0F172A","Trust blue + profit green on dark"), + "Chat & Messaging App": ("#2563EB","#6366F1","#059669","#FFFFFF","Messenger blue + online green"), + "Notes & Writing App": ("#78716C","#A8A29E","#D97706","#FFFBEB","Warm ink + amber accent on cream"), + "Habit Tracker": ("#D97706","#F59E0B","#059669","#FFFBEB","Streak amber + habit green"), + "Food Delivery / On-Demand": ("#EA580C","#F97316","#2563EB","#FFF7ED","Appetizing orange + trust blue"), + "Ride Hailing / Transportation":("#1E293B","#334155","#2563EB","#0F172A","Map dark + route blue"), + "Recipe & Cooking App": ("#9A3412","#C2410C","#059669","#FFFBEB","Warm terracotta + fresh green"), + "Meditation & Mindfulness": ("#7C3AED","#8B5CF6","#059669","#FAF5FF","Calm lavender + mindful green"), + "Weather App": ("#0284C7","#0EA5E9","#F59E0B","#F0F9FF","Sky blue + sun amber"), + "Diary & Journal App": ("#92400E","#A16207","#6366F1","#FFFBEB","Warm journal brown + ink violet"), + "CRM & Client Management": ("#2563EB","#3B82F6","#059669","#F8FAFC","Professional blue + deal green"), + "Inventory & Stock Management":("#334155","#475569","#059669","#F8FAFC","Industrial slate + stock green"), + "Flashcard & Study Tool": ("#7C3AED","#8B5CF6","#059669","#FAF5FF","Study purple + correct green"), + "Booking & Appointment App": ("#0284C7","#0EA5E9","#059669","#F0F9FF","Calendar blue + available green"), + "Invoice & Billing Tool": ("#1E3A5F","#2563EB","#059669","#F8FAFC","Navy professional + paid green"), + "Grocery & Shopping List": ("#059669","#10B981","#D97706","#ECFDF5","Fresh green + food amber"), + "Timer & Pomodoro": ("#DC2626","#EF4444","#059669","#0F172A","Focus red on dark + break green"), + "Parenting & Baby Tracker": ("#EC4899","#F472B6","#0284C7","#FDF2F8","Soft pink + trust blue"), + "Scanner & Document Manager": ("#1E293B","#334155","#2563EB","#F8FAFC","Document grey + scan blue"), + # โ”€โ”€ A. Utility / Productivity โ”€โ”€ + "Calendar & Scheduling App": ("#2563EB","#3B82F6","#059669","#F8FAFC","Calendar blue + event green"), + "Password Manager": ("#1E3A5F","#334155","#059669","#0F172A","Vault dark blue + secure green"), + "Expense Splitter / Bill Split":("#059669","#10B981","#DC2626","#F8FAFC","Balance green + owe red"), + "Voice Recorder & Memo": ("#DC2626","#EF4444","#2563EB","#FFFFFF","Recording red + waveform blue"), + "Bookmark & Read-Later": ("#D97706","#F59E0B","#2563EB","#FFFBEB","Warm amber + link blue"), + "Translator App": ("#2563EB","#0891B2","#EA580C","#F8FAFC","Global blue + teal + accent orange"), + "Calculator & Unit Converter": ("#EA580C","#F97316","#2563EB","#1C1917","Operation orange on dark"), + "Alarm & World Clock": ("#D97706","#F59E0B","#6366F1","#0F172A","Time amber + night indigo on dark"), + "File Manager & Transfer": ("#2563EB","#3B82F6","#D97706","#F8FAFC","Folder blue + file amber"), + "Email Client": ("#2563EB","#3B82F6","#DC2626","#FFFFFF","Inbox blue + priority red"), + # โ”€โ”€ B. Games โ”€โ”€ + "Casual Puzzle Game": ("#EC4899","#8B5CF6","#F59E0B","#FDF2F8","Cheerful pink + reward gold"), + "Trivia & Quiz Game": ("#2563EB","#7C3AED","#F59E0B","#EFF6FF","Quiz blue + gold leaderboard"), + "Card & Board Game": ("#15803D","#166534","#D97706","#0F172A","Felt green + gold on dark"), + "Idle & Clicker Game": ("#D97706","#F59E0B","#7C3AED","#FFFBEB","Coin gold + prestige purple"), + "Word & Crossword Game": ("#15803D","#059669","#D97706","#FFFFFF","Word green + letter amber"), + "Arcade & Retro Game": ("#DC2626","#2563EB","#22C55E","#0F172A","Neon red+blue on dark + score green"), + # โ”€โ”€ C. Creator Tools โ”€โ”€ + "Photo Editor & Filters": ("#7C3AED","#6366F1","#0891B2","#0F172A","Editor violet + filter cyan on dark"), + "Short Video Editor": ("#EC4899","#DB2777","#2563EB","#0F172A","Video pink on dark + timeline blue"), + "Drawing & Sketching Canvas": ("#7C3AED","#8B5CF6","#0891B2","#1C1917","Canvas purple + tool teal on dark"), + "Music Creation & Beat Maker": ("#7C3AED","#6366F1","#22C55E","#0F172A","Studio purple + waveform green on dark"), + "Meme & Sticker Maker": ("#EC4899","#F59E0B","#2563EB","#FFFFFF","Viral pink + comedy yellow + share blue"), + "AI Photo & Avatar Generator": ("#7C3AED","#6366F1","#EC4899","#FAF5FF","AI purple + generation pink"), + "Link-in-Bio Page Builder": ("#2563EB","#7C3AED","#EC4899","#FFFFFF","Brand blue + creator purple"), + # โ”€โ”€ D. Personal Life โ”€โ”€ + "Wardrobe & Outfit Planner": ("#BE185D","#EC4899","#D97706","#FDF2F8","Fashion rose + gold accent"), + "Plant Care Tracker": ("#15803D","#059669","#D97706","#F0FDF4","Nature green + sun yellow"), + "Book & Reading Tracker": ("#78716C","#92400E","#D97706","#FFFBEB","Book brown + page amber"), + "Couple & Relationship App": ("#BE185D","#EC4899","#DC2626","#FDF2F8","Romance rose + love red"), + "Family Calendar & Chores": ("#2563EB","#059669","#D97706","#F8FAFC","Family blue + chore green"), + "Mood Tracker": ("#7C3AED","#6366F1","#D97706","#FAF5FF","Mood purple + insight amber"), + "Gift & Wishlist": ("#DC2626","#D97706","#EC4899","#FFF1F2","Gift red + gold + surprise pink"), + # โ”€โ”€ E. Health โ”€โ”€ + "Running & Cycling GPS": ("#EA580C","#F97316","#059669","#0F172A","Energetic orange + pace green on dark"), + "Yoga & Stretching Guide": ("#6B7280","#78716C","#0891B2","#F5F5F0","Sage neutral + calm teal"), + "Sleep Tracker": ("#4338CA","#6366F1","#7C3AED","#0F172A","Night indigo + dream violet on dark"), + "Calorie & Nutrition Counter": ("#059669","#10B981","#EA580C","#ECFDF5","Healthy green + macro orange"), + "Period & Cycle Tracker": ("#BE185D","#EC4899","#7C3AED","#FDF2F8","Blush rose + fertility lavender"), + "Medication & Pill Reminder": ("#0284C7","#0891B2","#DC2626","#F0F9FF","Medical blue + alert red"), + "Water & Hydration Reminder": ("#0284C7","#06B6D4","#0891B2","#F0F9FF","Refreshing blue + water cyan"), + "Fasting & Intermittent Timer":("#6366F1","#4338CA","#059669","#0F172A","Fasting indigo on dark + eating green"), + # โ”€โ”€ F. Social โ”€โ”€ + "Anonymous Community / Confession":("#475569","#334155","#0891B2","#0F172A","Protective grey + subtle teal on dark"), + "Local Events & Discovery": ("#EA580C","#F97316","#2563EB","#FFF7ED","Event orange + map blue"), + "Study Together / Virtual Coworking":("#2563EB","#3B82F6","#059669","#F8FAFC","Focus blue + session green"), + # โ”€โ”€ G. Education โ”€โ”€ + "Coding Challenge & Practice": ("#22C55E","#059669","#D97706","#0F172A","Code green + difficulty amber on dark"), + "Kids Learning (ABC & Math)": ("#2563EB","#F59E0B","#EC4899","#EFF6FF","Learning blue + play yellow + fun pink"), + "Music Instrument Learning": ("#DC2626","#9A3412","#D97706","#FFFBEB","Musical red + warm amber"), + # โ”€โ”€ H. Transport โ”€โ”€ + "Parking Finder": ("#2563EB","#059669","#DC2626","#F0F9FF","Available blue/green + occupied red"), + "Public Transit Guide": ("#2563EB","#0891B2","#EA580C","#F8FAFC","Transit blue + line colors"), + "Road Trip Planner": ("#EA580C","#0891B2","#D97706","#FFF7ED","Adventure orange + map teal"), + # โ”€โ”€ I. Safety & Lifestyle โ”€โ”€ + "VPN & Privacy Tool": ("#1E3A5F","#334155","#22C55E","#0F172A","Shield dark + connected green"), + "Emergency SOS & Safety": ("#DC2626","#EF4444","#2563EB","#FFF1F2","Alert red + safety blue"), + "Wallpaper & Theme App": ("#7C3AED","#EC4899","#2563EB","#FAF5FF","Aesthetic purple + trending pink"), + "White Noise & Ambient Sound": ("#475569","#334155","#4338CA","#0F172A","Ambient grey + deep indigo on dark"), + "Home Decoration & Interior Design":("#78716C","#A8A29E","#D97706","#FAF5F2","Interior warm grey + gold accent"), +} + +# โ”€โ”€โ”€ 1. REBUILD colors.csv โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +def rebuild_colors(): + src = os.path.join(BASE, "colors.csv") + with open(src, newline="", encoding="utf-8") as f: + reader = csv.DictReader(f) + headers = reader.fieldnames + existing = list(reader) + + # Build lookup: Product Type -> row data + color_map = {} + for row in existing: + pt = row.get("Product Type", "").strip() + if not pt: + continue + # Remove deleted types + if pt in REMOVE_TYPES: + print(f" [colors] REMOVE: {pt}") + continue + # Rename mismatched types + if pt in COLOR_RENAMES: + new_name = COLOR_RENAMES[pt] + print(f" [colors] RENAME: {pt} โ†’ {new_name}") + row["Product Type"] = new_name + pt = new_name + color_map[pt] = row + + # Read products.csv to get the correct order + with open(os.path.join(BASE, "products.csv"), newline="", encoding="utf-8") as f: + products = list(csv.DictReader(f)) + + # Build final rows in products.csv order + final_rows = [] + added = 0 + for i, prod in enumerate(products, 1): + pt = prod["Product Type"] + if pt in color_map: + row = color_map[pt] + row["No"] = str(i) + final_rows.append(row) + elif pt in NEW_COLORS: + pri, sec, acc, bg, notes = NEW_COLORS[pt] + new_row = derive_row(pt, pri, sec, acc, bg, notes) + d = dict(zip(headers, [str(i)] + new_row)) + final_rows.append(d) + added += 1 + else: + print(f" [colors] WARNING: No color data for '{pt}' - using defaults") + new_row = derive_row(pt, "#2563EB", "#3B82F6", "#059669", "#F8FAFC", "Auto-generated default") + d = dict(zip(headers, [str(i)] + new_row)) + final_rows.append(d) + added += 1 + + # Write + with open(src, "w", newline="", encoding="utf-8") as f: + writer = csv.DictWriter(f, fieldnames=headers) + writer.writeheader() + writer.writerows(final_rows) + + product_count = len(products) + print(f"\n โœ… colors.csv: {len(final_rows)} rows ({product_count} products)") + print(f" Added: {added} new color rows") + +# โ”€โ”€โ”€ 2. REBUILD ui-reasoning.csv โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +def derive_ui_reasoning(prod): + """Generate ui-reasoning row from products.csv row.""" + pt = prod["Product Type"] + style = prod.get("Primary Style Recommendation", "") + landing = prod.get("Landing Page Pattern", "") + color_focus = prod.get("Color Palette Focus", "") + considerations = prod.get("Key Considerations", "") + keywords = prod.get("Keywords", "") + + # Typography mood derived from style + typo_map = { + "Minimalism": "Professional + Clean hierarchy", + "Glassmorphism": "Modern + Clear hierarchy", + "Brutalism": "Bold + Oversized + Monospace", + "Claymorphism": "Playful + Rounded + Friendly", + "Dark Mode": "High contrast + Light on dark", + "Neumorphism": "Subtle + Soft + Monochromatic", + "Flat Design": "Bold + Clean + Sans-serif", + "Vibrant": "Energetic + Bold + Large", + "Aurora": "Elegant + Gradient-friendly", + "AI-Native": "Conversational + Minimal chrome", + "Organic": "Warm + Humanist + Natural", + "Motion": "Dynamic + Hierarchy-shifting", + "Accessible": "Large + High contrast + Clear", + "Soft UI": "Modern + Accessible + Balanced", + "Trust": "Professional + Serif accents", + "Swiss": "Grid-based + Mathematical + Helvetica", + "3D": "Immersive + Spatial + Variable", + "Retro": "Nostalgic + Monospace + Neon", + "Cyberpunk": "Terminal + Monospace + Neon", + "Pixel": "Retro + Blocky + 8-bit", + } + typo_mood = "Professional + Clear hierarchy" + for key, val in typo_map.items(): + if key.lower() in style.lower(): + typo_mood = val + break + + # Key effects from style + eff_map = { + "Glassmorphism": "Backdrop blur (10-20px) + Translucent overlays", + "Neumorphism": "Dual shadows (light+dark) + Soft press 150ms", + "Claymorphism": "Multi-layer shadows + Spring bounce + Soft press 200ms", + "Brutalism": "No transitions + Hard borders + Instant feedback", + "Dark Mode": "Subtle glow + Neon accents + High contrast", + "Flat Design": "Color shift hover + Fast 150ms transitions + No shadows", + "Minimalism": "Subtle hover 200ms + Smooth transitions + Clean", + "Motion-Driven": "Scroll animations + Parallax + Page transitions", + "Micro-interactions": "Haptic feedback + Small 50-100ms animations", + "Vibrant": "Large section gaps 48px+ + Color shift hover + Scroll-snap", + "Aurora": "Flowing gradients 8-12s + Color morphing", + "AI-Native": "Typing indicator + Streaming text + Context reveal", + "Organic": "Rounded 16-24px + Natural shadows + Flowing SVG", + "Soft UI": "Improved shadows + Modern 200-300ms + Focus visible", + "3D": "WebGL/Three.js + Parallax 3-5 layers + Physics 300-400ms", + "Trust": "Clear focus rings + Badge hover + Metric pulse", + "Accessible": "Focus rings 3-4px + ARIA + Reduced motion", + } + key_effects = "Subtle hover (200ms) + Smooth transitions" + for key, val in eff_map.items(): + if key.lower() in style.lower(): + key_effects = val + break + + # Decision rules + rules = {} + if "dark" in style.lower() or "oled" in style.lower(): + rules["if_light_mode_needed"] = "provide-theme-toggle" + if "glass" in style.lower(): + rules["if_low_performance"] = "fallback-to-flat" + if "conversion" in landing.lower(): + rules["if_conversion_focused"] = "add-urgency-colors" + if "social" in landing.lower(): + rules["if_trust_needed"] = "add-testimonials" + if "data" in keywords.lower() or "dashboard" in keywords.lower(): + rules["if_data_heavy"] = "prioritize-data-density" + if not rules: + rules["if_ux_focused"] = "prioritize-clarity" + rules["if_mobile"] = "optimize-touch-targets" + + # Anti-patterns + anti_patterns = [] + if "minimalism" in style.lower() or "minimal" in style.lower(): + anti_patterns.append("Excessive decoration") + if "dark" in style.lower(): + anti_patterns.append("Pure white backgrounds") + if "flat" in style.lower(): + anti_patterns.append("Complex shadows + 3D effects") + if "vibrant" in style.lower(): + anti_patterns.append("Muted colors + Low energy") + if "accessible" in style.lower(): + anti_patterns.append("Color-only indicators") + if not anti_patterns: + anti_patterns = ["Inconsistent styling", "Poor contrast ratios"] + anti_str = " + ".join(anti_patterns[:2]) + + return { + "UI_Category": pt, + "Recommended_Pattern": landing, + "Style_Priority": style, + "Color_Mood": color_focus, + "Typography_Mood": typo_mood, + "Key_Effects": key_effects, + "Decision_Rules": json.dumps(rules), + "Anti_Patterns": anti_str, + "Severity": "HIGH" + } + + +def rebuild_ui_reasoning(): + src = os.path.join(BASE, "ui-reasoning.csv") + with open(src, newline="", encoding="utf-8") as f: + reader = csv.DictReader(f) + headers = reader.fieldnames + existing = list(reader) + + # Build lookup + ui_map = {} + for row in existing: + cat = row.get("UI_Category", "").strip() + if not cat: + continue + if cat in REMOVE_TYPES: + print(f" [ui-reason] REMOVE: {cat}") + continue + if cat in UI_RENAMES: + new_name = UI_RENAMES[cat] + print(f" [ui-reason] RENAME: {cat} โ†’ {new_name}") + row["UI_Category"] = new_name + cat = new_name + ui_map[cat] = row + + with open(os.path.join(BASE, "products.csv"), newline="", encoding="utf-8") as f: + products = list(csv.DictReader(f)) + + final_rows = [] + added = 0 + for i, prod in enumerate(products, 1): + pt = prod["Product Type"] + if pt in ui_map: + row = ui_map[pt] + row["No"] = str(i) + final_rows.append(row) + else: + row = derive_ui_reasoning(prod) + row["No"] = str(i) + final_rows.append(row) + added += 1 + + with open(src, "w", newline="", encoding="utf-8") as f: + writer = csv.DictWriter(f, fieldnames=headers) + writer.writeheader() + writer.writerows(final_rows) + + print(f"\n โœ… ui-reasoning.csv: {len(final_rows)} rows") + print(f" Added: {added} new reasoning rows") + + +# โ”€โ”€โ”€ MAIN โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +if __name__ == "__main__": + print("=== Rebuilding colors.csv ===") + rebuild_colors() + print("\n=== Rebuilding ui-reasoning.csv ===") + rebuild_ui_reasoning() + print("\n๐ŸŽ‰ Done!") diff --git a/codex/skills/ui-ux-pro-max/data/app-interface.csv b/codex/skills/ui-ux-pro-max/data/app-interface.csv new file mode 100644 index 0000000..f34c3cd --- /dev/null +++ b/codex/skills/ui-ux-pro-max/data/app-interface.csv @@ -0,0 +1,31 @@ +No,Category,Issue,Keywords,Platform,Description,Do,Don't,Code Example Good,Code Example Bad,Severity +1,Accessibility,Icon Button Labels,icon button accessibilityLabel,iOS/Android/React Native,Icon-only buttons must expose an accessible label,Set accessibilityLabel or label prop on icon buttons,Icon buttons without accessible names,"","",Critical +2,Accessibility,Form Control Labels,form input label accessibilityLabel,iOS/Android/React Native,All inputs must have a visible label and an accessibility label,Pair Text label with input and set accessibilityLabel,Inputs with placeholder only,"Email","",Critical +3,Accessibility,Role & Traits,accessibilityRole accessibilityTraits,iOS/Android/React Native,Interactive elements must expose correct roles/traits,Use accessibilityRole/button/link/checkbox etc.,Rely on generic views with no roles,"Submit","Submit",High +4,Accessibility,Dynamic Updates,accessibilityLiveRegion announce,iOS/Android/React Native,Async status updates should be announced to screen readers,Use accessibilityLiveRegion or announceForAccessibility,Update text silently with no announcement,"{status}","{status}",Medium +5,Accessibility,Decorative Icons,accessible={false} importantForAccessibility,iOS/Android/React Native,Decorative icons should be hidden from screen readers,Mark decorative icons as not accessible,Have screen reader read every icon,"","",Medium +6,Touch,Touch Target Size,touch 44x44 hitSlop,iOS/Android/React Native,Primary touch targets must be at least 44x44pt,Increase hitSlop or padding to meet minimum,Small icons with tiny touch area,"","",Critical +7,Touch,Touch Spacing,touch spacing gap 8px,iOS/Android/React Native,Adjacent touch targets need enough spacing,Keep at least 8dp spacing between touchables,Cluster many buttons with no gap,"