ArtCraft: ‏14 برنامجًا حرًّا للإبداع، مثل Photoshop وPremiere وWord وAutoCAD

🌐 Español · English · Português · Français · Italiano · Deutsch · Nederlands · Polski · Русский · Українська · Türkçe · Ελληνικά · العربية · فارسی · עברית · हिन्दी · বাংলা · தமிழ் · Bahasa Indonesia · Tiếng Việt · ไทย · 中文 · 日本語 · 한국어 · Kiswahili

القصة: رسوم إلغاء زائدة

القصة المتداولة هذه الأسابيع في المنتديات ومواقع التواصل أن ArtCraft وُلد من الغضب: مؤلفه «تعرّض للاحتيال». يقف خلف المشروع براندون توماس (Brandon Thomas)، مهندس برمجيات من أتلانتا (الولايات المتحدة) بخبرة خمسة عشر عامًا، كان يكتب على Reddit باسم u/ai_art_is_art. كلمة «احتيال» من الناس؛ أما هو فيروي أن Adobe اقتطعت منه رسوم إلغاء اشتراكه.

كتب على Reddit: «اقتطعوا مني “رسوم الإلغاء” مرات عدة، وأنا أكرههم منذ ذلك الحين». وقال على Hacker News إنه وقع في هذا «النمط المظلم» مرات كثيرة: الغرامة التي تُفرض عند ترك خطة سنوية تُدفع شهريًا قبل أوانها. وليست شكوى فردية: ففي يونيو 2024 رفعت وزارة العدل الأمريكية دعوى على Adobe لإخفائها هذه الرسوم وتعقيدها الإلغاء. وتؤكد Adobe أن شروطها شفافة.

كان ردّه أن يعيد صنع البرامج ويوزعها مجانًا. بمساعدة Claude، الذكاء الاصطناعي من Anthropic، ولغة Rust، نشر على GitHub بين أواخر سبتمبر وأوائل أكتوبر 2026 أول سبعة تطبيقات —ما يقابل Photoshop وIllustrator وPremiere وLightroom وAfter Effects وInDesign وAcrobat— ثم أضاف خلال أسابيع قليلة ما يقابل Word وPowerPoint وExcel وAutoCAD وPro Tools وFigma. ويؤكد أنها إعادة تنفيذ «في غرفة نظيفة»، مبنية على المواصفات العلنية لصيغ الملفات، دون شيفرة أحد.

عبارته التي لخّص بها كل شيء، «انتهى عصر البرمجيات»، جابت العالم: إن كان شخص واحد مع الذكاء الاصطناعي قادرًا على إعادة صنع ما استغرق عقودًا في أسابيع، فالبرمجيات المدفوعة في ورطة. وهناك من يشكك: يذكّر البروفيسور أندريس غوادَموز من جامعة ساسكس بأننا لا نعرف بعد إن كانت عملية «الغرفة النظيفة» تصمد أمام الفحص، ولا كم من الشيفرة كتبها هو وكم كتب الذكاء الاصطناعي. ويصرّ توماس على أنها ليست «فورة عابرة ستنطفئ»، وحتى الآن يؤيده إيقاع الإصدارات شبه اليومي.

ArtCraft مشروع مفتوح المصدر (رخصة MIT أو Apache-2.0) لفريق storytold، يعيد من الصفر وبلغة Rust كتابة الأدوات التي نستخدمها يوميًا للإبداع. إنها برامج أصلية وخفيفة بلا اشتراكات، تحاكي طريقة عمل أشهر البرامج التجارية دون نسخ شيفرتها. تعمل على Windows وmacOS وLinux وFreeBSD، وكثير منها في المتصفح أيضًا، وتصدر إصدارات جديدة كل أسبوع تقريبًا. الأسماء التجارية المذكورة للتوضيح فقط: لا علاقة لهذه البرامج بتلك العلامات.

الأدوات واحدة تلو الأخرى

