From ce4ea5071f58a3d9af7624d02a36f97200186431 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 23 Sep 2021 23:01:32 +0900 Subject: [PATCH 01/38] =?UTF-8?q?enhance(client):=20=E3=82=A2=E3=83=8B?= =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E6=B8=9B?= =?UTF-8?q?=E3=82=89=E3=81=99=E8=A8=AD=E5=AE=9A=E3=82=92=E3=83=A1=E3=83=8B?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=81=AE=E3=82=A2=E3=83=8B=E3=83=A1=E3=83=BC?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AB=E3=82=82=E9=81=A9=E7=94=A8?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #7826 --- CHANGELOG.md | 7 +++++++ src/client/directives/click-anime.ts | 3 +++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8331854c8..d515adf2d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ --> +## 12.x.x (unreleased) + +### Improvements +- アニメーションを減らす設定をメニューのアニメーションにも適用するように + +### Bugfixes + ## 12.91.0 (2021/09/22) ### Improvements diff --git a/src/client/directives/click-anime.ts b/src/client/directives/click-anime.ts index 9fd583d6dd..0d5a6da94e 100644 --- a/src/client/directives/click-anime.ts +++ b/src/client/directives/click-anime.ts @@ -1,7 +1,10 @@ import { Directive } from 'vue'; +import { defaultStore } from '@client/store'; export default { mounted(el, binding, vn) { + if (!defaultStore.state.animation) return; + el.classList.add('_anime_bounce_standBy'); el.addEventListener('mousedown', () => { From 76cdbe74ba15358422293102a9c9cf240d83932b Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 24 Sep 2021 22:28:17 +0900 Subject: [PATCH 02/38] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d515adf2d8..7d412fb7d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ - アニメーションを減らす設定をメニューのアニメーションにも適用するように ### Bugfixes +- Fix createDeleteAccountJob +- admin inbox queue does not show individual jobs ## 12.91.0 (2021/09/22) From ac93af8eb5c0c3c566f4bf9630027d07222736fe Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 25 Sep 2021 10:53:55 +0900 Subject: [PATCH 03/38] Update extensions.json --- .vscode/extensions.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 40b781b552..843b732cd4 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,12 +1,10 @@ { "recommendations": [ - "ducksoupdev.vue2", "editorconfig.editorconfig", "eg2.vscode-npm-script", - "hollowtree.vue-snippets", "ms-vscode.typescript-javascript-grammar", "ms-vscode.vscode-typescript-tslint-plugin", - "octref.vetur", + "johnsoncodehk.volar", "sysoev.language-stylus" ] } From da71d8f4afb83d62891388ab6900c297a60f51cb Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 26 Sep 2021 01:53:56 +0900 Subject: [PATCH 04/38] fix(client): fix tabs of page header behaviour --- CHANGELOG.md | 4 +++- src/client/ui/_common_/header.vue | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d412fb7d1..fcf2f369f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,13 @@ ## 12.x.x (unreleased) ### Improvements -- アニメーションを減らす設定をメニューのアニメーションにも適用するように +- クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように ### Bugfixes - Fix createDeleteAccountJob - admin inbox queue does not show individual jobs +- クライアント: ヘッダーのタブが折り返される問題を修正 +- クライアント: ヘッダーにタブが表示されている状態でタイトルをクリックしたときにタブ選択が表示されるのを修正 ## 12.91.0 (2021/09/22) diff --git a/src/client/ui/_common_/header.vue b/src/client/ui/_common_/header.vue index 1e0db9a3a1..f21be2f9cd 100644 --- a/src/client/ui/_common_/header.vue +++ b/src/client/ui/_common_/header.vue @@ -141,6 +141,7 @@ export default defineComponent({ showTabsPopup(ev) { if (!this.hasTabs) return; + if (!this.narrow) return; ev.preventDefault(); ev.stopPropagation(); const menu = this.info.tabs.map(tab => ({ @@ -218,6 +219,7 @@ export default defineComponent({ white-space: nowrap; text-align: left; font-weight: bold; + flex-shrink: 0; > .avatar { $size: 32px; @@ -263,6 +265,8 @@ export default defineComponent({ > .tabs { margin-left: 16px; font-size: 0.8em; + overflow: auto; + white-space: nowrap; > .tab { display: inline-block; From ece3ac967d90cb2e9744a2ebd55dcbee90cdb980 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sun, 26 Sep 2021 01:57:38 +0900 Subject: [PATCH 05/38] Tune mfmToHtml (#7841) * Tune mfmToHtml * typo * add --- src/mfm/from-html.ts | 124 ++++++++++++++++++++++++++++++++++++++----- test/mfm.ts | 24 +++++++++ 2 files changed, 136 insertions(+), 12 deletions(-) diff --git a/src/mfm/from-html.ts b/src/mfm/from-html.ts index 4c8e2dbec8..14279f3383 100644 --- a/src/mfm/from-html.ts +++ b/src/mfm/from-html.ts @@ -5,7 +5,9 @@ import { URL } from 'url'; const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; -export function fromHtml(html: string, hashtagNames?: string[]): string { +export function fromHtml(html: string, hashtagNames?: string[]): string | null { + if (html == null) return null; + const dom = parse5.parseFragment(html); let text = ''; @@ -19,6 +21,7 @@ export function fromHtml(html: string, hashtagNames?: string[]): string { function getText(node: parse5.Node): string { if (treeAdapter.isTextNode(node)) return node.value; if (!treeAdapter.isElementNode(node)) return ''; + if (node.nodeName === 'br') return '\n'; if (node.childNodes) { return node.childNodes.map(n => getText(n)).join(''); @@ -27,6 +30,14 @@ export function fromHtml(html: string, hashtagNames?: string[]): string { return ''; } + function appendChildren(childNodes: parse5.ChildNode[]): void { + if (childNodes) { + for (const n of childNodes) { + analyze(n); + } + } + } + function analyze(node: parse5.Node) { if (treeAdapter.isTextNode(node)) { text += node.value; @@ -42,6 +53,7 @@ export function fromHtml(html: string, hashtagNames?: string[]): string { break; case 'a': + { const txt = getText(node); const rel = node.attrs.find(x => x.name === 'rel'); const href = node.attrs.find(x => x.name === 'href'); @@ -87,23 +99,111 @@ export function fromHtml(html: string, hashtagNames?: string[]): string { text += generateLink(); } break; + } + + case 'h1': + { + text += '【'; + appendChildren(node.childNodes); + text += '】\n'; + break; + } + + case 'b': + case 'strong': + { + text += '**'; + appendChildren(node.childNodes); + text += '**'; + break; + } + + case 'small': + { + text += ''; + appendChildren(node.childNodes); + text += ''; + break; + } + + case 's': + case 'del': + { + text += '~~'; + appendChildren(node.childNodes); + text += '~~'; + break; + } + + case 'i': + case 'em': + { + text += ''; + appendChildren(node.childNodes); + text += ''; + break; + } + + // block code (
)
+			case 'pre': {
+				if (node.childNodes.length === 1 && node.childNodes[0].nodeName === 'code') {
+					text += '```\n';
+					text += getText(node.childNodes[0]);
+					text += '\n```\n';
+				} else {
+					appendChildren(node.childNodes);
+				}
+				break;
+			}
+
+			// inline code ()
+			case 'code': {
+				text += '`';
+				appendChildren(node.childNodes);
+				text += '`';
+				break;
+			}
+
+			case 'blockquote': {
+				const t = getText(node);
+				if (t) {
+					text += '> ';
+					text += t.split('\n').join(`\n> `);
+				}
+				break;
+			}
 
 			case 'p':
