Misskey Tips & Tricks

A comprehensive list of patches, plugins and various other things to help make your Misskey experience better. Collaboration for misskey stuff can be done at https://github.com/JakeMBauer/Misskey-Extras.

Custom CSS

Snippets of custom styling for Misskey.

Custom CSS

Snippet: Hide parent posts

This snippet will hide parent posts on the timeline.

Installation

Go to Settings -> Client Settings -> General -> Custom CSS and paste the following code into the text box:

.notes div[tabindex="-1"] {
  overflow: visible;
  contain: none;
}

.notes div[tabindex="-1"] .reply-to {
  position: absolute;
  left: 2%;
  bottom: calc(100% - 1em);
  max-width: 85%;
  box-sizing: border-box;
  background: var(--panelHighlight);
  z-index: 1000;
  padding: 20px 24px;
  box-shadow: 0 .5em 2em rgba(0, 0, 0, .5);
  opacity: 0;
  visibility: hidden;
  transition: opacity .2s, visibility 0s ease .2s;
}

.notes div[tabindex="-1"]:hover .reply-to {
  opacity: 1;
  visibility: visible;
  transition: opacity .2s ease .75s, visibility 0s ease .75s;
}

This snippet will hide parent posts on the timeline, instead allowing you to hover over a reply to view the parent post instead.

firefox_hbzeWxitGE.png
A reply with the parent post hidden...

firefox_pCK3jChHuq.png
... and visible when hovering over the child post.

The parent post will become visible after a short delay in order to prevent parent posts from popping up as your are scrolling your timeline.

Author: @volpeon

Custom CSS

Snippet: On-Hover MFM Animations

This snippet will halt MFM animations, only playing them when you over over a note.

Installation

Go to Settings -> Client Settings -> General -> Custom CSS and paste the following code into the text box:

.article:not(:hover) span, .reply-to:not(:hover) span {
	animation: none !important;
}

This will stop MFM animations from playing until you over over a particular note. Use this in case you often get a lot of complicated MFM animations on your timeline, or have a weaker computer that can't process a lot of animations at once.

Authors: @fristi, @volpeon, @robflop

Patches

Popular patches to help fix federation issues, among other things.

Patches

Patch: Fix leaky mutes on timelines

This patch will apply changes to how timelines handle mutes, preventing leaks.

Installation

Apply the following git patch to your misskey installation:

fix-leaky-mutes.patch

When you have muted someone, there is still a possibility to encounter posts by that person or see posts which mention them thanks to some loopholes in the way that the system checks whether or not to hide a post. This patch fixes those issues.

Version

This patch was made for misskey version 12.87.0.

Changelog

Source: https://github.com/JakeMBauer/Misskey-Extras/tree/main/patches#Fix-Leaky-Mutes
Author: @tost

Patches

Patch: Fix pleroma federation

This patch will make some to your misskey installation to allow it to better federate with pleroma instances.

Installation

Apply the following git patch to your misskey installation:

misskey-truncate-v2.patch

Note: This patch has been merged into the main Misskey repository and doesn't have to be applied anymore for misskey versions 12.88.0 and up.

This patch will truncate long user biographies that would otherwise lead to errors during federation. The patch and above setting are mainly to fix federation issues with pleroma, which allows for very long usernames and biographies that do not fit in the respective table columns in misskey.

Version

