Merge branch 'rework-emoji-management' into 'develop'

Remove finmoji and add a way to download emojis in packs

Closes #817 and #821

See merge request pleroma/pleroma!1073
This commit is contained in:
kaniini 2019-04-22 02:07:19 +00:00
commit 7fcbda702e
144 changed files with 422 additions and 127 deletions

View file

@ -24,6 +24,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mastodon API: [Reports](https://docs.joinmastodon.org/api/rest/reports/) - Mastodon API: [Reports](https://docs.joinmastodon.org/api/rest/reports/)
- ActivityPub C2S: OAuth endpoints - ActivityPub C2S: OAuth endpoints
- Metadata RelMe provider - Metadata RelMe provider
- Emoji packs and emoji pack manager
### Changed ### Changed
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer - **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
@ -50,6 +51,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mastodon API: Remove attachment limit in the Status entity - Mastodon API: Remove attachment limit in the Status entity
- Deps: Updated Cowboy to 2.6 - Deps: Updated Cowboy to 2.6
- Deps: Updated Ecto to 3.0.7 - Deps: Updated Ecto to 3.0.7
- Don't ship finmoji by default, they can be installed as an emoji pack
### Fixed ### Fixed
- Followers counter not being updated when a follower is blocked - Followers counter not being updated when a follower is blocked

View file

@ -39,10 +39,3 @@ does not include the right to compile photos from Unsplash to replicate
a similar or competing service. a similar or competing service.
priv/static/images/city.jpg priv/static/images/city.jpg
---
The files present under the priv/static/finmoji directory are copyright
Finland <https://finland.fi/emoji/>, and are distributed under the Creative
Commons Attribution-NonCommercial-NoDerivatives 4.0 International license, you
should have received a copy of the license file as CC-BY-NC-ND-4.0.

View file

@ -100,9 +100,9 @@ config :pleroma, :emoji,
shortcode_globs: ["/emoji/custom/**/*.png"], shortcode_globs: ["/emoji/custom/**/*.png"],
groups: [ groups: [
# Put groups that have higher priority than defaults here. Example in `docs/config/custom_emoji.md` # Put groups that have higher priority than defaults here. Example in `docs/config/custom_emoji.md`
Finmoji: "/finmoji/128px/*-128.png", Custom: ["/emoji/*.png", "/emoji/**/*.png"]
Custom: ["/emoji/*.png", "/emoji/custom/*.png"] ],
] default_manifest: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json"
config :pleroma, :uri_schemes, config :pleroma, :uri_schemes,
valid_schemes: [ valid_schemes: [
@ -223,7 +223,6 @@ config :pleroma, :instance,
"text/html", "text/html",
"text/markdown" "text/markdown"
], ],
finmoji_enabled: true,
mrf_transparency: true, mrf_transparency: true,
autofollowed_nicknames: [], autofollowed_nicknames: [],
max_pinned_statuses: 1, max_pinned_statuses: 1,

View file

@ -87,7 +87,6 @@ config :pleroma, Pleroma.Emails.Mailer,
* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send. * `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
* `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json`` * `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json``
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML) * `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML)
* `finmoji_enabled`: Whenether to enable the finmojis in the custom emojis.
* `mrf_transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo). * `mrf_transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
* `scope_copy`: Copy the scope (private/unlisted/public) in replies to posts by default. * `scope_copy`: Copy the scope (private/unlisted/public) in replies to posts by default.
* `subject_line_behavior`: Allows changing the default behaviour of subject lines in replies. Valid values: * `subject_line_behavior`: Allows changing the default behaviour of subject lines in replies. Valid values:

View file

