diff options
Diffstat (limited to 'contrib')
-rwxr-xr-x | contrib/gen-meta.sh | 145 | ||||
-rwxr-xr-x | contrib/get-cursors.sh | 80 | ||||
-rw-r--r-- | contrib/util.sh | 215 |
3 files changed, 440 insertions, 0 deletions
diff --git a/contrib/gen-meta.sh b/contrib/gen-meta.sh new file mode 100755 index 0000000..2f51e80 --- /dev/null +++ b/contrib/gen-meta.sh @@ -0,0 +1,145 @@ +#!/bin/bash +# Generates usable metadata for the cursors, allowing them to be more easily processed +# TODO: Rewite in Amber - https://amber-lang.com +set -e + +source contrib/util.sh + +CANCER_PATH="$(realpath cancer-meta.toml)" +CANCER="$(yq e -p toml -o json . "$CANCER_PATH")" +get_meta_from_cancer() { + jq -Mc '.cursor[]' <<< "$CANCER" | while read cursor_json; do + if [[ "$(jq -Mc .id <<< "$cursor_json")" == "$1" ]]; then + echo -n "$cursor_json" + return 0 + fi + done +} + +cd cursors +# Process metadata into usable files +for type in ./*; do + type="$(sed 's|./||' <<< "$type")" + start "Processing CursorType($type)" + cd "$type" + for cursor in ./*; do + cursor="$(sed 's|./||' <<< "$cursor")" + if [[ -d "$cursor" ]]; then + cd "$cursor" + cursor_meta="./meta.json" + cursor_assets="./assets" + get_from_orig() { + jq -Mc "$1" "$cursor_meta" + } + get_from_orig_raw() { + jq -Mcr "$1" "$cursor_meta" + } + + is_animated="$(get_from_orig .isAnimated)" + start "Processing Cursor(CursorType($type), name=$cursor, animated=$is_animated)" + + if [[ "$is_animated" == "true" ]]; then + # Animated files tend to have lots of SVGs, resulting in us being unable to store the entire JSON object in memory + # This results in us getting an "Argument list too long" error - even though its not the arg list, but rather the env + # We solve this by placing the temporary storage on a real file on the filesystem + jsettempstorage "/tmp/_$(date)" + else + # Otherwise, we (dangerously!) assume the file will fit in env, and clear the temp storage. + jsettempstorage + fi + + additional_meta="$(get_meta_from_cancer "$(get_from_orig .name)")" + + jnew "./cursor.json" + jtransaction + jrun ".animated=$is_animated" + jrun ".id=$(get_from_orig .id)" + jrun ".name=$(get_from_orig .name)" + jrun ".aliases=$(jq -Mc 'if has("links") then .links else [] end' <<< "$additional_meta")" + jrun ".hot={}" + jrun ".hot.x=$(jq -Mc 'if has("x") then .x else 128 end' <<< "$additional_meta")" + jrun ".hot.y=$(jq -Mc 'if has("y") then .y else 128 end' <<< "$additional_meta")" + jrun ".platform_names={}" + jrun ".platform_names.windows=$(jq -Mc 'if has("winname") then .winname else null end' <<< "$additional_meta")" + jrun ".platform_names.xorg=$(jq -Mc 'if has("xname") then .xname else .id end' <<< "$additional_meta")" + jrun ".frames=[]" + idx=0 + trx_ctr=1 + while read node_id; do + url="$(jq -Mc ".urls[${idx}]" < "$cursor_meta")" + raw_node_id="$(jq -Mcr . <<< "$node_id")" + frame="$cursor_assets/$raw_node_id.svg" + [[ "$is_animated" == "true" ]] && start "Processing CursorFrame(Cursor(CursorType($type), name=$cursor, animated=$is_animated), frame=$raw_node_id)" + if [[ "$trx_ctr" == "1" ]]; then + jcommit + jtransaction + trx_ctr=32 + else + ((trx_ctr=trx_ctr-1)) + fi + jrun ".frames[$idx]={}" + jrun ".frames[$idx].idx=$idx" + jrun ".frames[$idx].id=$node_id" + jrun ".frames[$idx].url=$url" + jrun ".frames[$idx].delay=(if $node_id | contains(\":\") then $node_id | split(\":\") | .[0] | tonumber else 60 end)" + jrun ".frames[$idx].sha512=$(escape_str "$(sha512sum "$frame" | awk '{print $1}')")" + jsetasset "$(cat "$frame")" && jrun ".frames[$idx].svg=$(jgetassetref)" + [[ "$is_animated" == "true" ]] && complete "Finished $(_last | sed s/Pr/pr/)" + ((idx=idx+1)) + done <<< "$(jq -Mc '.node_ids[]' "$cursor_meta")" + jrun ".frame_count=${idx}" + jcommit + jsave + + # We're done with the file, let's empty the storage + jnew + # If we started using tmpfs, let's go back to in-memory storage + if [[ "$is_animated" == "true" ]]; then + jsettempstorage + fi + + complete "Finished $(_last | sed s/Pr/pr/)" + + cd .. + fi + done + complete "Finished $(_last | sed s/Pr/pr/)" + cd .. +done +# Process outputs into single file +jsettempstorage "/tmp/_$(date)" +jnew ../.cursors +jtransaction +jrun '.=[]' +typeidx=0 +for type in ./*; do + type="$(sed 's|./||' <<< "$type")" + start "Bundle all CursorType($type)" + jrun ".[${typeidx}]={}" + jrun ".[${typeidx}].kind=$(escape_str "$type")" + jrun ".[${typeidx}].cursors=[]" + jcommit + jsave + trx_ctr=1 + cd "$type" + for cursor in ./*; do + cursor="$(sed 's|./||' <<< "$cursor")" + if [[ -d "$cursor" ]]; then + start "Push Cursor(CursorType($type), name=$cursor, animated=null)" + cd "$cursor" + _jrun_file ".[${typeidx}].cursors += [$(jq -M . ./cursor.json)]" + cd .. + complete + fi + done + complete + jtransaction + cd .. + ((typeidx=typeidx+1)) +done +jcommit +jsave + +cd .. + +jq -Mc . .cursors > .cursors.min diff --git a/contrib/get-cursors.sh b/contrib/get-cursors.sh new file mode 100755 index 0000000..755213a --- /dev/null +++ b/contrib/get-cursors.sh @@ -0,0 +1,80 @@ +#!/bin/bash +# Fetches bibata cursor svgs +set -e + +start() { + _LAST_MSG="$@" + echo -e "\x1b[0;1;34m@\x1b[0;34m $@\x1b[0m" +} +complete() { + if [[ "$1" == "" ]]; then + complete "$_LAST_MSG" + else + echo -e "\x1b[0;1;32m+\x1b[0;32m $@\x1b[0m" + fi +} +err() { + if [[ "$1" == "" ]]; then + err "$_LAST_MSG" + else + echo -e "\x1b[0;1;31m!\x1b[0;31m $@\x1b[0m" 1>&2; + fi +} +info() { + echo -e "\x1b[0;1;94mi\x1b[0;94m $@\x1b[0m" 1>&2; +} + +mkdir -p cursors +for type in Modern Original; do + type_dir="cursors/$type" + type_meta="$type_dir/meta.json" + mkdir -p "$type_dir" + start "Get Cursor Metadata for CursorType($type)" + curl -fsSL "https://www.bibata.live/api/svg?type=${type}&v=1.0.2" | jq -M > "$type_meta" + complete + + jq -Mc '.data[]' "$type_meta" | while read cursor_json; do + cursor_name="$(jq -r .name <<< "$cursor_json")" + cursor_dir="$type_dir/$cursor_name" + cursor_assets="$cursor_dir/assets" + cursor_meta="$cursor_dir/meta.json" + cursorfile="$cursor_dir/cursor.json" + mkdir -p "$cursor_dir" "$cursor_assets" + if [[ -f "$cursor_meta" ]] && [[ "$(jq -M "." "$cursor_meta" | sha512sum | awk '{print $1}')" == "$(jq -M "." <<< "$cursor_json" | sha512sum | awk '{print $1}')" ]]; then + start "Ensuring all keyframes for Cursor($type, $cursor_name) are present" + idx=0 + jq -Mcr '.node_ids[]' "$cursor_meta" | while read node_id; do + node="Node($type, $cursor_name, Node($idx, $node_id))" + if ! [[ -f "$cursor_assets/$node_id.svg" ]]; then + cursor_url="$(jq -Mr ".urls[${idx}]" "$cursor_meta")" + start "Fetch missing $node - NodeIndex($idx)" + curl -fsSL "$cursor_url" -o "$cursor_assets/$node_id.svg" + ((idx=idx+1)) + complete "Fetched missing $node" + else + info "$node is already present" + fi + done + complete "Ensured all keyframes for Cursor($type, $cursor_name) are present" + else + if [[ -f "$cursor_meta" ]]; then info "MetaFile($cursor_meta) already exists but differs (it has FileHash(sha512, $(jq -M "$cursor_meta" | sha512sum | awk '{print $1}')), new version has FileHash(sha512, $(jq -M <<< "$cursor_json" | sha512sum | awk '{print $1}')))"; mv "$cursor_meta"; fi + jq -M <<< "$cursor_json" > "$cursor_meta" + start "Get keyframes for Cursor($type, $cursor_name)" + idx=0 + total=0 + jq -Mcr '.node_ids[]' "$cursor_meta" | while read id; do + rm -f "$cursor_assets/$node_id.svg" + ((total=total+1)) + done + jq -Mcr '.urls[]' "$cursor_meta" | while read cursor_url; do + node_id="$(jq -Mr ".node_ids[${idx}]" "$cursor_meta")" + node="Node($type, $cursor_name, Node($idx, $node_id))" + start "Fetch $node - NodeIndex($idx) of NodeCount($total)" + curl -fsSL "$cursor_url" -o "$cursor_assets/$node_id.svg" + ((idx=idx+1)) + complete "Fetched $node" + done + complete "Got keyframes for Cursor($type, $cursor_name)" + fi + done +done diff --git a/contrib/util.sh b/contrib/util.sh new file mode 100644 index 0000000..5f846be --- /dev/null +++ b/contrib/util.sh @@ -0,0 +1,215 @@ +#!/bin/bash +set -e + +# Logging +TASKS=() +_last() { + echo -n "${TASKS[0]}" +} +start() { + for n in "${TASKS[@]}"; do + echo -n ' ' + done + TASKS=( + "$@" + "${TASKS[@]}" + ) + echo -e "\x1b[0;1;34m@\x1b[0;34m $@\x1b[0m" +} +complete() { + last="$(_last)" + if [[ "$NOSHIFT" != 1 ]]; then + TASKS=( + "${TASKS[@]:1}" + ) + fi + if [[ "$1" == "" ]] && [[ "$last" != "" ]]; then + NOSHIFT=1 complete "$last" + else + for n in "${TASKS[@]}"; do + echo -n ' ' + done + echo -e "\x1b[0;1;32m+\x1b[0;32m $@\x1b[0m" + fi +} +err() { + last="$(_last)" + if [[ "$NOSHIFT" != 1 ]]; then + TASKS=( + "${TASKS[@]:1}" + ) + fi + if [[ "$1" == "" ]] && [[ "$last" != "" ]]; then + NOSHIFT=1 err "$last" + else + for n in "${TASKS[@]}"; do + echo -n ' ' + done + echo -e "\x1b[0;1;31m!\x1b[0;31m $@\x1b[0m" 1>&2; + fi +} +info() { + echo -e "\x1b[0;1;94mi\x1b[0;94m $@\x1b[0m" 1>&2; +} + +# Bash Array Joiner +join_array() { + printf -v __ "${1//%/%%}%s" "${@:2}" + echo -n "${__:${#1}}" +} + +# jq abstraction layer + +# Determines where to store the JSON - if the value is `IN_MEM`, then the value is kept in the JQ_BUFFER variable +JSON_STORAGE_LOCATION="IN_MEM" +JQ_BUFFER="" +JQ_BUFFER_OUTPUT_FILE="" +JQ_TRANSACTION=() +JQ_IS_DOING_TRANSACTION=false +JQ_POSITIONALS=() +JQ_POSITIONAL_IDX=1 +ARG=null +jsettempstorage() { + newstorage="${1:-"IN_MEM"}" + if [[ "$newstorage" == "$JSON_STORAGE_LOCATION" ]]; then return 0; fi + if [[ "$JSON_STORAGE_LOCATION" == "IN_MEM" ]]; then + touch "$newstorage" + <<< "$JQ_BUFFER" > "$newstorage" + JQ_BUFFER="" + elif [[ "$newstorage" == "IN_MEM" ]]; then + JQ_BUFFER="$(<"$JSON_STORAGE_LOCATION")" + rm "$JSON_STORAGE_LOCATION" + else + mv "$JSON_STORAGE_LOCATION" "$newstorage" + fi + JSON_STORAGE_LOCATION="$newstorage" +} +# Reads the storage buffer +jreadbuffer() { + if [[ "$JSON_STORAGE_LOCATION" == "IN_MEM" ]]; then + echo -n "$JQ_BUFFER" + else + cat "$JSON_STORAGE_LOCATION" + fi +} +# Writes the storage buffer +jwritebuffer() { + if [[ "$JSON_STORAGE_LOCATION" == "IN_MEM" ]]; then + JQ_BUFFER="$@" + else + echo -n "$@" > "$JSON_STORAGE_LOCATION" + fi +} + +# Creates a new jq blank file +jnew() { + JQ_BUFFER_OUTPUT_FILE="$(realpath "${1:-"/dev/null"}")" + jwritebuffer "${2:-"{}"}" +} +# Loads an existing jq file, falling back to creating a blank one if it doesnt exist +jload() { + JQ_BUFFER_OUTPUT_FILE="$(realpath "${1:-"/dev/null"}")" + if [[ -f "$JQ_BUFFER_OUTPUT_FILE" ]]; then + jwritebuffer "$(cat $JQ_BUFFER_OUTPUT_FILE)" + else + jwritebuffer "${2:-"{}"}" + fi +} + +# SQL-like Transaction Logic +jtransaction() { + if [[ "$JQ_IS_DOING_TRANSACTION" == "true" ]]; then + err "Already performing transaction!" + return 1 + fi + JQ_IS_DOING_TRANSACTION=true + JQ_TRANSACTION=() +} +jabort() { + if [[ "$JQ_IS_DOING_TRANSACTION" == "false" ]]; then + err "No pending transaction!" + return 1 + fi + JQ_IS_DOING_TRANSACTION=false + JQ_TRANSACTION=() +} +jcommit() { + if [[ "$JQ_IS_DOING_TRANSACTION" == "false" ]]; then + err "No pending transaction!" + return 1 + fi + JQ_IS_DOING_TRANSACTION=false + # Incase it's empty: + JQ_TRANSACTION+=( + "." + ) + # As we are no longer in a transaction, jrun will run the pipe-delimited query + jrun "$(join_array ' | ' "${JQ_TRANSACTION[@]}")" + # We can, after running, clear the transaction + JQ_TRANSACTION=() +} + +# Loads a string that jq can positionally access later +jsetasset() { + ARG="(\$ARGS.positional[$JQ_POSITIONAL_IDX] | @base64d)" + JQ_POSITIONALS+=( + "$(base64 -w 0 <<< "$@")" + ); + ((JQ_POSITIONAL_IDX=JQ_POSITIONAL_IDX+1)) +} +jgetassetref() { + echo -n "$ARG" + ARG=null +} + +# Perform a jq write operation +jrun() { + if [[ "$JQ_IS_DOING_TRANSACTION" == "true" ]]; then + # If we're in a transaction, just push it to the list + JQ_TRANSACTION+=( + "($@)" + ) + else + # Otherwise, perform the operation and write to the buffer + if [[ "$JSON_STORAGE_LOCATION" == "IN_MEM" ]]; then + JQ_BUFFER="$(jq -Mc "$@" --args owo ${JQ_POSITIONALS[@]} <<< "$JQ_BUFFER")" + else + jq -Mc "$@" --args owo ${JQ_POSITIONALS[@]} < "$JSON_STORAGE_LOCATION" > "$JSON_STORAGE_LOCATION~" + mv "$JSON_STORAGE_LOCATION~" "$JSON_STORAGE_LOCATION" + fi + JQ_POSITIONALS=() + JQ_POSITIONAL_IDX=1 + fi +} +# Perform a jq write operation using a temp file +_jrun_file() { + if [[ "$JQ_IS_DOING_TRANSACTION" == "true" ]]; then + # Does not work for transactions + err "Cannot use _jrun_file in transaction!"; + return 1; + else + # Otherwise, perform the operation and write to the buffer + if [[ "$JSON_STORAGE_LOCATION" == "IN_MEM" ]]; then + echo -n "$@" > /tmp/.operation.jq + JQ_BUFFER="$(jq -Mcf /tmp/.operation.jq --args owo ${JQ_POSITIONALS[@]} <<< "$JQ_BUFFER")" + rm /tmp/.operation.jq + else + echo -n "$@" > /tmp/.operation.jq + jq -Mcf /tmp/.operation.jq --args owo ${JQ_POSITIONALS[@]} < "$JSON_STORAGE_LOCATION" > "$JSON_STORAGE_LOCATION~" + rm /tmp/.operation.jq + mv "$JSON_STORAGE_LOCATION~" "$JSON_STORAGE_LOCATION" + fi + JQ_POSITIONALS=() + JQ_POSITIONAL_IDX=1 + fi +} +jsave() { + if [[ "$JQ_IS_DOING_TRANSACTION" == "true" ]]; then + err "There is a pending transaction." + return 1 + fi + jreadbuffer | jq -M . > "${1:-"$JQ_BUFFER_OUTPUT_FILE"}" +} +escape_str() { + jq '.=$ARGS.positional[0]' --args "$@" -M <<< '""' +} |