diff --git a/app/aiscript.js b/app/aiscript.js index 1b1f5776..77c15c81 100644 --- a/app/aiscript.js +++ b/app/aiscript.js @@ -1,4 +1,6 @@ const { AiScript, parse, values, utils } = require('@syuilo/aiscript') +const gcc = require('textarea-caret') +global.getCaretCoordinates = gcc global.sanitizeHtml = require('sanitize-html') global.asValue = values global.AiScript = AiScript diff --git a/app/css/acct.css b/app/css/acct.css index 1cb3b415..65893721 100644 --- a/app/css/acct.css +++ b/app/css/acct.css @@ -97,4 +97,13 @@ i.left { } #add { max-width: 1000px; +} +.autocomplete-content { + min-width: 450px; +} +.autocomplete-content li { + white-space: nowrap; + text-overflow: ellipsis; + word-break: break-word; + overflow: hidden; } \ No newline at end of file diff --git a/app/css/master.css b/app/css/master.css index 70d74d78..baa93f81 100644 --- a/app/css/master.css +++ b/app/css/master.css @@ -579,7 +579,7 @@ h2.swal2-title { z-index: 501; } .pageSrcBtn:hover { - background-color: var(--active); + background-color: var(--selected); } .srcQ { margin-top: 0.4rem; diff --git a/app/css/post.css b/app/css/post.css index ef434339..ca0a6beb 100644 --- a/app/css/post.css +++ b/app/css/post.css @@ -69,6 +69,20 @@ textarea { overflow-y: scroll; overflow-x: hidden; } +#suggest { + position: absolute; + top: 0; + left: 0; + background-color: var(--bg); + z-index: 2; + border-radius: 0.5rem; + display: none; + max-height: 10rem; +} +#suggest a { + margin-left: 0.5rem; + margin-right: 0.5rem; +} #emoji-list { width: 100%; height: 15.4rem; diff --git a/app/css/tl.css b/app/css/tl.css index 718cbb1c..c2828824 100644 --- a/app/css/tl.css +++ b/app/css/tl.css @@ -590,7 +590,29 @@ p:not(:last-child) { .small-header .area-sta::-webkit-scrollbar { height: 5px; } - +#tagContextMenu { + position: absolute; + top: 0; + left: 0; + background-color: var(--subcolor); + z-index: 2; + border-radius: 0.4rem; +} +#tagContextMenu a { + display: block; + color: var(--text); + padding-left: 0.5rem; + padding-right: 0.5rem; +} +#tagContextMenu a:hover { + background-color: var(--selected); +} +.firstTCM { + margin-top: 0.4rem; +} +.lastTCM { + margin-bottom: 0.4rem; +} .area-notice_name { grid-area: notice_name; overflow: hidden; diff --git a/app/js/post/emoji.js b/app/js/post/emoji.js index 84e05915..5fe9cdc3 100644 --- a/app/js/post/emoji.js +++ b/app/js/post/emoji.js @@ -126,8 +126,10 @@ function emojiGet(parse, started) { md['if_categorized'] = if_categorized localStorage.setItem('emojis_' + acct_id, JSON.stringify(md)) + localStorage.setItem(`emojis_raw_${acct_id}`, JSON.stringify(json)) } else { localStorage.setItem('emojis_' + acct_id, JSON.stringify(md)) + localStorage.setItem(`emojis_raw_${acct_id}`, JSON.stringify(json)) } localStorage.setItem('emojiseek', 0) if (!started) { diff --git a/app/js/post/suggest.js b/app/js/post/suggest.js index 26626531..9832b23c 100644 --- a/app/js/post/suggest.js +++ b/app/js/post/suggest.js @@ -9,183 +9,162 @@ var suggest input.addEventListener( 'focus', - function() { + function () { localStorage.removeItem('cursor') var acct_id = $('#post-acct-sel').val() $('#suggest').html('') + $('#suggest').hide() window.clearInterval(timer) - timer = window.setInterval(function() { + timer = window.setInterval(function () { var new_val = input.value if (new_val == '') { $('#suggest').html('') - if ($('#poll').hasClass('hide') && $('#emoji').hasClass('hide') && $('#draft').hasClass('hide')) { - $('#right-side').hide() - $('#right-side').css('width', '300px') - $('#left-side').css('width', '100%') - var width = localStorage.getItem('postbox-width') - if (width) { - width = width.replace('px', '') * 1 - } else { - width = 300 - } - $('#post-box').css('width', width + 'px') - } + $('#suggest').hide() return } if (prev_val != new_val) { - var tag = new_val.match(/#(\S{3,})/) - var acct = new_val.match(/@(\S{3,})/) - if (tag && tag[1]) { - var q = tag[1] - } else if (acct && acct[1]) { - var q = acct[1] + const pos = input.selectionStart + let startI = pos - 1 + let hasDomain = false + let hasDomainOnce = false + for (startI = pos - 1; startI >= 0; startI--) { + if (new_val[startI].match(/\./)) hasDomain = true + if (new_val[startI].match(/#|@|:/) && !hasDomain) break + if (new_val[startI].match(/@/) && hasDomainOnce) break + if (new_val[startI].match(/@/) && !hasDomainOnce) hasDomainOnce = true + } + const target = new_val.substr(startI, pos - startI) + var tag = target.match(/#(\S{3,})/g) + var acct = target.match(/@(\S{3,})(@(\S{3,}))?/g) + var emoji = target.match(/:(\S{1,})/g) + if (emoji && emoji[0]) { + const l = emoji[0] + const emojis = localStorage.getItem('emojis_raw_' + acct_id) + const json = emojis ? JSON.parse(emojis) : [] + const reg = new RegExp(`${emoji[0]}`) + let listHtml = '' + for (const emoji of json) { + const { shortcode, url } = emoji + if (`:${shortcode}`.match(reg)) { + listHtml = listHtml + `${listHtml ? `
` : ``}:${shortcode}:` + } + } + $('#suggest').html(listHtml) + $('#suggest').show() + } else if (tag && tag[0]) { + var q = tag[0] + } else if (acct && acct[0]) { + var q = acct[0] } else { $('#suggest').html('') - if ($('#poll').hasClass('hide') && $('#emoji').hasClass('hide') && $('#draft').hasClass('hide')) { - $('#right-side').hide() - $('#right-side').css('width', '300px') - $('#left-side').css('width', '100%') - var width = localStorage.getItem('postbox-width') - if (width) { - width = width.replace('px', '') * 1 - } else { - width = 300 - } - $('#post-box').css('width', width + 'px') - } + $('#suggest').hide() return } - var domain = localStorage.getItem('domain_' + acct_id) - var at = localStorage.getItem('acct_' + acct_id + '_at') - suggest = 'https://' + domain + '/api/v2/search?q=' + q - if (suggest != oldSuggest) { - console.log('Try to get suggest at ' + suggest) - fetch(suggest, { - method: 'GET', - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer ' + at - } - }) - .then(function(response) { - if (!response.ok) { - response.text().then(function(text) { - setLog(response.url, response.status, text) - }) + if (q) { + var domain = localStorage.getItem('domain_' + acct_id) + var at = localStorage.getItem('acct_' + acct_id + '_at') + suggest = 'https://' + domain + '/api/v2/search?q=' + encodeURIComponent(q) + if (suggest != oldSuggest) { + console.log('Try to get suggest at ' + suggest) + fetch(suggest, { + method: 'GET', + headers: { + 'content-type': 'application/json', + Authorization: 'Bearer ' + at } - return response.json() }) - .catch(function(error) { - todo(error) - setLog(start, 'JSON', error) - console.error(error) - }) - .then(function(json) { - console.log(['Search', json]) - //ハッシュタグ - if (json.hashtags[0] && tag) { - if (tag[1]) { - var tags = [] - Object.keys(json.hashtags).forEach(function(key4) { - var tag = json.hashtags[key4] - var his = tag.history - var uses = - his[0].uses * 1 + - his[1].uses * 1 + - his[2].uses * 1 + - his[3].uses * 1 + - his[4].uses * 1 + - his[5].uses * 1 + - his[6].uses * 1 - tagHTML = `
#${escapeHTML(tag.name)} ${uses}toot(s)` + .then(function (response) { + if (!response.ok) { + response.text().then(function (text) { + setLog(response.url, response.status, text) + }) + } + return response.json() + }) + .catch(function (error) { + todo(error) + setLog(start, 'JSON', error) + console.error(error) + }) + .then(function (json) { + console.log(['Search', json]) + //ハッシュタグ + if (json.hashtags[0] && tag) { + if (tag[0]) { + var tags = [] + Object.keys(json.hashtags).forEach(function (key4) { + var tag = json.hashtags[key4] + var his = tag.history + var uses = + his[0].uses * 1 + + his[1].uses * 1 + + his[2].uses * 1 + + his[3].uses * 1 + + his[4].uses * 1 + + his[5].uses * 1 + + his[6].uses * 1 + tagHTML = `
#${escapeHTML(tag.name)} ${uses}toot(s)` - var item = { - uses: uses, - html: tagHTML - } - tags.push(item) - }) - var num_a = -1 - var num_b = 1 - tags = tags.sort(function(a, b) { - var x = a['uses'] - var y = b['uses'] - if (x > y) return num_a - if (x < y) return num_b - return 0 - }) - var ins = '' - var nev = false - Object.keys(tags).forEach(function(key7) { - ins = ins + tags[key7].html - if (key7 <= 0 && !nev) { - ins = ins + '
' - nev = true - } - }) - $('#suggest').html(ins) - $('#right-side').show() - $('#right-side').css('width', '200px') - $('#left-side').css('width', 'calc(100% - 200px)') - var width = localStorage.getItem('postbox-width') - if (width) { - width = width.replace('px', '') * 1 + 200 - } else { - width = 500 + var item = { + uses: uses, + html: tagHTML + } + tags.push(item) + }) + var num_a = -1 + var num_b = 1 + tags = tags.sort(function (a, b) { + var x = a['uses'] + var y = b['uses'] + if (x > y) return num_a + if (x < y) return num_b + return 0 + }) + var ins = '' + var nev = false + Object.keys(tags).forEach(function (key7) { + ins = ins + tags[key7].html + if (key7 <= 0 && !nev) { + //ins = ins + '
' + nev = true + } + }) + $('#suggest').html(ins) + $('#suggest').show() } - $('#post-box').css('width', width + 'px') - $('#poll').addClass('hide') - $('#emoji').addClass('hide') - $('#draft').addClass('hide') - } - } else if (json.accounts[0] && acct[1]) { - var accts = '' - Object.keys(json.accounts).forEach(function(key3) { - var acct = json.accounts[key3] - if (acct.acct != q) { - //Instance Actorって… - if (acct.username.indexOf('.') < 0) { - accts = - accts + - `@${acct.acct}
` + } else if (json.accounts[0] && acct[0]) { + var accts = '' + Object.keys(json.accounts).forEach(function (key3) { + var acct = json.accounts[key3] + if (acct.acct != q) { + //Instance Actorって… + if (acct.username.indexOf('.') < 0) { + accts = + accts + + `@${acct.acct}
` + } } - } - }) - $('#right-side').show() - $('#right-side').css('width', '200px') - $('#left-side').css('width', 'calc(100% - 200px)') - var width = localStorage.getItem('postbox-width') - if (width) { - width = width.replace('px', '') * 1 + 200 + }) + $('#suggest').html(accts) + $('#suggest').show() } else { - width = 500 + } - $('#post-box').css('width', width + 'px') - $('#suggest').html(accts) - $('#poll').addClass('hide') - $('#emoji').addClass('hide') - $('#draft').addClass('hide') - } else { - if ($('#poll').hasClass('hide') && $('#emoji').hasClass('hide') && $('#draft').hasClass('hide')) { - $('#right-side').hide() - $('#right-side').css('width', '300px') - $('#left-side').css('width', '100%') - var width = localStorage.getItem('postbox-width') - if (width) { - width = width.replace('px', '') * 1 - } else { - width = 300 - } - $('#post-box').css('width', width + 'px') - } - } - }) + }) + } } } oldSuggest = suggest - prev_value = new_val + prev_val = new_val + + const rectTextarea = document.querySelector('#textarea') + const rect = rectTextarea.getBoundingClientRect() + const caret = getCaretCoordinates(rectTextarea, rectTextarea.selectionEnd) + $('#suggest').css('top', `calc(${caret.top}px + 1rem)`) + const left = rect.width / 2 < caret.left ? rect.width / 2 : caret.left + $('#suggest').css('left', left) }, 1000) }, false @@ -193,15 +172,20 @@ input.addEventListener( input.addEventListener( 'blur', - function() { + function () { window.clearInterval(timer) favTag() }, false ) -function tagInsert(code, del) { - var blankBefore = ' ' - var blankAfter = ' ' +function tagInsert(code, del, emoji) { + if (localStorage.getItem('emoji-zero-width') == 'yes' && emoji) { + var blankBefore = '​' + var blankAfter = '​' + } else { + var blankBefore = ' ' + var blankAfter = ' ' + } var textarea = document.querySelector('#textarea') var sentence = textarea.value var len = sentence.length @@ -217,10 +201,10 @@ function tagInsert(code, del) { var after = sentence.substr(pos, len) var start = after.substr(0, 1) if (start == ' ') blankAfter = '' - if (len == 0) { - var word = code + if (len === delLen) { + var word = code + blankAfter } else if (len == pos) { - var word = blankBefore + code + var word = blankBefore + code + blankAfter } else if (pos == 0) { var word = code + blankAfter } else { @@ -228,17 +212,9 @@ function tagInsert(code, del) { } sentence = before + word + after textarea.value = sentence - if ($('#poll').hasClass('hide') && $('#emoji').hasClass('hide') && $('#draft').hasClass('hide')) { - $('#right-side').hide() - $('#right-side').css('width', '300px') - $('#left-side').css('width', '50%') - var width = localStorage.getItem('postbox-width').replace('px', '') * 1 - if (!width) { - width = 300 - } - $('#post-box').css('width', width + 'px') - } $('#suggest').html('') + $('#suggest').hide() + $('#textarea').focus() } function cgNPs(q) { suggest = 'https://cg.toot.app/api/v1/search/light?q=' + q @@ -250,23 +226,23 @@ function cgNPs(q) { 'content-type': 'application/json' } }) - .then(function(response) { + .then(function (response) { if (!response.ok) { - response.text().then(function(text) { + response.text().then(function (text) { setLog(response.url, response.status, text) }) } return response.json() }) - .catch(function(error) { + .catch(function (error) { todo(error) setLog(start, 'JSON', error) console.error(error) }) - .then(function(json) { + .then(function (json) { if (json[0]) { var tags = '' - Object.keys(json).forEach(function(key4) { + Object.keys(json).forEach(function (key4) { var tag = json[key4] tags = tags + diff --git a/app/js/tl/card.js b/app/js/tl/card.js index 52983320..2e0e4c05 100644 --- a/app/js/tl/card.js +++ b/app/js/tl/card.js @@ -17,7 +17,8 @@ function additional(acct_id, tlid) { } if (tagThis) { - $(this).attr('href', "javascript:tagShow('" + tagThis + "')") + $(this).attr('href', "#") + $(this).attr('onclick', "tagShow('" + tagThis + "', this)") } }) diff --git a/app/js/tl/misskeyparse.js b/app/js/tl/misskeyparse.js index 9deaaedb..73db0207 100644 --- a/app/js/tl/misskeyparse.js +++ b/app/js/tl/misskeyparse.js @@ -518,7 +518,7 @@ function misskeyParse(obj, mix, acct_id, tlid, popup, mutefilter) { if (tagck) { Object.keys(toot.tags).forEach(function (key4) { var tag = toot.tags[key4] - var tags = '#' + tag + '#' + tag + ':#' + tag + '#' + tag + ':TL Toot ' + 'Pin ' content = content.replace("#" + tag, tags) diff --git a/app/js/tl/parse.js b/app/js/tl/parse.js index c586bcbe..4f3f4103 100644 --- a/app/js/tl/parse.js +++ b/app/js/tl/parse.js @@ -703,31 +703,6 @@ function parse(obj, mix, acct_id, tlid, popup, mutefilter, type, onlyContent) { ${lang.lang_parse_thread} ` } - var tagck = toot.tags[0] - var tags = '' - //タグであれば - if (tagck) { - Object.keys(toot.tags).forEach(function (key4) { - var tag = toot.tags[key4] - var featured = ` Feature ` - tags = - tags + - `#${tag.name - }: - TL Toot  - Pin${featured} ` - }) - tags = '' - } //リプ数 if (toot.replies_count || toot.replies_count === 0) { var replyct = toot.replies_count @@ -1077,7 +1052,7 @@ function parse(obj, mix, acct_id, tlid, popup, mutefilter, type, onlyContent) {
${analyze} - ${mentions}${tags} + ${mentions}
${vis}
diff --git a/app/js/tl/tag.js b/app/js/tl/tag.js index c13b1d92..1d088bb0 100644 --- a/app/js/tl/tag.js +++ b/app/js/tl/tag.js @@ -9,9 +9,46 @@ if (location.search) { } } //よく使うタグ -function tagShow(tag) { - console.log('[data-regTag=' + decodeURI(tag).toLowerCase() + ']') - $('[data-regTag=' + decodeURI(tag).toLowerCase() + ']').toggleClass('hide') +function tagShow(tag, elm) { + const tagTL = lang.lang_parse_tagTL.replace('{{tag}}', '#' + tag) + const tagPin = lang.lang_parse_tagpin.replace('{{tag}}', '#' + tag) + const tagToot = lang.lang_parse_tagtoot.replace('{{tag}}', '#' + tag) + $('#tagCMTL').text(tagTL) + $('#tagCMPin').text(tagPin) + $('#tagCMToot').text(tagToot) + const acct_id = $(elm).parents('.tl').attr('data-acct') + const rect = elm.getBoundingClientRect() + $('#tagContextMenu').css('top', `calc(${rect.top}px + 1rem)`) + $('#tagContextMenu').css('left', `${rect.left}px`) + $('#tagContextMenu').attr('data-tag', tag) + $('#tagContextMenu').attr('data-acct', acct_id) + $('#tagContextMenu').removeClass('hide') + setTimeout(() => tShowBox('open'), 500) +} +function tShowBox(mode) { + if (mode == 'open') { + $('#tagContextMenu').removeClass('hide') + } else if (mode == 'close') { + if (!$('#tagContextMenu').hasClass('hide')) $('#tagContextMenu').addClass('hide') + $('#tagContextMenu').removeClass('keep') + } else { + $('#tagContextMenu').toggleClass('hide') + } +} +function doTShowBox(type) { + $('#tagContextMenu').addClass('hide') + $('#tagContextMenu').removeClass('keep') + const q = $('#tagContextMenu').attr('data-tag') + const acct_id = $('#tagContextMenu').attr('data-acct') + if (type == 'tl') { + tl('tag', q, acct_id, 'add') + } else if (type == 'toot') { + brInsert(`#${q}`) + } else if (type == 'pin') { + tagPin(q) + } else if (type == 'f') { + tagFeature(q, acct_id) + } } //タグ追加 function tagPin(tag) { @@ -22,7 +59,7 @@ function tagPin(tag) { var obj = JSON.parse(tags) } var can - Object.keys(obj).forEach(function(key) { + Object.keys(obj).forEach(function (key) { var tagT = obj[key] if (tagT == tag) { can = true @@ -56,7 +93,7 @@ function favTag() { } var tags = '' var nowPT = localStorage.getItem('stable') - Object.keys(obj).forEach(function(key) { + Object.keys(obj).forEach(function (key) { var tag = obj[key] if (nowPT != tag) { console.log('stable tags:' + nowPT + '/' + tag) @@ -69,28 +106,31 @@ function favTag() { tag = escapeHTML(tag) tags = tags + - `#${tag} + `#${tag} ${nowon}  TL -   + Toot -   + ${ptt} -   + ${lang.lang_del} ` }) if (obj.length > 0) { - $('#taglist').append('My Tags:' + tags) + $('#taglist').append('My Tags: ' + tags) } else { $('#taglist').append('') } } +function tagShowHorizon(tag) { + $('[data-regTag=' + decodeURI(tag).toLowerCase() + ']').toggleClass('hide') +} function trendTag() { $('.trendtag').remove() var domain = 'imastodon.net' @@ -103,28 +143,28 @@ function trendTag() { Authorization: 'Bearer ' + at } }) - .then(function(response) { + .then(function (response) { if (!response.ok) { - response.text().then(function(text) { + response.text().then(function (text) { setLog(response.url, response.status, text) }) } return response.json() }) - .catch(function(error) { + .catch(function (error) { todo(error) setLog(start, 'JSON', error) console.error(error) }) - .then(function(json) { + .then(function (json) { if (json) { var tags = '' json = json.score - Object.keys(json).forEach(function(tag) { + Object.keys(json).forEach(function (tag) { tag = escapeHTML(tag) tags = tags + - `#${tag} + `#${tag}   TL  @@ -134,8 +174,8 @@ function trendTag() { }) $('#taglist').append( '
アイマストドントレンドタグrefresh:' + - tags + - '
' + tags + + '
' ) trendintervalset() } else { @@ -182,18 +222,18 @@ function tagFeature(name, acct_id) { name: name }) }) - .then(function(response) { + .then(function (response) { if (!response.ok) { - response.text().then(function(text) { + response.text().then(function (text) { setLog(response.url, response.status, text) }) } return response.json() }) - .catch(function(error) { + .catch(function (error) { return false }) - .then(function(json) { + .then(function (json) { console.log(json) M.toast({ html: 'Complete: ' + escapeHTML(name), displayLength: 3000 }) }) @@ -202,7 +242,7 @@ function tagFeature(name, acct_id) { function addTag(id) { var columns = JSON.parse(localStorage.getItem('column')) var column = columns[id] - if(!column.data.name) { + if (!column.data.name) { var name = column.data } else { var name = column.data.name @@ -219,7 +259,7 @@ function addTag(id) { o[id] = obj var json = JSON.stringify(o) console.log(json) - localStorage.setItem('column',json) + localStorage.setItem('column', json) columnReload(id, 'tag') } diff --git a/app/js/ui/post-box.js b/app/js/ui/post-box.js index be50dbca..2add5db5 100644 --- a/app/js/ui/post-box.js +++ b/app/js/ui/post-box.js @@ -101,6 +101,7 @@ if (location.search) { $('.mini-btn').text('expand_less') } } + function initPostbox() { $('#posttgl').click(function (e) { if (!$('#post-box').hasClass('appear')) { @@ -124,6 +125,7 @@ function initPostbox() { selectedToot = 0 localStorage.removeItem('nohide') srcBox('close') + tShowBox('close') }) $('#textarea,#cw-text').focusout(function (e) { localStorage.setItem('nohide', true) diff --git a/app/js/ui/tips.js b/app/js/ui/tips.js index 9a933131..69c51bfa 100644 --- a/app/js/ui/tips.js +++ b/app/js/ui/tips.js @@ -117,7 +117,7 @@ function trendTagonTip() { Object.keys(json).forEach(function(tag) { tags = tags + - ` + ` #${escapeHTML(tag)}   diff --git a/app/package.json b/app/package.json index 116d0db7..78f11636 100644 --- a/app/package.json +++ b/app/package.json @@ -71,6 +71,7 @@ "sumchecker": "^3.0.1", "sweetalert2": "^10.15.7", "system-font-families": "^0.4.1", + "textarea-caret": "^3.1.0", "vue": "^2.6.12" }, "devDependencies": { diff --git a/app/source/themes/white.thedesktheme b/app/source/themes/white.thedesktheme index d4fafa2c..c616147a 100644 --- a/app/source/themes/white.thedesktheme +++ b/app/source/themes/white.thedesktheme @@ -17,7 +17,7 @@ bottom: '#eeeeee', emphasized: '#81c784', postbox: '#fff', - active: '#009688', + active: '#66dacf', selected: '#c0c0c0', selectedWithShared: '#b2babd', }, diff --git a/app/view/make/index.sample.html b/app/view/make/index.sample.html index 7fa3271e..0f270693 100644 --- a/app/view/make/index.sample.html +++ b/app/view/make/index.sample.html @@ -35,13 +35,20 @@ -
+
@@webSrc@@
@@tsSrc@@
@@copy@@
@@toot@@
+
+ + + + Feature +
@@nowOffline@@
@@ -199,6 +206,8 @@
+ +

@@ -307,7 +316,6 @@
-
@@ -568,14 +576,14 @@ class="material-icons">people→@@follow@@ people←@@follower@@ - + star@@favRegist@@ more_vert@@more@@
timeline@@timeline@@ - diff --git a/app/view/pl-PL/acct.html b/app/view/pl-PL/acct.html index 8b6842d1..8f9f5cf1 100644 --- a/app/view/pl-PL/acct.html +++ b/app/view/pl-PL/acct.html @@ -40,19 +40,28 @@
-
Dodaj konto
+ +
- Wprowadź domenę instancji (jak mastodon.social)
- -
-
+ Wprowadź domenę instancji (jak mastodon.social) +
+
+ + +
+
+ +
+
+
Uncheck it to skip pasiting code.(login to Mastodon on Windows or macOS) (Recommended: pcheck)