ArtCraft Launcher v0.8.1

ابدأ من هنا. إنه مثبّت جميع البرامج الأخرى: يعرض الكتالوج كاملًا، ويثبّت كل تطبيق بنقرة واحدة مختارًا الإصدار المناسب لجهازك، ويتحقق من سلامة الملف، وينبّهك عند صدور تحديثات. لا توجد حزمة واحدة تضم كل شيء، لكن هذا المشغّل هو الأقرب إلى ذلك.

PhotoCraft v0.16.0

محرر الصور، على نهج Photoshop: طبقات وتحديدات وفُرَش وتعديلات وتنقيح. وهو أشهر أدوات المشروع على GitHub.

LightCraft v0.14.0

مكتبة صور وتحميض RAW غير إتلافي، على طريقة Lightroom: تنظّم صورك وتضبط التعريض واللون والقص دون المساس بالملف الأصلي أبدًا.

VectorCraft v0.17.0

رسم متجهي كما في Illustrator: منحنيات وأشكال وخطوط ونصوص تُكبَّر دون فقدان الجودة. مناسب للشعارات والأيقونات والملصقات.

FilmCraft v0.13.0

محرر فيديو غير خطي يشبه Premiere Pro: خط زمني متعدد المسارات، وقص، وتصحيح ألوان، وصوت.

EffectCraft v0.17.0

رسوم متحركة وتركيب ومؤثرات بصرية على نهج After Effects: عناوين، وتحريك بالإطارات المفتاحية، وطبقات تُدمج معًا.

SoundCraft v0.13.1

محطة صوت للتسجيل والتحرير والمزج، مستوحاة من Pro Tools: مسارات متعددة، وقص، ومؤثرات، ومزج نهائي.

DesignCraft v0.13.0

تنضيد ونشر على طريقة InDesign: مجلات وكتيّبات وكتب بصفحات وأعمدة وأنماط نصوص وصور.

FigCraft v0.11.2

تصميم الواجهات والنماذج الأولية، مثل Figma: شاشات تطبيقات ومواقع، ومكوّنات قابلة لإعادة الاستخدام، ونماذج توضح كيف سيعمل الشيء قبل بنائه.

PdfCraft v0.14.0

لقراءة ملفات PDF وتنظيمها وحمايتها، على نهج Acrobat: فتح وتعليق وإعادة ترتيب الصفحات وحفظ مستندات محمية.

WordCraft v0.11.0

معالج نصوص يشبه Word، للرسائل والتقارير والمستندات مع الأنماط والجداول والصور.

DeckCraft v0.11.1

عروض تقديمية وشرائح على طريقة PowerPoint: شرائح بنصوص وصور وأشكال للعرض بملء الشاشة.

GridCraft v0.12.1

جدول بيانات مثل Excel: صيغ وجداول بيانات ورسوم بيانية وفرز المعلومات وتصفيتها.

CADCraft v0.13.0

تصميم بمساعدة الحاسوب ورسم هندسي على نهج AutoCAD: مخططات بقياسات دقيقة وطبقات وأبعاد.

ArtCraft v0.41.0

التطبيق الذي يحمل المشروع اسمه، والوحيد المستقل عن البقية: استوديو لإنشاء الصور والفيديو بالذكاء الاصطناعي، موجّه للفنانين والمصممين وصنّاع الأفلام. متاح حاليًا لنظامي Windows وMac فقط.

احفظها كلها: الشيفرة المصدرية والملفات التنفيذية

إن كنت تحب الاحتفاظ بنسخة من كل شيء (تحسّبًا لاختفاء مشروع يومًا ما، أو للتثبيت دون إنترنت)، فهذا السكربت لنظام Linux ينزّل الشيفرة المصدرية لكل أداة وجميع الملفات التنفيذية لآخر إصدار، مرتبةً في مجلدات حسب الأداة والنظام (Windows وmacOS وLinux وFreeBSD والويب). فُكّ ضغطه وشغّله؛ الوسيط هو مجلد الوجهة، وبالمتغيرين PLATFORMS وEXCLUDE تختار ما تنزّله. (تعليقاته بالإسبانية.)

