<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>1010</title>
    <link>https://1010.gitlab.io/</link>
    <description>The 1010 Feed</description>
    <image>
      <url>https://1010.gitlab.io/android-chrome-512x512.png</url>
      <title>1010</title>
      <link>https://1010.gitlab.io/</link>
    </image>
    <language>en-us</language>
    <managingEditor>chris@sourcefoundry.org (Chris Simpkins)</managingEditor>
    <webMaster>chris@sourcefoundry.org (Chris Simpkins)</webMaster>
    <copyright>This work is copyright Chris Simpkins. Content is released under the CC BY-SA 4.0 license.  Source code is released under the Apache License, v2.0.</copyright>
    <lastBuildDate>Fri, 08 Dec 2023 21:10:01 -0500</lastBuildDate>
    <atom:link href="https://1010.gitlab.io/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Ripgrep Configuration for Font Development</title>
      <link>https://1010.gitlab.io/posts/ripgrep-font-development/</link>
      <pubDate>Fri, 08 Dec 2023 21:10:01 -0500</pubDate><author>chris@sourcefoundry.org (Chris Simpkins)</author>
      <guid>https://1010.gitlab.io/posts/ripgrep-font-development/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://github.com/BurntSushi/ripgrep&#34;&gt;Ripgrep&lt;/a&gt; is a command line file search tool known for its speed on large codebases. Here&amp;rsquo;s an approach to defining custom filtered ripgrep search types on &lt;a href=&#34;https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html&#34;&gt;Adobe feature&lt;/a&gt;, &lt;a href=&#34;https://fonttools.readthedocs.io/en/latest/designspaceLib/index.html&#34;&gt;designspace&lt;/a&gt;, &lt;a href=&#34;https://handbook.glyphsapp.com/en/source-formats/&#34;&gt;glyphs&lt;/a&gt;, &lt;a href=&#34;https://handbook.glyphsapp.com/en/source-formats/&#34;&gt;glyphspackage&lt;/a&gt;, and &lt;a href=&#34;https://unifiedfontobject.org/&#34;&gt;UFO&lt;/a&gt; font development source files.&lt;/p&gt;