This patch has been tested on misskey versions 12.84.4 - 12.85.1.
It has been deprecated as of version 12.88.0; this patch was merged into the main Misskey repository. (See #7629 and #7642)

Changelog

Source: https://mk.toast.cafe/notes/8p01vttxpj
Authors: @tost, @Johann150

Patches

Patch: MFM Search engine

This patch will change the default search engine used with MFM's search bar feature.

Installation

Apply one of the following three patches to your misskey installation. Note that only one can be applied at a time, as they conflict with each other.

Misskey Flavoured Markup (MFM) allows to display a search bar in a post e.g. like this:

misskey [search]

By default, this will open a Google search. There are two places in the source where this URL is stored, although probably changing one would be enough.

The files which have to be changed are src/mfm/to-html.ts and src/client/components/google.vue.

Listed above are the patches for three different privacy-respecting search engines, namely DuckDuckGo, Metager and Qwant.

Version

This patch has been tested on misskey versions 12.84.4 - 12.85.1.

Changelog

Source: https://github.com/JakeMBauer/Misskey-Extras/tree/main/patches#Fix-Leaky-Mutes
Author: @Johann150

Plugins

Popular AiScript plugins for Misskey.

Plugins

Plugin: Catify

This plugin provides features to make your Misskey experience even more catty.

Installation

Go to Settings -> Plugins -> Install Plugins and paste the following code into the text box:

### {
  name: "Catify"
  version: "1.3.3"
  author:  "volpeon"
  description: "Various features to make your Misskey experience even more catty"
  permission: []
  config: {
    users_to_cats: {
      type: "boolean"
      label: "Turn all users into cats"
      description: "Turns all users into cats wherever possible"
      default: yes
    }
    nyanify_notes: {
      type: "boolean"
      label: "Nyanify outgoing posts"
      description: "Nyanifies your posts on sending"
      default: yes
    }
    nyanify_extra: {
      type: "boolean"
      label: "Nyanify extra"
      description: "Apply a stronger nyanification filter than Misskey's"
      default: yes
    }
    nyanify_suffix: {
      type: "boolean"
      label: "-nya suffix"
      description: "End sentences with -nya"
      default: yes
    }
  }
}

:: Util {
  @pipe(els) {
    Arr:reduce(els, @(res, el) { el(res) })
  }
}

:: MutableTrie {
  @empty() {
    {
      value: _
      children: {}
    }
  }
  
  @has(trie, c) {
    Obj:has(trie.children, c)
  }
  
  @get(trie, c) {
    Obj:get(trie.children, c)
  }

  @insert(trie, key, value) {
    $t <- trie

    ~~ #c, Str:split(key) {
      ? Core:not(has(t, c)) {
        Obj:set(t.children, c, empty())
      }

      t <- get(t, c)
    }

    t.value <- value
  }
}

:: MutableStream {
  @of(str) {
    #chars = Str:split(str)
    {
      chars: chars
      len: Arr:len(chars)
      pos: 1
      state: {}
    }
  }
  
  @end(stream) {
    (stream.pos > stream.len)
  }

  @peek(stream) {
    ? end(stream) {
      << _
    }

    #chars = stream.chars
    chars[stream.pos]
  }

  @take(stream) {
    #c = peek(stream)

    ? (c != _) {
      stream.pos <- (stream.pos + 1)
    }

    c
  }
}

@takeWhile(stream, cond, out) {
  #c = MutableStream:peek(stream)

  ? (c != _) {
    ? cond(c) {
      Arr:push(out, c)
      stream.pos <- (stream.pos + 1)
      takeWhile(stream, cond, out)
    }
  }
}

@takeUntil(stream, cond, out) {
  takeWhile(stream, @(c) { Core:not(cond(c)) }, out)
}

@match(trie, stream) {
  $tr <- trie
  $path <- []
  $successfulPath <- ""
  $pos <- stream.pos
  $action <- trie.value
  
  @run() {
    #c = MutableStream:take(stream)
    
    ? (c != _) {
      Arr:push(path, c)
      
      ? MutableTrie:has(tr, c) {
        tr <- MutableTrie:get(tr, c)
        ? (tr.value != _) {
          successfulPath <- Arr:join(path)
          pos <- stream.pos
          action <- tr.value
        }
        run()
      }
    }
  }

  run()
  
  stream.pos <- pos

  {
    path: successfulPath
    value: action
  }
}

@actionContinue() {
  {
    type: "continue"
  }
}

@actionNoNyanification() {
  {
    type: "noNyanification"
  }
}

@actionReplace(str) {
  {
    type: "replace"
    str: str
  }
}

@actionReplaceWhole(str) {
  {
    type: "replaceWhole"
    str: str
  }
}

@actionSkipUntilChar(c) {
  {
    type: "skipUntilChar"
    c: c
  }
}

@actionSkipUntilSpace() {
  {
    type: "skipUntilSpace"
  }
}

@actionSuffix(str) {
  {
    type: "suffix"
    str: str
  }
}

#tr = MutableTrie:empty()

MutableTrie:insert(tr, "", actionContinue())

MutableTrie:insert(tr, "(nya=off)", actionNoNyanification())