⬇ تنزيل السكربت (artcraft-mirror.zip)

chmod +x artcraft-mirror.sh
./artcraft-mirror.sh ~/artcraft

PLATFORMS="windows linux" EXCLUDE='riscv64|aarch64' ./artcraft-mirror.sh ~/artcraft
  • يمكن إيقافه: إن توقف، شغّله مجددًا فيكمل من حيث توقف.
  • ينزّل الجديد فقط: عند صدور إصدار ينشئ مجلده ولا يمسّ المجلدات السابقة.
  • يتحقق من كل شيء بمجاميع SHA-256 التي ينشرها كل إصدار.
  • يشغل مساحة كبيرة: جميع المنصات معًا نحو 24 غيغابايت لكل إصدار؛ إن كنت تحتاج بعضها فقط، فاستخدم التصفية.
عرض السكربت كاملًا
#!/usr/bin/env bash
# artcraft-mirror.sh — espejo local de las Craft Apps de ArtCraft (github.com/storytold)
#
# Estructura que genera:
#   <DEST>/<herramienta>/code/<rama>-<herramienta>-AAAAMMDD.zip   (+ .sha con el commit)
#   <DEST>/<herramienta>/release-<versión>/{windows,macos,linux,freebsd,web,otros}/...
#   <DEST>/<herramienta>/release-<versión>/SHA256SUMS.txt
#
# - Reanudable: las descargas van a *.part y continúan con Range (curl -C -).
#   Una release se da por terminada sólo cuando todo se verificó contra SHA256SUMS
#   (queda el marcador .complete). Si se corta, se vuelve a ejecutar y sigue.
# - Incremental: sólo mira la última release de cada herramienta; las carpetas
#   release-* anteriores y los zip de código de días anteriores no se tocan.
# - El código fuente se baja sólo si el commit de la rama principal cambió.
#
# Uso:
#   ./artcraft-mirror.sh [DEST]           (DEST por defecto: carpeta del script)
# Variables opcionales:
#   GITHUB_TOKEN=ghp_...                  sube el límite de la API de 60 a 5000 llamadas/h
#   PLATFORMS="windows linux macos"       sólo estas plataformas (por defecto todas)
#   EXCLUDE='riscv64|aarch64|flatpak'     regex de nombres de archivo a omitir
#   TOOLS="photocraft cadcraft"           sólo estas herramientas
#   EXTRA_TOOLS="anicraft"                añadir repos de storytold no incluidos en el catálogo
#   SKIP_CODE=1 / SKIP_RELEASES=1

set -uo pipefail

OWNER=storytold
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
DEST=${1:-$SCRIPT_DIR}
PLATFORMS=${PLATFORMS:-"windows macos linux freebsd web otros"}
EXCLUDE=${EXCLUDE:-}
TODAY=$(date +%Y%m%d)

# Catálogo de respaldo por si no se puede leer el del launcher
FALLBACK_TOOLS="photocraft vectorcraft filmcraft lightcraft pdfcraft effectcraft designcraft
figcraft wordcraft soundcraft deckcraft cadcraft gridcraft"
# Fuera del catálogo del launcher pero con releases propias
ALWAYS_TOOLS="craft-launcher artcraft"

for bin in curl jq git sha256sum flock; do
    command -v "$bin" >/dev/null || { echo "Falta '$bin'" >&2; exit 1; }
done

mkdir -p "$DEST"
exec 9>"$DEST/.artcraft-mirror.lock"
flock -n 9 || { echo "Ya hay otra ejecución en curso en $DEST" >&2; exit 1; }