@ -1,15 +1,25 @@
# Custom Emoji # Custom Emoji
Before you add your own custom emoji, check if they are available in an existing pack.
See `Mix.Tasks.Pleroma.Emoji` for information about emoji packs.
To add custom emoji: To add custom emoji:
* Add the image file(s) to `priv/static/emoji/custom` * Create the `STATIC-DIR/emoji/` directory if it doesn't exist
* In case of conflicts: add the desired shortcode with the path to `config/custom_emoji.txt`, comma-separated and one per line (`STATIC-DIR` is configurable, `instance/static/` by default)
* Force recompilation (``mix clean && mix compile``) * Create a directory with whatever name you want (custom is a good name to show the purpose of it).
This will create a local emoji pack.
* Put your `.png` emoji files in that directory. In case of conflicts, you can create an `emoji.txt`
file in that directory and specify a custom shortcode using the following format:
`shortcode, file-path, tag1, tag2, etc`. One emoji per line. Note that if you do so,
you'll have to list all other emojis in the pack too.
* Either restart pleroma or connect to the iex session pleroma's running and
run `Pleroma.Emoji.reload/0` in it.
Example: Example:
image files (in `/priv/static/emoji/custom`): `happy.png` and `sad.png` image files (in `instance/static/emoji/custom`): `happy.png` and `sad.png`
content of `config/custom_emoji.txt`: content of `emoji.txt`:
``` ```
happy, /emoji/custom/happy.png, Tag1,Tag2 happy, /emoji/custom/happy.png, Tag1,Tag2
sad, /emoji/custom/sad.png, Tag1 sad, /emoji/custom/sad.png, Tag1

View file