MutableTrie:insert(tr, ":", actionSkipUntilChar(":"))
MutableTrie:insert(tr, ": ", actionContinue())

MutableTrie:insert(tr, "`", actionSkipUntilChar("`"))

MutableTrie:insert(tr, "http://", actionSkipUntilSpace())
MutableTrie:insert(tr, "https://", actionSkipUntilSpace())

MutableTrie:insert(tr, "$[", actionSkipUntilSpace())

MutableTrie:insert(tr, "な", actionReplace("にゃ"))
MutableTrie:insert(tr, "ナ", actionReplace("ニャ"))
MutableTrie:insert(tr, "ナ", actionReplace("ニャ"))

MutableTrie:insert(tr, "na", actionReplace("nya"))
MutableTrie:insert(tr, "Na", actionReplace("Nya"))
MutableTrie:insert(tr, "NA", actionReplace("NYA"))

MutableTrie:insert(tr, "morning", actionReplace("mornyan"))
MutableTrie:insert(tr, "Morning", actionReplace("Mornyan"))
MutableTrie:insert(tr, "MORNING", actionReplace("MORNYAN"))

MutableTrie:insert(tr, "everyone", actionReplace("everynyan"))
MutableTrie:insert(tr, "Everyone", actionReplace("Everynyan"))
MutableTrie:insert(tr, "EVERYONE", actionReplace("EVERYNYAN"))

? Plugin:config.nyanify_extra {
  MutableTrie:insert(tr, "nu", actionReplace("nyu"))
  MutableTrie:insert(tr, "Nu", actionReplace("Nyu"))
  MutableTrie:insert(tr, "NU", actionReplace("NYU"))

  MutableTrie:insert(tr, "hello", actionReplaceWhole("henlo"))
  MutableTrie:insert(tr, "Hello", actionReplaceWhole("Henlo"))
  MutableTrie:insert(tr, "HELLO", actionReplaceWhole("HENLO"))

  MutableTrie:insert(tr, "hand", actionReplaceWhole("paw"))
  MutableTrie:insert(tr, "Hand", actionReplaceWhole("Paw"))
  MutableTrie:insert(tr, "HAND", actionReplaceWhole("PAW"))

  MutableTrie:insert(tr, "hands", actionReplaceWhole("paws"))
  MutableTrie:insert(tr, "Hands", actionReplaceWhole("Paws"))
  MutableTrie:insert(tr, "HANDS", actionReplaceWhole("PAWS"))

  MutableTrie:insert(tr, "foot", actionReplaceWhole("paw"))
  MutableTrie:insert(tr, "Foot", actionReplaceWhole("paw"))
  MutableTrie:insert(tr, "FOOT", actionReplaceWhole("PAW"))

  MutableTrie:insert(tr, "feet", actionReplaceWhole("paws"))
  MutableTrie:insert(tr, "Feet", actionReplaceWhole("paws"))
  MutableTrie:insert(tr, "FEET", actionReplaceWhole("PAWS"))

  MutableTrie:insert(tr, "arm", actionReplaceWhole("foreleg"))
  MutableTrie:insert(tr, "Arm", actionReplaceWhole("Foreleg"))
  MutableTrie:insert(tr, "ARM", actionReplaceWhole("FORELEG"))

  MutableTrie:insert(tr, "leg", actionReplaceWhole("hindleg"))
  MutableTrie:insert(tr, "Leg", actionReplaceWhole("Hindleg"))
  MutableTrie:insert(tr, "LEG", actionReplaceWhole("HINDLEG"))
}

? Plugin:config.nyanify_suffix {
  MutableTrie:insert(tr, "...", actionContinue())
  MutableTrie:insert(tr, ".", actionSuffix("-nya"))
  MutableTrie:insert(tr, ";", actionSuffix("-nya"))
  MutableTrie:insert(tr, "!", actionSuffix("-nya"))
  MutableTrie:insert(tr, "?", actionSuffix("-nya"))
  MutableTrie:insert(tr, "-nya.", actionContinue())
  MutableTrie:insert(tr, "-nya;", actionContinue())
  MutableTrie:insert(tr, "-nya!", actionContinue())
  MutableTrie:insert(tr, "-nya?", actionContinue())
}

@constYes() {
  yes
}

