mirror of
https://github.com/paricafe/misskey.git
synced 2025-02-17 13:37:29 -06:00
Pari Plus! note auto spacing
This commit is contained in:
parent
0205565045
commit
278a4a0ba1
9 changed files with 165 additions and 3 deletions
|
@ -1289,6 +1289,9 @@ showDetailTimeWhenHover: "Hover the timestamp of the note to expand the detailed
|
|||
noteClickToOpen: "Click to open note details"
|
||||
enableFallbackReactButton: "Enable fallback reaction button"
|
||||
enableMFMCheatsheet: "Enable MFM Cheatsheet in post form"
|
||||
autoSpacing: "Auto Spacing"
|
||||
autoSpacingDescription: "Adding spaces between CJK and English characters"
|
||||
auto: "Auto"
|
||||
performance: "Performance"
|
||||
modified: "Modified"
|
||||
discard: "Discard"
|
||||
|
|
|
@ -1298,6 +1298,9 @@ showDetailTimeWhenHover: "悬浮/长按帖文时间戳时,展开详细时间"
|
|||
noteClickToOpen: "点击展开帖文详情"
|
||||
enableFallbackReactButton: "开启Fallback回应按钮"
|
||||
enableMFMCheatsheet: "在帖文编辑框中启用MFM Cheatsheet"
|
||||
autoSpacing: "自动空格"
|
||||
autoSpacingDescription: "在CJK字符和英文字符中添加空格"
|
||||
auto: "自动"
|
||||
messageToFollower: "给关注者的消息"
|
||||
target: "对象"
|
||||
testCaptchaWarning: "这是用于验证码测试的功能。**请勿在生产环境中使用。**"
|
||||
|
|
|
@ -1298,6 +1298,9 @@ showDetailTimeWhenHover: "長按貼文時間戳記時展開詳細時間"
|
|||
noteClickToOpen: "點擊展開貼文詳情"
|
||||
enableFallbackReactButton: "啓用Fallback回應鍵"
|
||||
enableMFMCheatsheet: "在貼文編輯框中啓用MFM Cheatsheet"
|
||||
autoSpacing: "自動間距"
|
||||
autoSpacingDescription: "在CJK字符和英文字符中添加間距"
|
||||
auto: "自動"
|
||||
messageToFollower: "給追隨者的訊息"
|
||||
target: "目標 "
|
||||
testCaptchaWarning: "這是用於驗證碼測試的功能。**請勿在生產環境中使用。**"
|
||||
|
|
|
@ -224,6 +224,7 @@ import { getAppearNote } from '@/scripts/get-appear-note.js';
|
|||
import { useRouter } from '@/router/supplier.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import detectLanguage from '@/scripts/detect-language.js';
|
||||
import { spacingNote } from '@/scripts/autospacing.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
note: Misskey.entities.Note;
|
||||
|
@ -278,7 +279,7 @@ const renoteTime = shallowRef<HTMLElement>();
|
|||
const reactButton = shallowRef<HTMLElement>();
|
||||
const clipButton = shallowRef<HTMLElement>();
|
||||
const likeButton = shallowRef<HTMLElement>();
|
||||
const appearNote = computed(() => getAppearNote(note.value));
|
||||
const appearNote = computed(() => spacingNote(getAppearNote(note.value)));
|
||||
const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
|
||||
const isMyRenote = $i && ($i.id === note.value.userId);
|
||||
const showContent = ref(false);
|
||||
|
|
|
@ -281,6 +281,7 @@ import { getAppearNote } from '@/scripts/get-appear-note.js';
|
|||
import { type Keymap } from '@/scripts/hotkey.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import detectLanguage from '@/scripts/detect-language.js';
|
||||
import { spacingNote } from '@/scripts/autospacing.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
note: Misskey.entities.Note;
|
||||
|
@ -322,7 +323,7 @@ const reactButton = shallowRef<HTMLElement>();
|
|||
const clipButton = shallowRef<HTMLElement>();
|
||||
const likeButton = shallowRef<HTMLElement>();
|
||||
const historyMenuButton = shallowRef<HTMLElement>();
|
||||
const appearNote = computed(() => getAppearNote(note.value));
|
||||
const appearNote = computed(() => spacingNote(getAppearNote(note.value)));
|
||||
const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>();
|
||||
const isMyRenote = $i && ($i.id === note.value.userId);
|
||||
const showContent = ref(false);
|
||||
|
|
|
@ -11,6 +11,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkSwitch v-model="noteClickToOpen">{{ i18n.ts.noteClickToOpen }}</MkSwitch>
|
||||
<MkSwitch v-model="enableFallbackReactButton">{{ i18n.ts.enableFallbackReactButton }}</MkSwitch>
|
||||
<MkSwitch v-model="enableMFMCheatsheet">{{ i18n.ts.enableMFMCheatsheet }}</MkSwitch>
|
||||
<MkSelect v-model="autoSpacingBehaviour">
|
||||
<template #label>{{ i18n.ts.autoSpacing }}</template>
|
||||
<option :value="null">{{ i18n.ts.disabled }}</option>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="special">{{ i18n.ts.auto }}</option>
|
||||
<template #caption>{{ i18n.ts.autoSpacingDescription }}</template>
|
||||
</MkSelect>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -20,12 +27,14 @@ import { i18n } from '@/i18n.js';
|
|||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
|
||||
const autoTranslateButton = computed(defaultStore.makeGetterSetter('autoTranslateButton'));
|
||||
const showDetailTimeWhenHover = computed(defaultStore.makeGetterSetter('showDetailTimeWhenHover'));
|
||||
const noteClickToOpen = computed(defaultStore.makeGetterSetter('noteClickToOpen'));
|
||||
const enableFallbackReactButton = computed(defaultStore.makeGetterSetter('enableFallbackReactButton'));
|
||||
const enableMFMCheatsheet = computed(defaultStore.makeGetterSetter('enableMFMCheatsheet'));
|
||||
const autoSpacingBehaviour = computed(defaultStore.makeGetterSetter('autoSpacingBehaviour'));
|
||||
|
||||
definePageMetadata(() => ({
|
||||
title: 'Pari Plus!',
|
||||
|
|
138
packages/frontend/src/scripts/autospacing.ts
Normal file
138
packages/frontend/src/scripts/autospacing.ts
Normal file
|
@ -0,0 +1,138 @@
|
|||
import * as misskey from 'misskey-js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
||||
const NO_SPACEING_LIST = [
|
||||
'A股',
|
||||
'B股',
|
||||
'H股',
|
||||
'O型',
|
||||
'A型',
|
||||
'B型',
|
||||
'AB型',
|
||||
'RH阴性',
|
||||
'RH阳性',
|
||||
'IP地址',
|
||||
'IP卡',
|
||||
'SIM卡',
|
||||
'IC卡',
|
||||
'ID号',
|
||||
'CD机',
|
||||
'IT界',
|
||||
'PC端',
|
||||
'PM',
|
||||
'PM2.5',
|
||||
'PPT',
|
||||
'QQ号',
|
||||
'SQL注入',
|
||||
'USB口',
|
||||
'UV层',
|
||||
'WiFi',
|
||||
'T恤',
|
||||
'A字裙',
|
||||
'B字裙',
|
||||
'H型',
|
||||
'T型',
|
||||
'X型',
|
||||
'S码',
|
||||
'M码',
|
||||
'L码',
|
||||
'XL码',
|
||||
'XXL码',
|
||||
'A站',
|
||||
'B站',
|
||||
'C站',
|
||||
'N卡',
|
||||
'A卡',
|
||||
'UP主',
|
||||
'X光',
|
||||
'B超',
|
||||
'CT室',
|
||||
'PH值',
|
||||
'X档案',
|
||||
'Q弹',
|
||||
'N倍',
|
||||
'AA制',
|
||||
'K歌',
|
||||
'维生素A',
|
||||
'维生素B',
|
||||
'维生素C',
|
||||
'维生素D',
|
||||
'维生素E',
|
||||
'阿Q',
|
||||
'Q版',
|
||||
'OA系统',
|
||||
'PR值',
|
||||
'A型柱',
|
||||
'B型柱',
|
||||
'N档',
|
||||
'P档',
|
||||
'R档',
|
||||
'D档',
|
||||
'3D打印',
|
||||
];
|
||||
|
||||
const LIST_WINDOW = NO_SPACEING_LIST.reduce((a, b) => Math.max(a, b.length), 0) + 1;
|
||||
|
||||
const hashtagMap = new Map<string, string>();
|
||||
let placeholderCounter = 0;
|
||||
|
||||
function preserveHashtags(text: string): string {
|
||||
placeholderCounter = 0;
|
||||
hashtagMap.clear();
|
||||
|
||||
return text.replace(/#[^\s]+/g, (match) => {
|
||||
const placeholder = `__HASHTAG_${placeholderCounter}__`;
|
||||
hashtagMap.set(placeholder, match);
|
||||
placeholderCounter++;
|
||||
return placeholder;
|
||||
});
|
||||
}
|
||||
|
||||
function restoreHashtags(text: string): string {
|
||||
let result = text;
|
||||
for (const [placeholder, hashtag] of hashtagMap) {
|
||||
result = result.replace(placeholder, hashtag);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function autoSpacing(plainText: string) {
|
||||
if (defaultStore.reactiveState.autoSpacingBehaviour.value == null) return plainText;
|
||||
|
||||
const textWithPlaceholders = preserveHashtags(plainText);
|
||||
|
||||
const rep = (matched: string, c1: string, c2: string, position: number) => {
|
||||
if (defaultStore.reactiveState.autoSpacingBehaviour.value === 'all') return `${c1} ${c2}`;
|
||||
const context = plainText
|
||||
.slice(Math.max(0, position - LIST_WINDOW), position + LIST_WINDOW)
|
||||
.toUpperCase();
|
||||
if (NO_SPACEING_LIST.some((text) => context.includes(text))) {
|
||||
return matched;
|
||||
} else {
|
||||
return `${c1} ${c2}`;
|
||||
}
|
||||
};
|
||||
|
||||
const spacedText = textWithPlaceholders
|
||||
.replace(/([\u4e00-\u9fa5\u0800-\u4e00\uac00-\ud7ff])([a-zA-Z0-9])/g, rep)
|
||||
.replace(/([a-zA-Z0-9,\.:])([\u4e00-\u9fa5\u0800-\u4e00\uac00-\ud7ff])/g, rep);
|
||||
|
||||
return restoreHashtags(spacedText);
|
||||
}
|
||||
|
||||
export function spacingNote(note: misskey.entities.Note) {
|
||||
const noteAsRecord = note as unknown as Record<string, string | null | undefined>;
|
||||
if (!noteAsRecord.__autospacing_raw_text) {
|
||||
noteAsRecord.__autospacing_raw_text = note.text;
|
||||
}
|
||||
if (!noteAsRecord.__autospacing_raw_cw) {
|
||||
noteAsRecord.__autospacing_raw_cw = note.cw;
|
||||
}
|
||||
note.text = noteAsRecord.__autospacing_raw_text
|
||||
? autoSpacing(noteAsRecord.__autospacing_raw_text)
|
||||
: null;
|
||||
note.cw = noteAsRecord.__autospacing_raw_cw
|
||||
? autoSpacing(noteAsRecord.__autospacing_raw_cw)
|
||||
: null;
|
||||
return note;
|
||||
}
|
|
@ -6,5 +6,5 @@
|
|||
import * as Misskey from 'misskey-js';
|
||||
|
||||
export function getAppearNote(note: Misskey.entities.Note) {
|
||||
return Misskey.note.isPureRenote(note) ? note.renote : note;
|
||||
return Misskey.note.isPureRenote(note) ? note.renote! : note;
|
||||
}
|
||||
|
|
|
@ -527,6 +527,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
|||
where: 'device',
|
||||
default: true,
|
||||
},
|
||||
autoSpacingBehaviour: {
|
||||
where: 'device',
|
||||
default: null as 'all' | 'special' | null,
|
||||
},
|
||||
}));
|
||||
|
||||
// TODO: 他のタブと永続化されたstateを同期
|
||||
|
|
Loading…
Add table
Reference in a new issue