+			case 'h2':
+			case 'h3':
+			case 'h4':
+			case 'h5':
+			case 'h6':
+			{
 				text += '\n\n';
-				if (node.childNodes) {
-					for (const n of node.childNodes) {
-						analyze(n);
-					}
-				}
+				appendChildren(node.childNodes);
 				break;
+			}
 
-			default:
-				if (node.childNodes) {
-					for (const n of node.childNodes) {
-						analyze(n);
-					}
-				}
+			// other block elements
+			case 'div':
+			case 'header':
+			case 'footer':
+			case 'article':
+			case 'li':
+			case 'dt':
+			case 'dd':
+			{
+				text += '\n';
+				appendChildren(node.childNodes);
 				break;
+			}
+
+			default:	// includes inline elements
+			{
+				appendChildren(node.childNodes);
+				break;
+			}
 		}
 	}
 }
diff --git a/test/mfm.ts b/test/mfm.ts
index d9b98cdac3..ecf886ad6c 100644
--- a/test/mfm.ts
+++ b/test/mfm.ts
@@ -19,6 +19,30 @@ describe('toHtml', () => {
 });
 
 describe('fromHtml', () => {
+	it('p', () => {
+		assert.deepStrictEqual(fromHtml('

a

b

'), 'a\n\nb'); + }); + + it('block element', () => { + assert.deepStrictEqual(fromHtml('
a
b
'), 'a\nb'); + }); + + it('inline element', () => { + assert.deepStrictEqual(fromHtml('
  • a
  • b
'), 'a\nb'); + }); + + it('block code', () => { + assert.deepStrictEqual(fromHtml('
a\nb
'), '```\na\nb\n```'); + }); + + it('inline code', () => { + assert.deepStrictEqual(fromHtml('a'), '`a`'); + }); + + it('quote', () => { + assert.deepStrictEqual(fromHtml('
a\nb
'), '> a\n> b'); + }); + it('br', () => { assert.deepStrictEqual(fromHtml('

abc

d

'), 'abc\n\nd'); }); From 67e2768c3e7dec14fa12e27b60ba165dc9cb4de0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 26 Sep 2021 01:58:20 +0900 Subject: [PATCH 06/38] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcf2f369f5..4a5f497ddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Improvements - クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように +- ActivityPub: HTML -> MFMの変換を強化 ### Bugfixes - Fix createDeleteAccountJob From a75f3fb87c2fdd6d0eb66af4c2ce9939a29c180d Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 26 Sep 2021 02:10:07 +0900 Subject: [PATCH 07/38] refactor: fix types --- src/client/scripts/autocomplete.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/scripts/autocomplete.ts b/src/client/scripts/autocomplete.ts index 924d6a62ee..c4bcc4b724 100644 --- a/src/client/scripts/autocomplete.ts +++ b/src/client/scripts/autocomplete.ts @@ -7,9 +7,9 @@ export class Autocomplete { private suggestion: { x: Ref; y: Ref; - q: Ref; + q: Ref; close: Function; - }; + } | null; private textarea: any; private vm: any; private currentType: string; @@ -122,7 +122,7 @@ export class Autocomplete { /** * サジェストを提示します。 */ - private async open(type: string, q: string) { + private async open(type: string, q: string | null) { if (type != this.currentType) { this.close(); } From a70dbb7e74356caed2fe03bec05c4e8d5bde4316 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 26 Sep 2021 02:55:11 +0900 Subject: [PATCH 08/38] =?UTF-8?q?feat(client):=20MFM=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E6=A7=8B=E6=96=87=E3=81=AE=E3=82=B5=E3=82=B8=E3=82=A7=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/client/components/autocomplete.vue | 47 ++++++++++++++++---------- src/client/scripts/autocomplete.ts | 29 +++++++++++++++- 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a5f497ddb..9bbc09860e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Improvements - クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように +- クライアント: MFM関数構文のサジェストを実装 - ActivityPub: HTML -> MFMの変換を強化 ### Bugfixes diff --git a/src/client/components/autocomplete.vue b/src/client/components/autocomplete.vue index 065ee6de2e..e2c1af3356 100644 --- a/src/client/components/autocomplete.vue +++ b/src/client/components/autocomplete.vue @@ -10,12 +10,12 @@
  • {{ $ts.selectUser }}
  • -
      +
      1. {{ hashtag }}
      -
        +
        1. @@ -24,6 +24,11 @@ ({{ emoji.aliasOf }})
        +
          +
        1. + {{ tag }} +
        2. +
        @@ -106,6 +111,8 @@ emojiDefinitions.sort((a, b) => a.name.length - b.name.length); const emojiDb = markRaw(emojiDefinitions.concat(emjdb)); //#endregion +const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'font', 'blur', 'rainbow', 'sparkle']; + export default defineComponent({ props: { type: { @@ -137,11 +144,6 @@ export default defineComponent({ type: Number, required: true, }, - - showing: { - type: Boolean, - required: true - }, }, emits: ['done', 'closed'], @@ -154,18 +156,11 @@ export default defineComponent({ hashtags: [], emojis: [], items: [], + mfmTags: [], select: -1, } }, - watch: { - showing() { - if (!this.showing) { - this.$emit('closed'); - } - } - }, - updated() { this.setPosition(); this.items = (this.$refs.suggests as Element | undefined)?.children || []; @@ -236,7 +231,9 @@ export default defineComponent({ } } - if (this.type == 'user') { + console.log(this.type); + + if (this.type === 'user') { if (this.q == null) { this.users = []; this.fetching = false; @@ -262,7 +259,7 @@ export default defineComponent({ sessionStorage.setItem(cacheKey, JSON.stringify(users)); }); } - } else if (this.type == 'hashtag') { + } else if (this.type === 'hashtag') { if (this.q == null || this.q == '') { this.hashtags = JSON.parse(localStorage.getItem('hashtags') || '[]'); this.fetching = false; @@ -286,7 +283,7 @@ export default defineComponent({ }); } } - } else if (this.type == 'emoji') { + } else if (this.type === 'emoji') { if (this.q == null || this.q == '') { // 最近使った絵文字をサジェスト this.emojis = this.$store.state.recentlyUsedEmojis.map(emoji => emojiDb.find(e => e.emoji == emoji)).filter(x => x != null); @@ -314,6 +311,14 @@ export default defineComponent({ } this.emojis = matched; + } else if (this.type === 'mfmTag') { + console.log(this.q); + if (this.q == null || this.q == '') { + this.mfmTags = MFM_TAGS; + return; + } + + this.mfmTags = MFM_TAGS.filter(tag => tag.startsWith(this.q)); } }, @@ -490,5 +495,11 @@ export default defineComponent({ margin: 0 0 0 8px; } } + + > .mfmTags > li { + + .name { + } + } } diff --git a/src/client/scripts/autocomplete.ts b/src/client/scripts/autocomplete.ts index c4bcc4b724..e952ad3907 100644 --- a/src/client/scripts/autocomplete.ts +++ b/src/client/scripts/autocomplete.ts @@ -70,11 +70,13 @@ export class Autocomplete { const mentionIndex = text.lastIndexOf('@'); const hashtagIndex = text.lastIndexOf('#'); const emojiIndex = text.lastIndexOf(':'); + const mfmTagIndex = text.lastIndexOf('$'); const max = Math.max( mentionIndex, hashtagIndex, - emojiIndex); + emojiIndex, + mfmTagIndex); if (max == -1) { this.close(); @@ -83,6 +85,7 @@ export class Autocomplete { const isMention = mentionIndex != -1; const isHashtag = hashtagIndex != -1; + const isMfmTag = mfmTagIndex != -1; const isEmoji = emojiIndex != -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':'); let opened = false; @@ -114,6 +117,14 @@ export class Autocomplete { } } + if (isMfmTag && !opened) { + const mfmTag = text.substr(mfmTagIndex + 1); + if (!mfmTag.includes(' ')) { + this.open('mfmTag', mfmTag); + opened = true; + } + } + if (!opened) { this.close(); } @@ -244,6 +255,22 @@ export class Autocomplete { const pos = trimmedBefore.length + value.length; this.textarea.setSelectionRange(pos, pos); }); + } else if (type == 'mfmTag') { + const source = this.text; + + const before = source.substr(0, caret); + const trimmedBefore = before.substring(0, before.lastIndexOf('$')); + const after = source.substr(caret); + + // 挿入 + this.text = `${trimmedBefore}$[${value} ]${after}`; + + // キャレットを戻す + this.vm.$nextTick(() => { + this.textarea.focus(); + const pos = trimmedBefore.length + (value.length + 3); + this.textarea.setSelectionRange(pos, pos); + }); } } } From 5fd549656b14d2ba79dca70bc87325844ed1a27a Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 26 Sep 2021 02:56:02 +0900 Subject: [PATCH 09/38] chore: clean up --- src/client/components/autocomplete.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/client/components/autocomplete.vue b/src/client/components/autocomplete.vue index e2c1af3356..98998293ac 100644 --- a/src/client/components/autocomplete.vue +++ b/src/client/components/autocomplete.vue @@ -231,8 +231,6 @@ export default defineComponent({ } } - console.log(this.type); - if (this.type === 'user') { if (this.q == null) { this.users = []; From 4b8a2d2a6bbdeb50aeb55220cfff37c048c10c74 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 26 Sep 2021 03:16:30 +0900 Subject: [PATCH 10/38] =?UTF-8?q?fix(client):=20=E7=B5=B5=E6=96=87?= =?UTF-8?q?=E5=AD=97=E4=B8=80=E8=A6=A7=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AE?= =?UTF-8?q?=E3=82=BF=E3=82=B0=E4=B8=80=E8=A6=A7=E3=82=92=E3=81=A8=E3=82=8A?= =?UTF-8?q?=E3=81=82=E3=81=88=E3=81=9A=E7=84=A1=E5=8A=B9=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重いため --- src/client/pages/emojis.category.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client/pages/emojis.category.vue b/src/client/pages/emojis.category.vue index 091c3f20a9..69e22361d8 100644 --- a/src/client/pages/emojis.category.vue +++ b/src/client/pages/emojis.category.vue @@ -5,9 +5,11 @@ + From 8d93f148beb12b9d740edfd98bdc6778dfc52be4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 26 Sep 2021 03:17:16 +0900 Subject: [PATCH 11/38] =?UTF-8?q?=E9=87=8D=E3=81=84=E3=81=A8=E3=81=84?= =?UTF-8?q?=E3=81=86=E3=81=8B=E9=82=AA=E9=AD=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/pages/emojis.category.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/pages/emojis.category.vue b/src/client/pages/emojis.category.vue index 69e22361d8..7fee792854 100644 --- a/src/client/pages/emojis.category.vue +++ b/src/client/pages/emojis.category.vue @@ -5,7 +5,7 @@ -
        - +
        - + {{ $ts.name }} - + - + CSS @@ -95,11 +95,11 @@ import { defineComponent } from 'vue'; import * as JSON5 from 'json5'; import { toUnicode } from 'punycode/'; -import MkRadio from '@client/components/ui/radio.vue'; +import MkRadio from '@client/components/form/radio.vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; +import MkSelect from '@client/components/form/select.vue'; import MkSample from '@client/components/sample.vue'; import { convertToMisskeyTheme, ThemeValue, convertToViewModel, ThemeViewModel } from '@client/scripts/theme-editor'; diff --git a/src/client/pages/api-console.vue b/src/client/pages/api-console.vue index c6d459fd6d..9aa7d4ea4d 100644 --- a/src/client/pages/api-console.vue +++ b/src/client/pages/api-console.vue @@ -1,7 +1,7 @@