@isSpace(c) {
  ? (c = _) { yes }
  .? (c = " ") { yes }
  .? (c = Str:lf) { yes }
  . { no }
}

@isWordBoundary(c) {
  ? isSpace(c) { yes }
  .? (c = ".") { yes }
  .? (c = ",") { yes }
  .? (c = ";") { yes }
  .? (c = "!") { yes }
  .? (c = "?") { yes }
  . { no }
}

@isCandidate(c) {
  MutableTrie:has(tr, c)
}

@parse(stream, out) {
  takeUntil(stream, isCandidate, out)
  
  ? MutableStream:end(stream) {
    << Arr:join(out)
  }
  
  #res = match(tr, stream)
  #path = res.path
  #action = res.value
  
  ? action.type {
    "continue" => {
      ? (path = "") {
        Arr:push(out, MutableStream:take(stream))
      } . {
        Arr:push(out, path)
      }
    }

    "noNyanification" => {
      Arr:push(out, path)
      takeWhile(stream, constYes, out)
    }
    
    "replace" => {
      Arr:push(out, action.str)
    }
    
    "replaceWhole" => {
      #last = out[Arr:len(out)]
      #next = MutableStream:peek(stream)
      ? (isWordBoundary(last) & isWordBoundary(next)) {
        Arr:push(out, action.str)
      } . {
        Arr:push(out, path)
      }
    }
    
    "skipUntilChar" => {
      Arr:push(out, path)
      takeWhile(stream, @(c) { (c != action.c) }, out)
      Arr:push(out, MutableStream:take(stream))
    }
    
    "skipUntilSpace" => {
      Arr:push(out, path)
      takeUntil(stream, isSpace, out)
    }
    
    "suffix" => {
      #c = MutableStream:peek(stream)
      ? isSpace(c) {
        Arr:push(out, action.str)
      }
      Arr:push(out, path)
    }
  }
  
  parse(stream, out)
}

@nyanify(text) {
  parse(MutableStream:of(text), [])
}

@nyanifyNoteText(note) {
  ? ((note.text = _) | (note.text = "")) {
    << note
  }

  note.text <- nyanify(note.text)

  note
}

@catifyNote(note) {
  note.user.isCat <- yes
  note <- nyanifyNoteText(note)
  note
}

? Plugin:config.users_to_cats { 
  Plugin:register_note_view_interruptor(catifyNote)
}

? Plugin:config.nyanify_notes { 
  Plugin:register_note_post_interruptor(nyanifyNoteText)
}


This plugin expands on the cat-features of misskey. Features include:

All features are enabled by default, but can be disabled in the plugin configuration.

Changelog

Source: https://mk.vulpes.one/@volpeon/pages/1628621742554
Author: @volpeon

Plugins

Plugin: DeepL Translation

A simple plugin that gives you an automatic translation of a post from any language into any other language via DeepL. The translation is displayed in a new tab.

Installation

Go to Settings -> Plugins -> Install Plugins and paste the following code into the text box:

### {
  name: "DeepL Translate"
  version: "1.1.1"
  author:  "volpeon"
  description: "Translate posts from any language into any other language with DeepL"
  permission: []
  config: {
    target_language: {
      type: "string"
      label: "Target language to translate into (ISO 639-1 language code)"
      default: "en"
    }
  }
}


@translate(note) {
  Plugin:open_url(Arr:join(["https://www.deepl.com/translator#auto/" Plugin:config.target_language "/" Str:replace(Str:replace(note.text "/" "\%2F") "#" "%23")]))
  note
}

Plugin:register_note_action("Translate" translate)

The target langauge is English by default and can be changed in the plugin's configuration. The setting requires an ISO-639-1 language code (such as "en" or "de"). Wikipedia has a list of all codes: https://de.wikipedia.org/wiki/Liste_der_ISO-639-1-Codes

Changelog

Source: https://mk.vulpes.one/@volpeon/pages/1628327651656
Author: @volpeon

Plugins

Plugin: Note MFM Toggle

A comprehensive plugin that allows you to toggle MFM (Misskey Flavored Markdown) on individual posts.

Installation

Go to Settings -> Plugins -> Install Plugins and paste the following code into the text box:

### {
  name: "Note MFM Toggle"
  version: "1.1.0"
  author:  "volpeon, Johann150"
  description: "MFM can be toggled for specific notes from the three dot menu of the note."
  permission: []
  config: {}
}