@ -0,0 +1,293 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Emoji do
use Mix.Task
@shortdoc "Manages emoji packs"
@moduledoc """
Manages emoji packs
## ls-packs
mix pleroma.emoji ls-packs [OPTION...]
Lists the emoji packs and metadata specified in the manifest.
### Options
- `-m, --manifest PATH/URL` - path to a custom manifest, it can
either be an URL starting with `http`, in that case the
manifest will be fetched from that address, or a local path
## get-packs
mix pleroma.emoji get-packs [OPTION...] PACKS
Fetches, verifies and installs the specified PACKS from the
manifest into the `STATIC-DIR/emoji/PACK-NAME
### Options
- `-m, --manifest PATH/URL` - same as ls-packs
## gen-pack
mix pleroma.emoji gen-pack PACK-URL
Creates a new manifest entry and a file list from the specified
remote pack file. Currently, only .zip archives are recognized
as remote pack files and packs are therefore assumed to be zip
archives. This command is intended to run interactively and will
first ask you some basic questions about the pack, then download
the remote file and generate an SHA256 checksum for it, then
generate an emoji file list for you.
The manifest entry will either be written to a newly created
`index.json` file or appended to the existing one, *replacing*
the old pack with the same name if it was in the file previously.
The file list will be written to the file specified previously,
*replacing* that file. You _should_ check that the file list doesn't
contain anything you don't need in the pack, that is, anything that is
not an emoji (the whole pack is downloaded, but only emoji files
are extracted).
"""
@default_manifest Pleroma.Config.get!([:emoji, :default_manifest])
def run(["ls-packs" | args]) do
Application.ensure_all_started(:hackney)
{options, [], []} = parse_global_opts(args)
manifest =
fetch_manifest(if options[:manifest], do: options[:manifest], else: @default_manifest)
Enum.each(manifest, fn {name, info} ->
to_print = [
{"Name", name},
{"Homepage", info["homepage"]},
{"Description", info["description"]},
{"License", info["license"]},
{"Source", info["src"]}
]
for {param, value} <- to_print do
IO.puts(IO.ANSI.format([:bright, param, :normal, ": ", value]))
end
# A newline
IO.puts("")
end)
end
def run(["get-packs" | args]) do
Application.ensure_all_started(:hackney)
{options, pack_names, []} = parse_global_opts(args)
manifest_url = if options[:manifest], do: options[:manifest], else: @default_manifest
manifest = fetch_manifest(manifest_url)
for pack_name <- pack_names do
if Map.has_key?(manifest, pack_name) do
pack = manifest[pack_name]
src_url = pack["src"]
IO.puts(
IO.ANSI.format([
"Downloading ",
:bright,
pack_name,
:normal,
" from ",
:underline,
src_url
])
)
binary_archive = Tesla.get!(src_url).body
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright]
if archive_sha == String.upcase(pack["src_sha256"]) do
IO.puts(IO.ANSI.format(sha_status_text ++ [:green, "OK"]))
else
IO.puts(IO.ANSI.format(sha_status_text ++ [:red, "BAD"]))
raise "Bad SHA256 for #{pack_name}"
end
# The url specified in files should be in the same directory
files_url = Path.join(Path.dirname(manifest_url), pack["files"])
IO.puts(
IO.ANSI.format([
"Fetching the file list for ",
:bright,
pack_name,
:normal,
" from ",
:underline,
files_url
])
)
files = Tesla.get!(files_url).body |> Poison.decode!()
IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
pack_path =
Path.join([
Pleroma.Config.get!([:instance, :static_dir]),
"emoji",
pack_name
])
files_to_unzip =
Enum.map(
files,
fn {_, f} -> to_charlist(f) end
)
{:ok, _} =
:zip.unzip(binary_archive,
cwd: pack_path,
file_list: files_to_unzip
)
IO.puts(IO.ANSI.format(["Writing emoji.txt for ", :bright, pack_name]))
emoji_txt_str =
Enum.map(
files,
fn {shortcode, path} ->
emojo_path = Path.join("/emoji/#{pack_name}", path)
"#{shortcode}, #{emojo_path}"
end
)
|> Enum.join("\n")
File.write!(Path.join(pack_path, "emoji.txt"), emoji_txt_str)
else
IO.puts(IO.ANSI.format([:bright, :red, "No pack named \"#{pack_name}\" found"]))
end
end
end
def run(["gen-pack", src]) do
Application.ensure_all_started(:hackney)
proposed_name = Path.basename(src) |> Path.rootname()
name = String.trim(IO.gets("Pack name [#{proposed_name}]: "))
# If there's no name, use the default one
name = if String.length(name) > 0, do: name, else: proposed_name
license = String.trim(IO.gets("License: "))
homepage = String.trim(IO.gets("Homepage: "))
description = String.trim(IO.gets("Description: "))
proposed_files_name = "#{name}.json"
files_name = String.trim(IO.gets("Save file list to [#{proposed_files_name}]: "))
files_name = if String.length(files_name) > 0, do: files_name, else: proposed_files_name
default_exts = [".png", ".gif"]
default_exts_str = Enum.join(default_exts, " ")
exts =
String.trim(
IO.gets("Emoji file extensions (separated with spaces) [#{default_exts_str}]: ")
)
exts =
if String.length(exts) > 0 do
String.split(exts, " ")
|> Enum.filter(fn e -> e |> String.trim() |> String.length() > 0 end)
else
default_exts
end
IO.puts("Downloading the pack and generating SHA256")
binary_archive = Tesla.get!(src).body
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
IO.puts("SHA256 is #{archive_sha}")
pack_json = %{
name => %{
license: license,
homepage: homepage,
description: description,
src: src,
src_sha256: archive_sha,
files: files_name
}
}
tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}")
{:ok, _} =
:zip.unzip(
binary_archive,
cwd: tmp_pack_dir
)
emoji_map = Pleroma.Emoji.make_shortcode_to_file_map(tmp_pack_dir, exts)
File.write!(files_name, Poison.encode!(emoji_map, pretty: true))
IO.puts("""
#{files_name} has been created and contains the list of all found emojis in the pack.
Please review the files in the remove those not needed.
""")
if File.exists?("index.json") do
existing_data = File.read!("index.json") |> Poison.decode!()
File.write!(
"index.json",
Poison.encode!(
Map.merge(
existing_data,
pack_json
),
pretty: true
)
)
IO.puts("index.json file has been update with the #{name} pack")
else
File.write!("index.json", Poison.encode!(pack_json, pretty: true))
IO.puts("index.json has been created with the #{name} pack")
end
end
defp fetch_manifest(from) do
Poison.decode!(
if String.starts_with?(from, "http") do
Tesla.get!(from).body
else
File.read!(from)
end
)
end
defp parse_global_opts(args) do
OptionParser.parse(
args,
strict: [
manifest: :string
],
aliases: [
m: :manifest
]
)
end
end