&lt;h2 id=&#34;create-ripgrep-configuration-file&#34;&gt;Create ripgrep Configuration File&lt;/h2&gt;
&lt;p&gt;Create a &lt;a href=&#34;https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md#configuration-file&#34;&gt;ripgrep configuration file&lt;/a&gt; on the path &lt;code&gt;~/.ripgreprc&lt;/code&gt; and enter the following definitions:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Custom types
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--type-add=dspace:*.designspace
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--type-add=fea:*.fea
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--type-add=glyphs:*.glyphs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--type-add=gpack:*.{plist,glyph}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--type-add=plist:*.plist
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;--type-add=ufo:*.{glyf,plist,designspace,fea}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This configuration creates six new search types, automatically filtering ripgrep searches on the defined file extensions. Our new types are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dspace&lt;/code&gt; - *.designspace files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fea&lt;/code&gt; - *.fea files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;glyphs&lt;/code&gt; - *.glyphs files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gpack&lt;/code&gt; - glyphspackage file types: *.plist, *.glyph&lt;/li&gt;
&lt;li&gt;&lt;code&gt;plist&lt;/code&gt; - *.plist files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ufo&lt;/code&gt; - UFO file types: *.designspace, *.fea, *.glyf, &amp;amp; *.plist files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you prefer a different set of type names, modify the name after the equal sign and before the colon on each line of the ripgrep configuration file.&lt;/p&gt;
&lt;h2 id=&#34;export-ripgrep-configuration-path-environment-variable&#34;&gt;Export ripgrep Configuration Path Environment Variable&lt;/h2&gt;
&lt;p&gt;Export the configuration file path in an environment variable. Add the following line to your shell configuration rc file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export RIPGREP_CONFIG_PATH=~/.ripgreprc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and run &lt;code&gt;source&lt;/code&gt; on the shell configuration file. My zsh shell configuration file is on the path &lt;code&gt;~/.zshrc&lt;/code&gt;, and I run it like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;source ~/.zshrc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Modify the shell configuration file path with other shells (e.g., bash). Refer to your shell documentation for details.&lt;/p&gt;
&lt;p&gt;The above source step is not necessary with new terminal launches.  You should only need to use it if your terminal is open at the time that you edit your shell configuration file.&lt;/p&gt;
&lt;h2 id=&#34;use-the-custom-filtered-search-types&#34;&gt;Use the Custom Filtered Search Types&lt;/h2&gt;
&lt;p&gt;Ripgrep searches recursively by default. Navigate to the root level of your source repository before you run the commands below. Use the custom search types with the &lt;code&gt;-t&lt;/code&gt; or &lt;code&gt;--type&lt;/code&gt; command line option. I&amp;rsquo;ll use the short option in the examples below. Replace it with &lt;code&gt;--type=[TYPE]&lt;/code&gt; syntax if you prefer the long option.&lt;/p&gt;
&lt;h3 id=&#34;search-adobe-feature-files&#34;&gt;Search Adobe feature files&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rg -t fea [SEARCH TERM]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;search-designspace-files&#34;&gt;Search designspace files&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rg -t dspace [SEARCH TERM]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;search-glyphs-files&#34;&gt;Search Glyphs files&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rg -t glyphs [SEARCH TERM]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;search-glyphspackage-files&#34;&gt;Search glyphspackage files&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rg -t gpack [SEARCH TERM]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;search-plist-files&#34;&gt;Search plist files&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rg -t plist [SEARCH TERM]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;search-ufo-files&#34;&gt;Search UFO files&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rg -t ufo [SEARCH TERM]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may use these custom types with other ripgrep command line options. Refer to the &lt;a href=&#34;https://github.com/BurntSushi/ripgrep#documentation-quick-links&#34;&gt;ripgrep documentation&lt;/a&gt;, command line help (&lt;code&gt;rg --help&lt;/code&gt;), and man page (&lt;code&gt;man rg&lt;/code&gt;) documentation for the complete set of search features to customize the simple examples above.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>How to Serve Brotli and GZIP Compressed Content on GitLab Pages</title>
      <link>https://1010.gitlab.io/posts/brotli-gzip-gitlab-pages/</link>
      <pubDate>Mon, 27 Nov 2023 00:22:14 -0500</pubDate><author>chris@sourcefoundry.org (Chris Simpkins)</author>
      <guid>https://1010.gitlab.io/posts/brotli-gzip-gitlab-pages/</guid>
      <description>&lt;p&gt;Text content compression reduces the size of HTTP response body payloads and improves web page load times. &lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc1952&#34;&gt;GZIP&lt;/a&gt; and &lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc7932&#34;&gt;Brotli&lt;/a&gt; are both widely supported in current era web clients and use of these text compression technologies is a &lt;a href=&#34;https://developer.chrome.com/docs/lighthouse/performance/uses-text-compression/&#34;&gt;Lighthouse Performance Audit criterion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.gitlab.com/ee/user/project/pages/&#34;&gt;GitLab Pages&lt;/a&gt; doesn&amp;rsquo;t serve compressed HTML, CSS, or Javascript files by default, but it is fast and straightforward to implement static GZIP and Brotli compression as part of your build and deployment workflow. After you generate the compressed files, the GitLab Pages servers will deliver the appropriate file formats to clients based on support. The approach described below should work with all static site generators. The examples are based on this site&amp;rsquo;s Hugo build workflow.&lt;/p&gt;
&lt;aside&gt;
&lt;div class=&#39;admonition admonition-blue&#39;&gt;
    &lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: &lt;a href=&#34;https://caniuse.com/brotli&#34;&gt;caniuse&lt;/a&gt; summarizes Brotli support across the most widely used web browsers.&lt;/p&gt;
&lt;/div&gt;
&lt;/aside&gt;
&lt;h2 id=&#34;the-gzip-and-brotli-compressed-file-paths&#34;&gt;The GZIP and Brotli Compressed File Paths&lt;/h2&gt;
&lt;p&gt;This file compression approach happens during the static site build workflow. Your gzip- (extension &lt;code&gt;.gz&lt;/code&gt;) and brotli- (extension &lt;code&gt;.br&lt;/code&gt;) compressed files must be located in the same directory as the pre-compressed versions of the text files. Here&amp;rsquo;s an example site directory tree from the &lt;a href=&#34;https://docs.gitlab.com/ee/user/project/pages/introduction.html#serving-compressed-assets&#34;&gt;GitLab Pages documentation&lt;/a&gt;. It assumes your production distribution files are located in a top-level &lt;code&gt;public&lt;/code&gt; directory.&lt;/p&gt;
&lt;div class=&#34;plaintext&#34;&gt;&lt;pre&gt;
public/
├─┬ index.html
│ | index.html.br
│ └ index.html.gz
│
├── css/
│   └─┬ main.css
│     | main.css.br
│     └ main.css.gz
│
└── js/
    └─┬ main.js
      | main.js.br
      └ main.js.gz
&lt;/pre&gt;&lt;/div&gt;