:: Util {
  @pipe(els) {
    #len = Arr:len(els)

    ? (len = 0) {
      << _
    }

    #result = els[1]

    @run(i) {
      ? (i > len) {
        << result
      }

      #el = els[i]
      result <- el(result)
      
      run((i + 1))
    }

    run(2)
  }

  @merge(obj1, obj2) {
    #o = Obj:copy(obj1)
    #kvs = Obj:kvs(obj2)
    #len = Arr:len(kvs)
  
    @step(i) {
      ? (i > len) {
        << o
      }

      #kv = kvs[i]
      Obj:set(o, kv[1], kv[2])

      step((i + 1))
    }

    step(1)
  }
}

:: Stream {
  @resultOk(read, stream) {
    {
      ok: yes
      read: read
      stream: stream
    }
  }

  @resultFail() {
    {
      ok: no
    }
  }

  @mk(str, pos) {
    {
      str: str
      pos: pos
    }
  }

  @of(str) {
    mk(str, 1)
  }

  @read(stream, length) {
    #remainingLength = ((Str:len(stream.str) - stream.pos) + 1)

    ? (length > remainingLength) {
      << resultFail()
    }

    #newPos = (stream.pos + length)

    resultOk(Str:slice(stream.str, stream.pos, newPos), mk(stream.str, newPos))
  }
}

:: Parser {
  // Constructors

  @resultOk(stream, value) {
    {
      ok: yes
      stream: stream
      value: value
    }
  }

  @resultFail(stream, error) {
    {
      ok: no
      stream: stream
      error: error
    }
  }

  @mk(parse) {
    {
      parse: parse
    }
  }
  
  @ok(value) {
    mk(@(stream) { resultOk(stream, value) })
  }
  
  @fail(error) {
    mk(@(stream) { resultFail(stream, error) })
  }

  @anyToken(length) {
    mk(@(stream) {
      #result = Stream:read(stream, length)

      ? result.ok {
        << resultOk(result.stream, result.read)
      }

      resultFail(stream, "End of input")
    })
  }

  @eof() {
    mk(@(stream) {
      #result = Stream:read(stream, 1)

      ? result.ok {
        << resultFail(stream, "Expected end of input")
      }

      resultOk(stream, yes)
    })
  }

  // Basic operators

  @then(cont) {
    @(parser) {
      mk(@(stream) {
        #result = parser.parse(stream)

        ? result.ok {
          #parserCont = cont(result.value)
          << parserCont.parse(result.stream)
        }
    
        result
      })
    }
  }

  @map(fn) {
    @(parser) {
      mk(@(stream) {
        #result = parser.parse(stream)

        ? result.ok {
          << resultOk(result.stream, fn(result.value))
        }
    
        result
      })
    }
  }

  // Generic combinators
  
  @satisfy(condition, description) {
    @(parser) {
      mk(@(stream) {
        #result = parser.parse(stream)
        
        ? result.ok {
          ? condition(result.value) {
            << resultOk(result.stream, result.value)
          }
          
          << resultFail(stream, description)
        }

        result
      })
    }
  }

  @oneOf(parsers) {
    #len = Arr:len(parsers)

    mk(@(stream) {
      #errors = []

      @run(i) {
        ? (i > len) {
          << resultFail(stream, Arr:join(["All parsers failed: " Arr:join(errors, "; ")]))
        }

        #parser = parsers[i]
        #result = parser.parse(stream)
        
        ? result.ok {
          << result
        }

        Arr:push(errors, Arr:join(["(" Core:to_str(i) ") " result.error]))
        
        run((i + 1))
      }

      run(1)
    })
  }

  @many(parser) {
    @run(matches) {
      mk(@(stream) {
        #result = parser.parse(stream)
        
        ? result.ok {
          #step = run(Arr:concat(matches, [result.value]))
          << step.parse(result.stream)
        }

        resultOk(result.stream, matches)
      })
    }

    run([])
  }

  @many1(parser) {
    Util:pipe([
      many(parser)
      satisfy(@(value) {
        ? (Arr:len(value) = 0) { no } . { yes }
      }, "Expected at least one match")
    ])
  }

  // Specialized string parsers

  @token(str) {
    Util:pipe([
      anyToken(Str:len(str))
      satisfy(@(value) {
        ? (value = str) { yes } . { no }
      }, Arr:join(["Expected '" str "'"]))
    ])
  }

  @takeWhile(condition) {
    mk(@(stream) {
      #matches = []

      @run(s) {
        #result = Stream:read(s, 1)

        ? result.ok {
          ? condition(result.read) {
            Arr:push(matches, result.read)
            << run(result.stream)
          }
        }

        resultOk(s, Arr:join(matches))
      }

      run(stream)
    })
  }

  @takeWhile1(condition) {
    Util:pipe([
      takeWhile(condition)
      satisfy(@(value) {
        ? (Str:len(value) = 0) { no } . { yes }
      }, "Expected at least one match")
    ])
  }
}