log()  { printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*"; }
warn() { printf '[%s] AVISO: %s\n' "$(date +%H:%M:%S)" "$*" >&2; }

api() {
    local auth=()
    [[ -n ${GITHUB_TOKEN:-} ]] && auth=(-H "Authorization: Bearer $GITHUB_TOKEN")
    curl -fsSL --retry 3 -H "Accept: application/vnd.github+json" "${auth[@]}" \
        "https://api.github.com/$1"
}

# Descarga: $1 url, $2 destino final, $3 tamaño esperado.
# Con tamaño conocido continúa el .part (Range); sin él (zip de código, generado
# al vuelo y sin Range) empieza de cero.
fetch() {
    local url=$1 out=$2 size=${3:-} part="$2.part" have resume=()
    mkdir -p "$(dirname "$out")"
    if [[ -n $size && -f $part ]]; then
        have=$(stat -c %s "$part")
        if (( have == size )); then mv -f "$part" "$out"; return 0; fi
        if (( have > size )); then rm -f "$part"; fi
        resume=(-C -)
    else
        rm -f "$part"
    fi
    curl -fL --retry 5 --retry-delay 5 --retry-all-errors "${resume[@]}" \
        --progress-bar -o "$part" "$url" || return 1
    mv -f "$part" "$out"
}

platform_of() {
    case $1 in
        SHA256SUMS*)                              echo . ;;
        *windows*|*.msi|*.exe)                    echo windows ;;
        *macos*|*.dmg|*.app.tar.gz|*universal*)   echo macos ;;
        *freebsd*)                                echo freebsd ;;
        *linux*|*.AppImage*|*.deb|*.rpm|*.flatpak) echo linux ;;
        *-web-*)                                  echo web ;;
        *)                                        echo otros ;;
    esac
}

wanted() {
    local name=$1 plat=$2
    [[ $plat == . ]] && return 0
    [[ " $PLATFORMS " == *" $plat "* ]] || return 1
    [[ -n $EXCLUDE && $name =~ $EXCLUDE ]] && return 1
    return 0
}

tool_list() {
    if [[ -n ${TOOLS:-} ]]; then echo $TOOLS; return; fi
    local cat
    cat=$(curl -fsSL "https://raw.githubusercontent.com/$OWNER/craft-launcher/main/crates/core/src/catalog.rs" 2>/dev/null \
          | grep -oE "\"$OWNER/[a-z0-9-]+\"" | tr -d '"' | cut -d/ -f2 | grep -v '^craft-launcher$' | sort -u)
    [[ -z $cat ]] && { warn "no pude leer el catálogo del launcher; uso la lista interna"; cat=$FALLBACK_TOOLS; }
    echo $cat $ALWAYS_TOOLS ${EXTRA_TOOLS:-} | tr ' ' '\n' | awk 'NF && !seen[$0]++'
}

mirror_code() {
    local tool=$1 dir="$DEST/$1/code" head branch sha last
    head=$(git ls-remote --symref "https://github.com/$OWNER/$tool" HEAD 2>/dev/null) \
        || { warn "$tool: no pude consultar el repositorio"; return 1; }
    branch=$(awk '/^ref:/ {sub("refs/heads/","",$2); print $2}' <<<"$head")
    sha=$(awk '$2=="HEAD" && $1!="ref:" {print $1}' <<<"$head")
    mkdir -p "$dir"

    last=$(ls -1 "$dir"/*.zip.sha 2>/dev/null | sort | tail -1)
    if [[ -n $last && $(<"$last") == "$sha" ]]; then
        log "$tool: código al día ($branch@${sha:0:8})"
        return 0
    fi
    local zip="$dir/$branch-$tool-$TODAY.zip"
    log "$tool: bajando código $branch@${sha:0:8} → $(basename "$zip")"
    # El zip por commit garantiza que el archivo corresponde al sha anotado
    fetch "https://github.com/$OWNER/$tool/archive/$sha.zip" "$zip" || { warn "$tool: falló el zip"; return 1; }
    echo "$sha" > "$zip.sha"
}

verify() { # $1 archivo, $2 SHA256SUMS → 0 ok / 1 mal / 2 sin suma publicada
    local file=$1 sums=$2 expected
    [[ -f $sums ]] || return 2
    expected=$(awk -v n="$(basename "$file")" '{f=$2; sub(/^\*/,"",f)} f==n {print $1}' "$sums")
    [[ -z $expected ]] && return 2
    [[ $(sha256sum "$file" | cut -d' ' -f1) == "$expected" ]]
}