&lt;h2 id=&#34;compress-files-in-your-gitlab-pages-build&#34;&gt;Compress Files in Your GitLab Pages Build&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;gzip&lt;/code&gt; is installed by default in the GitLab CI/CD runner environment. We&amp;rsquo;ll configure the &lt;code&gt;brotli&lt;/code&gt; installation and compress all HTML, CSS, and JS files with a few lines in the &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; configuration file. This site is built with Hugo, and the example below shows the &lt;code&gt;hugo&lt;/code&gt; static site build step before the file compression takes place. Replace the &lt;code&gt;hugo&lt;/code&gt; build command with your static site generator build command(s) &lt;em&gt;before you compress the files&lt;/em&gt; if you use something else.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;pages&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;script&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - apk add --update brotli
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - hugo &lt;span style=&#34;color:#6272a4&#34;&gt;# replace with your static site generator build command if not using Hugo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - find public -type f -regex &amp;#39;.*\.\(htm\|html\|txt\|text\|js\|css\)$&amp;#39; -exec gzip -f -k {} \;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - find public -type f -regex &amp;#39;.*\.\(htm\|html\|txt\|text\|js\|css\)$&amp;#39; -exec brotli -f -k {} \;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The final two configuration lines above find all HTML, CSS, and JS files in the &lt;code&gt;public&lt;/code&gt; directory by file extensions, execute &lt;code&gt;gzip&lt;/code&gt; and &lt;code&gt;brotli&lt;/code&gt; compression, and write the compressed files with the appropriate file extensions in the same directory as the pre-compressed file.  Change &lt;code&gt;public&lt;/code&gt; in these commands to your production distribution directory path if you use something else.&lt;/p&gt;
&lt;p&gt;See my &lt;a href=&#34;https://gitlab.com/1010/1010.gitlab.io/-/blob/ba8523317aa3a49cf76caa5b4e7476dfb3e37a1e/.gitlab-ci.yml&#34;&gt;.gitlab-ci.yml build and distribution configuration file&lt;/a&gt; for a working example of the complete Hugo site build configuration that is used to compile and deploy this site.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all there is to it. Once the files are in your production environment, web clients with GZIP and Brotli support should begin to receive the compressed versions. This takes place without any further configuration from you.&lt;/p&gt;
&lt;h2 id=&#34;confirm-compressed-files-in-http-get-responses-with-curl&#34;&gt;Confirm Compressed Files in HTTP GET Responses with curl&lt;/h2&gt;
&lt;p&gt;Here is a &lt;code&gt;curl&lt;/code&gt;-based one-liner that reports the &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding&#34;&gt;Content-Encoding header&lt;/a&gt; on HTTP GET requests for files on your site. It can be configured with any &lt;a href=&#34;https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding&#34;&gt;Accept-Encoding Content Coding header value&lt;/a&gt; to examine the response body encoding format for assets on your site.&lt;/p&gt;
&lt;p&gt;The following commands send an HTTP GET request—configured with an &lt;code&gt;Accept-Encoding&lt;/code&gt; header—for this site&amp;rsquo;s home page HTML file , pipe it through &lt;code&gt;head&lt;/code&gt;, and report out all of the &lt;code&gt;content-*&lt;/code&gt; response headers with a grep regular expression match. Inspect the &lt;code&gt;content-encoding&lt;/code&gt; line for the compression format. There will be no &lt;code&gt;content-encoding&lt;/code&gt; response header when the file is not compressed.  The &lt;code&gt;content-length&lt;/code&gt; value is the response body size in bytes.&lt;/p&gt;
&lt;h3 id=&#34;uncompressed-file&#34;&gt;Uncompressed File&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Accept-Encoding: *&amp;#34;&lt;/span&gt; -i https://1010.gitlab.io/ 2&amp;gt;/dev/null | head | grep -E &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;content\-.*:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Output:&lt;/p&gt;
&lt;div class=&#34;plaintext&#34;&gt;&lt;pre&gt;
content-type: text/html; charset=utf-8
content-length: 9279
&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id=&#34;gzip-compressed-file&#34;&gt;gzip Compressed File&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Accept-Encoding: gzip&amp;#34;&lt;/span&gt; -i https://1010.gitlab.io/ 2&amp;gt;/dev/null | head | grep -E &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;content\-.*:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Output:&lt;/p&gt;
&lt;div class=&#34;plaintext&#34;&gt;&lt;pre&gt;
content-encoding: gzip
content-type: text/html; charset=utf-8
content-length: 3187
&lt;/pre&gt;&lt;/div&gt;