:: ParserStore {
  @mk(parser) {
    {
      parser: parser
    }
  }

  @create() {
    mk(Parser:ok({}))
  }

  @merge(parserFn) {
    @(store) {
      mk(
        Util:pipe([
          store.parser
          Parser:then(@(value1) {
            #parser = parserFn(value1)
            Util:pipe([
              parser
              Parser:map(@(value2) {
                Util:merge(value1, value2)
              })
            ])
          })
        ])
      )
    }
  }

  @skip(parserFn) {
    @(store) {
      mk(
        Util:pipe([
          store.parser
          Parser:then(@(value) {
            #parser = parserFn(value)
            Util:pipe([
              parser
              Parser:map(@() { value })
            ])
          })
        ])
      )
    }
  }

  @store(key, parserFn) {
    merge(@(value1) {
      #parser = parserFn(value1)
      Util:pipe([
        parser
        Parser:map(@(value2) { 
          #o = {}
          Obj:set(o, key, value2)
          o
        })
      ])
    })
  }

  @parser(store) {
    store.parser
  }
}

#spaces = Parser:takeWhile(@(value) {
  ? (value = " ") { yes } . { no }
})

#mfmTagOpen = Parser:token("$[")

#mfmTagName = Parser:takeWhile1(@(value) {
  ? (value = " ") { no } . { yes }
})

#mfmTagClose = Parser:token("]")

#mfmTagText = Parser:takeWhile1(@(value) {
  ? Str:incl("`$]", value) { no } . { yes }
})

#codeToggle = Parser:token("`")

#codeBlockToggle = Parser:token("```")

#codeText = Parser:takeWhile1(@(value) {
  ? (value = "`") { no } . { yes }
})

#code = Util:pipe([
  ParserStore:create()
  ParserStore:skip(@() { codeToggle })
  ParserStore:store("content", @() { codeText })
  ParserStore:skip(@() { codeToggle })
  ParserStore:parser
  Parser:map(@(store) { Arr:join(["`" store.content "`"]) })
])

#codeBlock = Util:pipe([
  ParserStore:create()
  ParserStore:skip(@() { codeBlockToggle })
  ParserStore:store("content", @() { codeText })
  ParserStore:skip(@() { codeBlockToggle })
  ParserStore:parser
  Parser:map(@(store) { Arr:join(["`" store.content "`"]) })
])

#mfmTag = Util:pipe([
  ParserStore:create()
  ParserStore:skip(@() { mfmTagOpen })
  ParserStore:skip(@() { mfmTagName })
  ParserStore:skip(@() { spaces })
  ParserStore:store("content", @() { mfmTagContent })
  ParserStore:skip(@() { mfmTagClose })
  ParserStore:parser
  Parser:map(@(store) { store.content })
])

#mfmTagContent = Util:pipe([
  Parser:many(
    Parser:oneOf([
      mfmTagText
      codeBlock
      code
      mfmTag
      Parser:token("`")
      Parser:token("$")
    ])
  )
  Parser:map(@(parts) { Arr:join(parts) })
])

#noteText = Parser:takeWhile1(@(value) {
  ? Str:incl("`$", value) { no } . { yes }
})

#noteContent = Util:pipe([
  Parser:many(
    Parser:oneOf([
      noteText
      codeBlock
      code
      mfmTag
      Parser:token("`")
      Parser:token("$")
    ])
  )
  Parser:map(@(parts) { Arr:join(parts) })
])