View file

@ -6,7 +6,7 @@ defmodule Pleroma.Emoji do
@moduledoc """ @moduledoc """
The emojis are loaded from: The emojis are loaded from:
* the built-in Finmojis (if enabled in configuration), * emoji packs in INSTANCE-DIR/emoji
* the files: `config/emoji.txt` and `config/custom_emoji.txt` * the files: `config/emoji.txt` and `config/custom_emoji.txt`
* glob paths, nested folder is used as tag name for grouping e.g. priv/static/emoji/custom/nested_folder * glob paths, nested folder is used as tag name for grouping e.g. priv/static/emoji/custom/nested_folder
@ -14,6 +14,8 @@ defmodule Pleroma.Emoji do
""" """
use GenServer use GenServer
require Logger
@type pattern :: Regex.t() | module() | String.t() @type pattern :: Regex.t() | module() | String.t()
@type patterns :: pattern() | [pattern()] @type patterns :: pattern() | [pattern()]
@type group_patterns :: keyword(patterns()) @type group_patterns :: keyword(patterns())
@ -79,95 +81,94 @@ defmodule Pleroma.Emoji do
end end
defp load do defp load do
finmoji_enabled = Keyword.get(Application.get_env(:pleroma, :instance), :finmoji_enabled) emoji_dir_path =
Path.join(
Pleroma.Config.get!([:instance, :static_dir]),
"emoji"
)
case File.ls(emoji_dir_path) do
{:error, :enoent} ->
# The custom emoji directory doesn't exist,
# don't do anything
nil
{:error, e} ->
# There was some other error
Logger.error("Could not access the custom emoji directory #{emoji_dir_path}: #{e}")
{:ok, packs} ->
# Print the packs we've found
Logger.info("Found emoji packs: #{Enum.join(packs, ", ")}")
emojis =
Enum.flat_map(
packs,
fn pack -> load_pack(Path.join(emoji_dir_path, pack)) end
)
true = :ets.insert(@ets, emojis)
end
# Compat thing for old custom emoji handling & default emoji,
# it should run even if there are no emoji packs
shortcode_globs = Application.get_env(:pleroma, :emoji)[:shortcode_globs] || [] shortcode_globs = Application.get_env(:pleroma, :emoji)[:shortcode_globs] || []
emojis = emojis =
(load_finmoji(finmoji_enabled) ++ (load_from_file("config/emoji.txt") ++
load_from_file("config/emoji.txt") ++
load_from_file("config/custom_emoji.txt") ++ load_from_file("config/custom_emoji.txt") ++
load_from_globs(shortcode_globs)) load_from_globs(shortcode_globs))
|> Enum.reject(fn value -> value == nil end) |> Enum.reject(fn value -> value == nil end)
true = :ets.insert(@ets, emojis) true = :ets.insert(@ets, emojis)
:ok :ok
end end
@finmoji [ defp load_pack(pack_dir) do
"a_trusted_friend", pack_name = Path.basename(pack_dir)
"alandislands",
"association",
"auroraborealis",
"baby_in_a_box",
"bear",
"black_gold",
"christmasparty",
"crosscountryskiing",
"cupofcoffee",
"education",
"fashionista_finns",
"finnishlove",
"flag",
"forest",
"four_seasons_of_bbq",
"girlpower",
"handshake",
"happiness",
"headbanger",
"icebreaker",
"iceman",
"joulutorttu",
"kaamos",
"kalsarikannit_f",
"kalsarikannit_m",
"karjalanpiirakka",
"kicksled",
"kokko",
"lavatanssit",
"losthopes_f",
"losthopes_m",
"mattinykanen",
"meanwhileinfinland",
"moominmamma",
"nordicfamily",
"out_of_office",
"peacemaker",
"perkele",
"pesapallo",
"polarbear",
"pusa_hispida_saimensis",
"reindeer",
"sami",
"sauna_f",
"sauna_m",
"sauna_whisk",
"sisu",
"stuck",
"suomimainittu",
"superfood",
"swan",
"the_cap",
"the_conductor",
"the_king",
"the_voice",
"theoriginalsanta",
"tomoffinland",
"torillatavataan",
"unbreakable",
"waiting",
"white_nights",
"woollysocks"
]
defp load_finmoji(true) do emoji_txt = Path.join(pack_dir, "emoji.txt")
Enum.map(@finmoji, fn finmoji ->
file_name = "/finmoji/128px/#{finmoji}-128.png" if File.exists?(emoji_txt) do
group = match_extra(@groups, file_name) load_from_file(emoji_txt)
{finmoji, file_name, to_string(group)} else
Logger.info(
"No emoji.txt found for pack \"#{pack_name}\", assuming all .png files are emoji"
)
make_shortcode_to_file_map(pack_dir, [".png"])
|> Enum.map(fn {shortcode, rel_file} ->
filename = Path.join("/emoji/#{pack_name}", rel_file)
{shortcode, filename, [to_string(match_extra(@groups, filename))]}
end) end)
end end
end
defp load_finmoji(_), do: [] def make_shortcode_to_file_map(pack_dir, exts) do
find_all_emoji(pack_dir, exts)
|> Enum.map(&Path.relative_to(&1, pack_dir))
|> Enum.map(fn f -> {f |> Path.basename() |> Path.rootname(), f} end)
|> Enum.into(%{})
end
def find_all_emoji(dir, exts) do
Enum.reduce(
File.ls!(dir),
[],
fn f, acc ->
filepath = Path.join(dir, f)
if File.dir?(filepath) do
acc ++ find_all_emoji(filepath, exts)
else
acc ++ [filepath]
end
end
)
|> Enum.filter(fn f -> Path.extname(f) in exts end)
end
defp load_from_file(file) do defp load_from_file(file) do
if File.exists?(file) do if File.exists?(file) do
@ -182,11 +183,11 @@ defmodule Pleroma.Emoji do
|> Stream.map(&String.trim/1) |> Stream.map(&String.trim/1)
|> Stream.map(fn line -> |> Stream.map(fn line ->
case String.split(line, ~r/,\s*/) do case String.split(line, ~r/,\s*/) do
[name, file, tags] ->
{name, file, tags}
[name, file] -> [name, file] ->
{name, file, to_string(match_extra(@groups, file))} {name, file, [to_string(match_extra(@groups, file))]}
[name, file | tags] ->
{name, file, tags}
_ -> _ ->
nil nil
@ -209,7 +210,7 @@ defmodule Pleroma.Emoji do
tag = match_extra(@groups, Path.join("/", Path.relative_to(path, static_path))) tag = match_extra(@groups, Path.join("/", Path.relative_to(path, static_path)))
shortcode = Path.basename(path, Path.extname(path)) shortcode = Path.basename(path, Path.extname(path))
external_path = Path.join("/", Path.relative_to(path, static_path)) external_path = Path.join("/", Path.relative_to(path, static_path))
{shortcode, external_path, to_string(tag)} {shortcode, external_path, [to_string(tag)]}
end) end)
end end

View file

@ -190,7 +190,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
"static_url" => url, "static_url" => url,
"visible_in_picker" => true, "visible_in_picker" => true,
"url" => url, "url" => url,
"tags" => String.split(tags, ",") "tags" => tags
} }
end) end)
end end

View file

@ -286,7 +286,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
emoji = emoji =
Emoji.get_all() Emoji.get_all()
|> Enum.map(fn {short_code, path, tags} -> |> Enum.map(fn {short_code, path, tags} ->
{short_code, %{image_url: path, tags: String.split(tags, ",")}} {short_code, %{image_url: path, tags: tags}}
end) end)
|> Enum.into(%{}) |> Enum.into(%{})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Some files were not shown because too many files have changed in this diff Show more