&lt;h3 id=&#34;brotli-compressed-file&#34;&gt;brotli Compressed File&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -H &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Accept-Encoding: br&amp;#34;&lt;/span&gt; -i https://1010.gitlab.io/ 2&amp;gt;/dev/null | head | grep -E &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;content\-.*:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Output:&lt;/p&gt;
&lt;div class=&#34;plaintext&#34;&gt;&lt;pre&gt;
content-encoding: br
content-type: text/html; charset=utf-8
content-length: 2638
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You should be able to change the above URLs to any of your compressed file paths and test the content encoding in the response.&lt;/p&gt;
&lt;p&gt;Hat tip to Mattias Geniar for the curl and head pipeline bits of the one-liners above in &lt;a href=&#34;https://ma.ttias.be/test-gzip-compression-site-via-curl/&#34;&gt;&lt;em&gt;Test Gzip Compression of Site via Curl&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;what-about-github-pages&#34;&gt;What about GitHub Pages?&lt;/h2&gt;
&lt;p&gt;GitHub Pages gzip compresses by default. No need to do anything. It just works. Despite &lt;a href=&#34;https://github.com/orgs/community/discussions/21655&#34;&gt;user requests for brotli compression dating back to at least 2019&lt;/a&gt;, there has yet to be brotli support on GitHub Pages. &lt;a href=&#34;https://github.com/orgs/community/discussions/21655#discussioncomment-3234042&#34;&gt;This response from one of GitHub&amp;rsquo;s Open Source Community Managers&lt;/a&gt; indicates that support is in the queue, but it hasn&amp;rsquo;t launched to date.&lt;/p&gt;
&lt;h2 id=&#34;further-reading&#34;&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.gitlab.com/ee/user/project/pages/introduction.html#serving-compressed-assets&#34;&gt;GitLab Pages &lt;em&gt;Serving Compressed Assets&lt;/em&gt; Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc1952&#34;&gt;RFC 1952&lt;/a&gt; - GZIP file format specification version 4.3&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc7932&#34;&gt;RFC 7932&lt;/a&gt; - Brotli Compressed Data Format specification&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding&#34;&gt;MDN Content-Encoding header documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding&#34;&gt;MDN Accept-Encoding header documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding&#34;&gt;IANA HTTP Content Coding Registry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.chrome.com/docs/lighthouse/performance/uses-text-compression/&#34;&gt;Lighthouse Enable Text Compression Performance Audit Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>Customize Mastodon to Change Your Post Character Limit</title>
      <link>https://1010.gitlab.io/posts/customize-mastodon-character-limit/</link>
      <pubDate>Wed, 21 Dec 2022 22:26:41 -0500</pubDate><author>chris@sourcefoundry.org (Chris Simpkins)</author>
      <guid>https://1010.gitlab.io/posts/customize-mastodon-character-limit/</guid>
      <description>&lt;p&gt;&lt;em&gt;New: Updated for Mastodon v4.2.1!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://joinmastodon.org/&#34;&gt;Mastodon service&lt;/a&gt; has a 500-character limit on post text.  This article documents how you change the character limit in the web user interface and API endpoint that returns instance configuration data to clients (the latter is how many mobile apps that support post length customization define your instance character limit).&lt;/p&gt;