@strip(note) {
  ? (note.renote = _) {
    #result = noteContent.parse(Stream:of(note.text))

    ? result.ok {
      note.text <- result.value
    }
  }
  . {
    #result = noteContent.parse(Stream:of(note.renote.text))

    ? result.ok {
      note.renote.text <- result.value
    }
  }
  
  note
}

@toggleMFM(note) {
	$mutes <- Mk:load("mutes")
	? (mutes = _) {
		mutes <- [note.id]
		Mk:dialog("MFM disabled for this note" "Reload or go to different page to take effect.")
	} . {
		? Arr:incl(mutes note.id) {
			mutes <- Arr:filter(mutes @(x){(x != note.id)})
			Mk:dialog("MFM reenabled for this note" "Reload or go to different page to take effect.")
		} . {
			Arr:push(mutes note.id)
			Mk:dialog("MFM disabled for this note" "Reload or go to different page to take effect.")
		}
	}
	Mk:save("mutes" mutes)
}

@checkMute(note) {
	#mutes = Mk:load("mutes")
	? (mutes != _) {
		? Arr:incl(mutes note.id) {
			note <- strip(note)
		}
		? Arr:incl(mutes note.renote.id) {
			note.renote <- strip(note.renote)
		}
	}
	<<note
}


Plugin:register_note_action("toggle MFM" toggleMFM)
Plugin:register_note_view_interruptor(checkMute)

Changelog

Source: https://genau.qwertqwefsday.eu/@Johann150/pages/1628622773084
Author: @volpeon @Johann150

Plugins

Plugin: Note Preview

This is a plugin that lets you immediately view previews from within the post form.

Installation

Go to Settings -> Plugins -> Install Plugins and paste the following code into the text box:

### {
  id: "8ec6c410-67b8-11eb-ae93-0242ac130002"
  name: "Basically Preview EN"
  version: "1.0.0"
  author:  "robflop"
  description: "Displays previews in the center due to specification"
  permission: []
  config: {
    show_when_posting: {
      type: "boolean"
      label: "Show confirmation before posting"
     }
  }
}

@preview(note) {
  Mk:dialog("" note.text "")
  note
}

@preview_with_confirm(note) {
  ? Mk:confirm("Post confirmation" `{note.text}{Str:lf}The note will be sent like this.`) {
    <<note
  }
  _
}

Plugin:register_post_form_action("Basically Preview EN" preview)
? Plugin:config.show_when_posting { 
  Plugin:register_note_post_interruptor(preview_with_confirm)
}

This is a plugin that lets you immediately view previews from within the post form. The preview dialog will however always appear in the center of the screen due to the AiScript specification😭

Thus "Basically"...

You can enable always showing a preview before submitting a post by going to:

and then enabling the "Show confirmation before posting" option there.

(If you click "Cancel" on the dialog that appears before sending a post an error will appear, but this is normal and can be ignored)

🙂 How to use

Original JP Source: https://misskey.io/@hakohako_f2/pages/notePreview
Author: @hakohako_f2

Translated Source: https://misskey.io/@robflop/pages/notePreviewEN
Translator: @robflop

Settings

Setup guides and tips on how to properly tune your Misskey instance.

Settings

Federation Tips

By default, Misskey has a number of issues affecting its ability to federate with other federated ActivityPub services, such as Pleroma and Mastodon. This article contains a set of tips to help mitigate these problems.

User profile character limits

In Pleroma, users may have very long user profiles and nicknames, which tends to conflict with Misskey's limits for these fields. Misskey's default behavior is to instead ignore these users, breaking federation with them.

As a temporary fix, a patch has been made to mitigate this issue, which can be found here on this wiki. Applying this patch will change Misskey's behavior to instead truncate fields too long to fit into its database, thus restoring federation with afflicted users.

See https://mk.toast.cafe/notes/8p01vttxpj, patch by @tost, @Johann150

General ActivityPub related issues

Signing ActivityPub requests

Some other ActivityPub Software can have issues federating with Misskey with the default settings. This is because especially with Mastodon, signing ActivityPub requests is necessary.

Because of this you should enable the signToActivityPubGet option in .config/default.yml. This option should already be on the last line of the example configuration included with Misskey.

See https://github.com/JakeMBauer/Misskey-Extras/tree/main/documentation#ActivityPub-Signing, tip by @Johann150