mirror_release() {
    local tool=$1 json tag ver rdir
    json=$(api "repos/$OWNER/$tool/releases/latest") || { warn "$tool: sin releases o límite de API alcanzado"; return 1; }
    tag=$(jq -r .tag_name <<<"$json")
    ver=${tag#"$tool"-}; ver=${ver#v}
    rdir="$DEST/$tool/release-$ver"

    if [[ -f $rdir/.complete && $(<"$rdir/.complete") == "$PLATFORMS|$EXCLUDE" ]]; then
        log "$tool: release $ver ya completa"
        return 0
    fi
    log "$tool: release $ver"
    mkdir -p "$rdir"
    jq -c '{tag_name, name, published_at, html_url, body}' <<<"$json" > "$rdir/release.json"

    local ok=1 name url size plat out sums="$rdir/SHA256SUMS.txt"
    # SHA256SUMS primero para poder verificar el resto
    while IFS=$'\t' read -r name url size; do
        plat=$(platform_of "$name")
        wanted "$name" "$plat" || continue
        if [[ $plat == . ]]; then out="$rdir/$name"; else out="$rdir/$plat/$name"; fi

        if [[ -f $out ]]; then
            verify "$out" "$sums"; case $? in
                0) continue ;;
                2) [[ $(stat -c %s "$out") == "$size" ]] && continue ;;
            esac
            warn "$tool: $name no coincide, se vuelve a bajar"; rm -f "$out"
        fi
        log "  ↓ ${out#"$rdir"/} ($((size / 1048576)) MiB)"
        if ! fetch "$url" "$out" "$size"; then warn "  falló $name"; ok=0; continue; fi
        verify "$out" "$sums"; case $? in
            1) warn "  checksum INCORRECTO: $name (se borra)"; rm -f "$out"; ok=0 ;;
            2) [[ $name != SHA256SUMS* ]] && [[ $(stat -c %s "$out") != "$size" ]] && { warn "  tamaño incorrecto: $name"; rm -f "$out"; ok=0; } ;;
        esac
    done < <(jq -r '.assets | sort_by(.name | startswith("SHA256SUMS") | not)[]
                    | [.name, .browser_download_url, .size] | @tsv' <<<"$json")

    if (( ok )); then echo "$PLATFORMS|$EXCLUDE" > "$rdir/.complete"; log "$tool: release $ver completa ✔"
    else warn "$tool: release $ver incompleta; vuelve a ejecutar el script para continuar"; return 1; fi
}

fail=0
for tool in $(tool_list); do
    echo "──────── $tool"
    [[ -z ${SKIP_CODE:-} ]]     && { mirror_code "$tool"    || fail=1; }
    [[ -z ${SKIP_RELEASES:-} ]] && { mirror_release "$tool" || fail=1; }
done

(( fail )) && { warn "Hubo errores; ejecuta de nuevo para reintentar lo pendiente."; exit 1; }
log "Todo al día en $DEST"

هذه برامج حديثة جدًا، ومعظمها موسوم بـألفا مبكرة: واعدة جدًا، لكنها لا تحلّ بعدُ محل الأدوات الاحترافية في العمل الجاد. جرّبها، واحتفظ بنسخ من ملفاتك، وأبلغ عن الأخطاء في GitHub الخاص بها.

المراجع