&lt;p&gt;These edits &lt;strong&gt;must be performed by an instance administrator&lt;/strong&gt; with access to the server hosting the Mastodon software.  This is an instance-wide setting that requires direct edits to the Mastodon software files and applies to all users on an instance.  It is not currently possible for users to customize the default value defined on an instance.&lt;/p&gt;
&lt;p&gt;I confirmed the instructions below function on my instance as of the &lt;a href=&#34;https://github.com/mastodon/mastodon/releases/tag/v4.2.1&#34;&gt;Mastodon v4.2.1 release&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;customization-targets&#34;&gt;Customization Targets&lt;/h2&gt;
&lt;p&gt;The character limit is defined in two Mastodon source files.  We will edit the following JavaScript and Ruby sources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mastodon &amp;lt; v4.2.0: &lt;code&gt;compose_form.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Mastodon v4.2.0 and later: &lt;code&gt;compose_form.jsx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;status_length_validator.rb&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that the JavaScript file extension changed as of the Mastodon v4.2.0 release.  The required edits did not.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.draklyckan.se/2021/11/how-to-increase-the-character-limit-for-toots-in-mastodon/&#34;&gt;Documentation&lt;/a&gt; of additional &lt;code&gt;instance_serializer.rb&lt;/code&gt; file changes for earlier REST API v1 instance endpoint support are available; however, this does not appear to be relevant as of the latest API v1 and API v2 updates (at least as of the changes that landed in &lt;a href=&#34;https://github.com/mastodon/mastodon/pull/16485&#34;&gt;this pull request&lt;/a&gt;).  I can confirm the current versions of the official Mastodon app on iOS and Android, Ivory on iOS, Toot! on iOS, IceCubes on iOS, and Tusky on Android release support the character limit increase approach described here as of the Mastodon v4.2.1 release.  Please review the documentation in the link above if you need to support an earlier version of the REST API.  And ping the client app development team about the API update if they use an older API version!&lt;/p&gt;
&lt;h2 id=&#34;prep&#34;&gt;Prep&lt;/h2&gt;
&lt;h3 id=&#34;log-in-to-your-mastodon-user&#34;&gt;Log in to your Mastodon user&lt;/h3&gt;
&lt;p&gt;ssh into your server and log in to your Mastodon user.  Assuming that you defined the user as mastodon at instance setup, the command is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;su - mastodon
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;optional-create-a-new-git-branch-for-your-modifications&#34;&gt;(Optional) Create a new git branch for your modifications&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Please note: Upstream/downstream git repository management is well beyond the scope of the information presented here. Please look into this issue in more detail if you are unaware of the consequences of your edits and intend to update your instance&amp;rsquo;s Mastodon version in the future.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you cloned the &lt;a href=&#34;https://github.com/mastodon/mastodon&#34;&gt;Mastodon source repository&lt;/a&gt; with git as &lt;a href=&#34;https://docs.joinmastodon.org/admin/install/#checking-out-the-code&#34;&gt;documented in the official instance setup docs&lt;/a&gt;, I recommend creating a new git branch for your modifications.  This is optional but likely to help you pull new changes from the upstream Mastodon repository main branch to your local upstream tracking branch as new releases are available.&lt;/p&gt;
&lt;p&gt;Change to the Mastodon &lt;code&gt;live&lt;/code&gt; directory (this is the git repository root) and enter the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git checkout -b mods
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This generates a local modifications branch named &lt;code&gt;mods&lt;/code&gt; starting at your instance’s Mastodon release version git tag commit.&lt;/p&gt;
&lt;p&gt;To get back to the main branch that is tracking upstream Mastodon, either git stash or commit the changes that you make based on the documentation below, and then use the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git checkout main
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can remove all live edits defined in your &lt;code&gt;mods&lt;/code&gt; branch using the instructions below by switching back to the main branch with the command above, checking out a desired Mastodon release tag,  recompiling Mastodon, and restarting your Mastodon services. The recompile and service restart commands are documented below.&lt;/p&gt;
&lt;h2 id=&#34;source-edits&#34;&gt;Source Edits&lt;/h2&gt;
&lt;h3 id=&#34;compose_formjsx&#34;&gt;compose_form.js(x)&lt;/h3&gt;
&lt;p&gt;Open the &lt;code&gt;compose_form.js&lt;/code&gt; (pre-v4.2.0) or &lt;code&gt;compose_form.jsx&lt;/code&gt; (post-v4.2.0) JavaScript file with your preferred text editor.  Here, I’ll use vim with the source file path as of v4.2.1:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vim live/app/javascript/mastodon/features/compose/components/compose_form.jsx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Replace &lt;code&gt;500&lt;/code&gt; in the following lines (as of Mastodon v4.2.1) with your new limit value:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;!&lt;/span&gt;(isSubmitting &lt;span style=&#34;color:#ff79c6&#34;&gt;||&lt;/span&gt; isUploading &lt;span style=&#34;color:#ff79c6&#34;&gt;||&lt;/span&gt; isChangingUpload &lt;span style=&#34;color:#ff79c6&#34;&gt;||&lt;/span&gt; length(fulltext) &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;500&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;||&lt;/span&gt; (isOnlyWhitespace &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;!&lt;/span&gt;anyMedia));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt;CharacterCounter max&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#bd93f9&#34;&gt;500&lt;/span&gt;} text&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#ff79c6&#34;&gt;this&lt;/span&gt;.getFulltextForCharacterCounting()} &lt;span style=&#34;color:#ff79c6&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And write the changes to disk.&lt;/p&gt;
&lt;h3 id=&#34;status_length_validatorrb&#34;&gt;status_length_validator.rb&lt;/h3&gt;
&lt;p&gt;Open the &lt;code&gt;status_length_validator.rb&lt;/code&gt; Ruby file with your text editor.  Again, I’ll use vim with the source file path:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vim live/app/validators/status_length_validator.rb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Replace &lt;code&gt;500&lt;/code&gt; in the &lt;code&gt;MAX_CHARS&lt;/code&gt; definition below (as of Mastodon v4.2.1) with your new limit value:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#50fa7b&#34;&gt;StatusLengthValidator&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt; ActiveModel&lt;span style=&#34;color:#ff79c6&#34;&gt;::&lt;/span&gt;Validator
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   MAX_CHARS &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   URL_PLACEHOLDER_CHARS &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;23&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   URL_PLACEHOLDER &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;x&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#bd93f9&#34;&gt;23&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And write the changes to disk.&lt;/p&gt;
&lt;h2 id=&#34;git-patches&#34;&gt;Git Patches&lt;/h2&gt;
&lt;h3 id=&#34;v40&#34;&gt;v4.0&lt;/h3&gt;
&lt;p&gt;Here is a &lt;a href=&#34;https://gist.github.com/chrissimpkins/cbfff90ad0b471615707aab074b4c084&#34;&gt;git patch with the full set of v4.0.x changes&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;index 6a65f44da..b1e364451 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#f55&#34;&gt;--- a/app/javascript/mastodon/features/compose/components/compose_form.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+++ b/app/javascript/mastodon/features/compose/components/compose_form.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;@@ -90,7 +90,7 @@ class ComposeForm extends ImmutablePureComponent {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;     const fulltext = this.getFulltextForCharacterCounting();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     const isOnlyWhitespace = fulltext.length !== 0 &amp;amp;&amp;amp; fulltext.trim().length === 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;-    return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) &amp;gt; 500 || (isOnlyWhitespace &amp;amp;&amp;amp; !anyMedia));
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+    return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) &amp;gt; 5000 || (isOnlyWhitespace &amp;amp;&amp;amp; !anyMedia));
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   handleSubmit = (e) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;@@ -277,7 +277,7 @@ class ComposeForm extends ImmutablePureComponent {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;           &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &amp;lt;div className=&amp;#39;character-counter__wrapper&amp;#39;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;-            &amp;lt;CharacterCounter max={500} text={this.getFulltextForCharacterCounting()} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+            &amp;lt;CharacterCounter max={5000} text={this.getFulltextForCharacterCounting()} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;           &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;diff --git a/app/validators/status_length_validator.rb b/app/validators/status_length_validator.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;index e107912b7..a3cbe5123 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#f55&#34;&gt;--- a/app/validators/status_length_validator.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+++ b/app/validators/status_length_validator.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;@@ -1,7 +1,7 @@
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt; # frozen_string_literal: true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; class StatusLengthValidator &amp;lt; ActiveModel::Validator
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;-  MAX_CHARS = 500
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+  MAX_CHARS = 5000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;   URL_PLACEHOLDER_CHARS = 23
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   URL_PLACEHOLDER = &amp;#39;x&amp;#39; * 23
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;v41&#34;&gt;v4.1&lt;/h3&gt;
&lt;p&gt;Here is a &lt;a href=&#34;https://gist.github.com/chrissimpkins/a84a62311653db0c22c181dec123cc74&#34;&gt;git patch with the full set of v4.1.x changes&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;index e641d59f4..9b6f84fa1 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#f55&#34;&gt;--- a/app/javascript/mastodon/features/compose/components/compose_form.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+++ b/app/javascript/mastodon/features/compose/components/compose_form.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;@@ -90,7 +90,7 @@ class ComposeForm extends ImmutablePureComponent {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;     const fulltext = this.getFulltextForCharacterCounting();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     const isOnlyWhitespace = fulltext.length !== 0 &amp;amp;&amp;amp; fulltext.trim().length === 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;-    return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) &amp;gt; 500 || (isOnlyWhitespace &amp;amp;&amp;amp; !anyMedia));
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+    return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) &amp;gt; 5000 || (isOnlyWhitespace &amp;amp;&amp;amp; !anyMedia));
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;   };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   handleSubmit = (e) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;@@ -280,7 +280,7 @@ class ComposeForm extends ImmutablePureComponent {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;           &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &amp;lt;div className=&amp;#39;character-counter__wrapper&amp;#39;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;-            &amp;lt;CharacterCounter max={500} text={this.getFulltextForCharacterCounting()} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+            &amp;lt;CharacterCounter max={5000} text={this.getFulltextForCharacterCounting()} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;           &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;diff --git a/app/validators/status_length_validator.rb b/app/validators/status_length_validator.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;index e107912b7..a3cbe5123 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#f55&#34;&gt;--- a/app/validators/status_length_validator.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+++ b/app/validators/status_length_validator.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;@@ -1,7 +1,7 @@
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt; # frozen_string_literal: true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; class StatusLengthValidator &amp;lt; ActiveModel::Validator
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;-  MAX_CHARS = 500
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+  MAX_CHARS = 5000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;   URL_PLACEHOLDER_CHARS = 23
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   URL_PLACEHOLDER = &amp;#39;x&amp;#39; * 23
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;v42&#34;&gt;v4.2&lt;/h3&gt;
&lt;p&gt;Here is a &lt;a href=&#34;https://gist.github.com/chrissimpkins/9e1ccf063c2d9bbe305ded61ffa9dcde&#34;&gt;git patch with the full set of v4.2.x changes&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;index 9222b2dc8..962310a28 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#f55&#34;&gt;--- a/app/javascript/mastodon/features/compose/components/compose_form.jsx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;@@ -100,7 +100,7 @@ class ComposeForm extends ImmutablePureComponent {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;     const fulltext = this.getFulltextForCharacterCounting();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     const isOnlyWhitespace = fulltext.length !== 0 &amp;amp;&amp;amp; fulltext.trim().length === 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;-    return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) &amp;gt; 500 || (isOnlyWhitespace &amp;amp;&amp;amp; !anyMedia));
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+    return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) &amp;gt; 5000 || (isOnlyWhitespace &amp;amp;&amp;amp; !anyMedia));
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;   };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   handleSubmit = (e) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;@@ -297,7 +297,7 @@ class ComposeForm extends ImmutablePureComponent {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;             &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             &amp;lt;div className=&amp;#39;character-counter__wrapper&amp;#39;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;-              &amp;lt;CharacterCounter max={500} text={this.getFulltextForCharacterCounting()} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+              &amp;lt;CharacterCounter max={5000} text={this.getFulltextForCharacterCounting()} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;             &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;diff --git a/app/validators/status_length_validator.rb b/app/validators/status_length_validator.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;index dc841ded3..9cb1ec94b 100644
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#f55&#34;&gt;--- a/app/validators/status_length_validator.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+++ b/app/validators/status_length_validator.rb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;@@ -1,7 +1,7 @@
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;font-weight:bold&#34;&gt;&lt;/span&gt; # frozen_string_literal: true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; class StatusLengthValidator &amp;lt; ActiveModel::Validator
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;-  MAX_CHARS = 500
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f55&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;+  MAX_CHARS = 5000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b;font-weight:bold&#34;&gt;&lt;/span&gt;   URL_PLACEHOLDER_CHARS = 23
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   URL_PLACEHOLDER = &amp;#39;x&amp;#39; * 23
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;rebuild-mastodon&#34;&gt;Rebuild Mastodon&lt;/h2&gt;
&lt;p&gt;Change to the &lt;code&gt;live&lt;/code&gt; sub-directory from your &lt;code&gt;mastodon&lt;/code&gt; user home directory:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;cd&lt;/span&gt; live
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and re-compile your Mastodon project with the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;RAILS_ENV&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;production bundle &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;exec&lt;/span&gt; rails assets:precompile
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;restart-your-mastodon-services&#34;&gt;Restart Your Mastodon Services&lt;/h2&gt;
&lt;p&gt;Exit to your root user:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and &lt;a href=&#34;https://docs.joinmastodon.org/admin/upgrading/&#34;&gt;execute the following commands to restart (or reload) your Mastodon services&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;systemctl restart mastodon-sidekiq
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;systemctl reload mastodon-web
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reload your Mastodon instance in the web browser UI, and you should be ready to post with the new character limit.&lt;/p&gt;
&lt;h2 id=&#34;confirm-the-mastodon-rest-api-update&#34;&gt;Confirm the Mastodon REST API Update&lt;/h2&gt;
&lt;p&gt;REST API clients will rely on the updated max character count data in your &lt;a href=&#34;https://docs.joinmastodon.org/methods/instance/#v2&#34;&gt;instance API endpoint&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here is an example of the relevant part of the JSON response from the mastodon.social instance:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;configuration&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;urls&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;streaming&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;wss://mastodon.social&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;accounts&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;max_featured_tags&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;statuses&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;max_characters&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;500&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;max_media_attachments&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;characters_reserved_per_url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;23&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default, the &lt;code&gt;max_characters&lt;/code&gt; field is defined with 500 characters.&lt;/p&gt;
&lt;p&gt;Your instance&amp;rsquo;s response should be different. Use a GET request to retrieve the JSON data at the following URL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;https://&lt;span style=&#34;color:#ff79c6&#34;&gt;[&lt;/span&gt;ENTER YOUR DOMAIN HERE&lt;span style=&#34;color:#ff79c6&#34;&gt;]&lt;/span&gt;/api/v2/instance
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should see the following updates in the response:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;configuration&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;urls&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;streaming&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;wss://[YOUR DOMAIN]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;accounts&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;max_featured_tags&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;statuses&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;max_characters&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;5000&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;max_media_attachments&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;characters_reserved_per_url&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;23&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;max_characters&lt;/code&gt; will list the custom character limit value you defined in the steps here if all went well with your patch. You can use this API check to troubleshoot any issues you encounter in clients that support custom character limits.&lt;/p&gt;
&lt;p&gt;Close and reload all REST API clients that support custom post lengths to apply the update. This includes Mastodon applications on mobile devices. You should now see the updated character count value in the UI.&lt;/p&gt;
&lt;h2 id=&#34;ui-update-examples&#34;&gt;UI Update Examples&lt;/h2&gt;
&lt;h3 id=&#34;mastodon-web-ui&#34;&gt;Mastodon Web UI&lt;/h3&gt;
&lt;p&gt;
    &lt;picture&gt;
        &lt;source srcset=&#34;https://1010.gitlab.io/img/customize-mastodon-character-limit/mastodon-web-5000-crunch.avif&#34; type=&#34;image/avif&#34; &gt;
            
        &lt;source srcset=&#34;https://1010.gitlab.io/img/customize-mastodon-character-limit/mastodon-web-5000-crunch.webp&#34; type=&#34;image/webp&#34; &gt;
            
        &lt;img
        src=&#34;https://1010.gitlab.io/img/customize-mastodon-character-limit/mastodon-web-5000-crunch.png&#34;
        sizes=&#34;100vw&#34;
        alt=&#34;&#34; title=&#34;&#34;
        loading=&#34;lazy&#34;
        decoding=&#34;async&#34;
        width=&#34;100%&#34;
        height=&#34;100%&#34;
        /&gt;
    &lt;/picture&gt;
&lt;/p&gt;
&lt;h3 id=&#34;mastodon-official-app-on-ios&#34;&gt;Mastodon official app on iOS&lt;/h3&gt;
&lt;p&gt;
    &lt;picture&gt;
        &lt;source srcset=&#34;https://1010.gitlab.io/img/customize-mastodon-character-limit/mastodon-ios-5000-crunch.avif&#34; type=&#34;image/avif&#34; &gt;
            
        &lt;source srcset=&#34;https://1010.gitlab.io/img/customize-mastodon-character-limit/mastodon-ios-5000-crunch.webp&#34; type=&#34;image/webp&#34; &gt;
            
        &lt;img
        src=&#34;https://1010.gitlab.io/img/customize-mastodon-character-limit/mastodon-ios-5000-crunch.png&#34;
        sizes=&#34;100vw&#34;
        alt=&#34;&#34; title=&#34;&#34;
        loading=&#34;lazy&#34;
        decoding=&#34;async&#34;
        width=&#34;100%&#34;
        height=&#34;100%&#34;
        /&gt;
    &lt;/picture&gt;
&lt;/p&gt;
&lt;h3 id=&#34;tusky-on-android&#34;&gt;Tusky on Android&lt;/h3&gt;
&lt;p&gt;
    &lt;picture&gt;
        &lt;source srcset=&#34;https://1010.gitlab.io/img/customize-mastodon-character-limit/tusky-android-5000-crunch.avif&#34; type=&#34;image/avif&#34; &gt;
            
        &lt;source srcset=&#34;https://1010.gitlab.io/img/customize-mastodon-character-limit/tusky-android-5000-crunch.webp&#34; type=&#34;image/webp&#34; &gt;
            
        &lt;img
        src=&#34;https://1010.gitlab.io/img/customize-mastodon-character-limit/tusky-android-5000-crunch.png&#34;
        sizes=&#34;100vw&#34;
        alt=&#34;&#34; title=&#34;&#34;
        loading=&#34;lazy&#34;
        decoding=&#34;async&#34;
        width=&#34;100%&#34;
        height=&#34;100%&#34;
        /&gt;
    &lt;/picture&gt;
&lt;/p&gt;
&lt;h2 id=&#34;edits&#34;&gt;Edits&lt;/h2&gt;
&lt;p&gt;2023-10-13: updated documentation with the requirements in Mastodon v4.1.x and v4.2.x, including the &lt;code&gt;compose_form.jsx&lt;/code&gt; path change as of v4.2.0.&lt;/p&gt;
&lt;p&gt;2022-12-22: added a “Git Patch” section with a Mastodon v4.0.2 git patch that describes the source code changes.&lt;/p&gt;
</description>
    </item>
  </channel>
</rss>