Themes

Appealing custom themes for Misskey.

Tips

Tips for misskey newcomers regarding some of misskey's unique functionalities.

Tips

Finding information

Finding information about Misskey might not be always easy. This article will list a number of information sources you can look through in order to find out more about Misskey's internals, updates and api features, as well as community-related information.

Bots

There are a number of bots to be found that will continuously post new information about various things.

Misskey Repo Bot (https://misskey.io/@repo):
The Repo Bot posts information about activity on the Misskey git repository (https://github.com/misskey-dev/misskey) that includes information on new commits, progress on issues, new comments as well as new releases, besides other things. Following this bot will be mostly useful for those that help develop Misskey, as well as those running the development branch of Misskey.

Misskey Forum Bot (https://misskey.io/@checkforum):
The Forum Bot posts activity updates about the Misskey Forum (https://forum.misskey.io/). It will post notes about any new discussions being added, as well as updates about those discussions when new replies are posted. This bot is most useful for people who help out on the Misskey Forum.

Tips

Frequently Asked Questions (FAQ)

This article contains answers to a number of commonly asked questions by misskey newcomers. Since these end up being asked frequently, I have compiled them together in a convenient document.

Q: What is federation, what does ActivityPub (AP) mean?

ActivityPub is a protocol that defines how software can communicate with each other. Software like Misskey uses the ActivityPub protocol to allow multiple instances to send messages to each other. The protocol is decentralized, which means that no one Misskey instance acts as a central server; each instance is simply a node in a network of many. The act of multiple instances exchanging messages with each other is what is known as federation. Hence, a network of Misskey instances can be called a federated network.

The ActivityPub protocol is also used by other similar software, such as Pleroma and Mastodon, allowing both to also federate with each other and Misskey instances. Together, all these instances form what is known as the Fediverse.

Q: Are all of Misskey's features federated?

No. Only the sending of Notes and private Chats are currently federated. Other features, such as Channels, Groups, Antennas, Clips, Pages, Favorites, Galleries and Rooms do not federate to other instances, even other Misskey instances. These features were meant to be used locally within each Misskey instance. Group chats also don't federate, as a group can only contain people from the same instance.

Q: What is MFM?

MFM stands for Misskey Flavored Markdown. This is a custom markdown-like syntax that can be used in notes and pages to add various markup effects to your content. A cheat sheet can be found in the Help section of your instance. Alternatively, visit https://{your-instance-domain}/mfm-cheat-sheet.

Note that the various MFM effects can also stack, by nesting their syntax. While this is generally not recommended for performance, some interesting effects can be made by stacking some of the animated features. If you see a note with some fancy effects and wonder "How did they make it?", you can click the three-dots (...) menu under the post and click "Copy Contents" and paste it in a text editor to see what MFM syntax was used.

Q What is AiScript?

AiScript is a custom scripting language that can be used in various places in Misskey. Plugins are written with it. It can also be used in pages and some UI widgets. Documentation for it is currently still a bit limited to these Japanese pages: https://github.com/syuilo/aiscript/blob/master/docs/get-started.md and https://wiki.misskey.io/ja/function/AiScript.

Q: The Misskey UI feels slow and laggy, what can I do about this?

The Misskey UI by default uses various animations which may be too heavy for older or slower computers to run smoothly. These can be disabled in Settings > Client Settings > General:

Q: I am getting some errors when opening the Misskey UI!

This is possible if your instance administrator has performed an update to their Misskey installation. This can be fixed by going to Settings and scrolling down to Clear Cache. This will automatically reload the page and in most cases fix your issue. If you are unable to reach the Settings menu, you can also manually clear your browser's cache. Note that cookies do not have to be cleared, only your browser's cache.

If your issue still persists, you should contact your instance's administrator for further assistance.

Q: I just opened Misskey on another computer and my settings are gone!

Some of Misskey's user preferences are stored in a local cookie. These preferences will not be persistent between different browsers and computers. All settings located under "Client Settings" are stored in a cookie, so these will have to be manually redone on every browser and pc you wish to use Misskey on.

Q: Hey, my question is not here! How can I ask for help?

If you still have questions about Misskey, feel free to ask the community! Simply add the hashtag #askmisskey to your note. Additionally, you can also ask your question on the Misskey Forum.