Frontend: Code and post form cleanup

This commit is contained in:
Natty 2024-04-30 13:52:13 +02:00
parent ea4751400b
commit 155e458806
Signed by: natty
GPG Key ID: BF6CB659ADEE60EC
20 changed files with 691 additions and 630 deletions

View File

@ -5,7 +5,6 @@ import type {
MeDetailed, MeDetailed,
Note, Note,
Notification, Notification,
PageEvent,
User, User,
} from "./entities"; } from "./entities";
@ -23,7 +22,6 @@ export type Channels = {
followed: (payload: User) => void; // 他人が自分をフォローしたとき followed: (payload: User) => void; // 他人が自分をフォローしたとき
unfollow: (payload: User) => void; // 自分が他人をフォロー解除したとき unfollow: (payload: User) => void; // 自分が他人をフォロー解除したとき
meUpdated: (payload: MeDetailed) => void; meUpdated: (payload: MeDetailed) => void;
pageEvent: (payload: PageEvent) => void;
urlUploadFinished: (payload: { marker: string; file: DriveFile }) => void; urlUploadFinished: (payload: { marker: string; file: DriveFile }) => void;
readAllNotifications: () => void; readAllNotifications: () => void;
unreadNotification: (payload: Notification) => void; unreadNotification: (payload: Notification) => void;

View File

@ -64,7 +64,7 @@
"prismjs": "1.29.0", "prismjs": "1.29.0",
"punycode": "2.1.1", "punycode": "2.1.1",
"rndstr": "1.0.0", "rndstr": "1.0.0",
"rollup": "^4.17.1", "rollup": "^4.17.2",
"s-age": "1.1.2", "s-age": "1.1.2",
"sass": "1.62.1", "sass": "1.62.1",
"seedrandom": "3.0.5", "seedrandom": "3.0.5",
@ -88,7 +88,7 @@
"vite": "^5.2.10", "vite": "^5.2.10",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vue": "^3.4.26", "vue": "^3.4.26",
"vue-component-type-helpers": "^2.0.14", "vue-component-type-helpers": "^2.0.15",
"vue-isyourpasswordsafe": "^2.0.0", "vue-isyourpasswordsafe": "^2.0.0",
"vue3-otp-input": "^0.4.4", "vue3-otp-input": "^0.4.4",
"vuedraggable": "4.1.0" "vuedraggable": "4.1.0"

View File

@ -3,7 +3,7 @@
:is="self ? 'MkA' : 'a'" :is="self ? 'MkA' : 'a'"
ref="el" ref="el"
class="xlcxczvw _link" class="xlcxczvw _link"
:[attr]="self ? url.substr(local.length) : url" :[attr]="self ? url.substring(local.length) : url"
:rel="rel" :rel="rel"
:target="target" :target="target"
:title="url" :title="url"
@ -28,7 +28,7 @@ const props = withDefaults(
url: string; url: string;
rel?: null | string; rel?: null | string;
}>(), }>(),
{}, {}
); );
const self = props.url.startsWith(local); const self = props.url.startsWith(local);
@ -40,7 +40,7 @@ const el = ref();
useTooltip(el, (showing) => { useTooltip(el, (showing) => {
os.popup( os.popup(
defineAsyncComponent( defineAsyncComponent(
() => import("@/components/MkUrlPreviewPopup.vue"), () => import("@/components/MkUrlPreviewPopup.vue")
), ),
{ {
showing, showing,
@ -48,7 +48,7 @@ useTooltip(el, (showing) => {
source: el.value, source: el.value,
}, },
{}, {},
"closed", "closed"
); );
}); });
</script> </script>

View File

@ -10,7 +10,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { toUnicode } from "punycode"; import { toUnicode } from "punycode";
import MkLink from "@/components/MkLink.vue"; import MkLink from "@/components/MagLink.vue";
import { computed } from "vue"; import { computed } from "vue";
const props = defineProps<{ const props = defineProps<{
@ -19,7 +19,7 @@ const props = defineProps<{
}>(); }>();
const url = computed( const url = computed(
() => `https://matrix.to/#/@${props.username}:${props.host}`, () => `https://matrix.to/#/@${props.username}:${props.host}`
); );
</script> </script>

View File

@ -228,7 +228,7 @@ import XReactionsViewer from "@/components/MkReactionsViewer.vue";
import XStarButton from "@/components/MkStarButton.vue"; import XStarButton from "@/components/MkStarButton.vue";
import XStarButtonNoEmoji from "@/components/MkStarButtonNoEmoji.vue"; import XStarButtonNoEmoji from "@/components/MkStarButtonNoEmoji.vue";
import XQuoteButton from "@/components/MagQuoteButton.vue"; import XQuoteButton from "@/components/MagQuoteButton.vue";
import MkVisibility from "@/components/MkVisibility.vue"; import MkVisibility from "@/components/MagVisibility.vue";
import copyToClipboard from "@/scripts/copy-to-clipboard"; import copyToClipboard from "@/scripts/copy-to-clipboard";
import { url } from "@/config"; import { url } from "@/config";
import { pleaseLogin } from "@/scripts/please-login"; import { pleaseLogin } from "@/scripts/please-login";
@ -611,15 +611,18 @@ defineExpose({
padding: 0 32px 0 32px; padding: 0 32px 0 32px;
display: flex; display: flex;
z-index: 1; z-index: 1;
&:first-child { &:first-child {
margin-top: 20px; margin-top: 20px;
} }
> :not(.line) { > :not(.line) {
width: 0; width: 0;
flex-grow: 1; flex-grow: 1;
position: relative; position: relative;
line-height: 28px; line-height: 28px;
} }
> .line { > .line {
position: relative; position: relative;
z-index: 2; z-index: 2;
@ -634,6 +637,7 @@ defineExpose({
> div > i { > div > i {
margin-left: -0.5px; margin-left: -0.5px;
} }
> .info { > .info {
display: flex; display: flex;
align-items: center; align-items: center;
@ -683,6 +687,7 @@ defineExpose({
color: inherit; color: inherit;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
> .dropdownIcon { > .dropdownIcon {
margin-right: 4px; margin-right: 4px;
} }
@ -693,6 +698,7 @@ defineExpose({
&.collapsedReply { &.collapsedReply {
.line { .line {
opacity: 0.25; opacity: 0.25;
&::after { &::after {
content: ""; content: "";
position: absolute; position: absolute;
@ -705,10 +711,12 @@ defineExpose({
height: calc(50% + 5px); height: calc(50% + 5px);
} }
} }
.info { .info {
color: var(--fgTransparentWeak); color: var(--fgTransparentWeak);
transition: color 0.2s; transition: color 0.2s;
} }
.avatar { .avatar {
width: 1.2em; width: 1.2em;
height: 1.2em; height: 1.2em;
@ -717,14 +725,17 @@ defineExpose({
margin-right: 0.4em; margin-right: 0.4em;
background: var(--panelHighlight); background: var(--panelHighlight);
} }
.username { .username {
font-weight: 700; font-weight: 700;
flex-shrink: 0; flex-shrink: 0;
max-width: 30%; max-width: 30%;
&::after { &::after {
content: ": "; content: ": ";
} }
} }
&:hover, &:hover,
&:focus-within { &:focus-within {
.info { .info {
@ -754,6 +765,7 @@ defineExpose({
display: flex; display: flex;
position: relative; position: relative;
z-index: 2; z-index: 2;
> .avatar { > .avatar {
flex-shrink: 0; flex-shrink: 0;
display: block; display: block;
@ -764,30 +776,36 @@ defineExpose({
top: 0; top: 0;
left: 0; left: 0;
} }
> .header { > .header {
width: 0; width: 0;
flex-grow: 1; flex-grow: 1;
} }
} }
> .main { > .main {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
> .body { > .body {
margin-top: 0.7em; margin-top: 0.7em;
> .translation { > .translation {
border: solid 0.5px var(--divider); border: solid 0.5px var(--divider);
border-radius: var(--radius); border-radius: var(--radius);
padding: 12px; padding: 12px;
margin-top: 8px; margin-top: 8px;
} }
> .renote { > .renote {
padding-top: 8px; padding-top: 8px;
> * { > * {
padding: 16px; padding: 16px;
border: solid 1px var(--renote); border: solid 1px var(--renote);
border-radius: 8px; border-radius: 8px;
transition: background 0.2s; transition: background 0.2s;
&:hover, &:hover,
&:focus-within { &:focus-within {
background-color: var(--panelHighlight); background-color: var(--panelHighlight);
@ -795,6 +813,7 @@ defineExpose({
} }
} }
} }
> .info { > .info {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -804,6 +823,7 @@ defineExpose({
opacity: 0.7; opacity: 0.7;
font-size: 0.9em; font-size: 0.9em;
} }
> .footer { > .footer {
position: relative; position: relative;
z-index: 2; z-index: 2;
@ -811,6 +831,7 @@ defineExpose({
flex-wrap: wrap; flex-wrap: wrap;
pointer-events: none; // Allow clicking anything w/out pointer-events: all; to open post pointer-events: none; // Allow clicking anything w/out pointer-events: all; to open post
margin-top: 0.4em; margin-top: 0.4em;
> :deep(.button) { > :deep(.button) {
position: relative; position: relative;
margin: 0; margin: 0;
@ -823,6 +844,7 @@ defineExpose({
pointer-events: all; pointer-events: all;
height: auto; height: auto;
transition: opacity 0.2s; transition: opacity 0.2s;
&::before { &::before {
content: ""; content: "";
position: absolute; position: absolute;
@ -832,17 +854,21 @@ defineExpose({
z-index: -1; z-index: -1;
transition: background 0.2s; transition: background 0.2s;
} }
&:first-of-type { &:first-of-type {
margin-left: -0.5em; margin-left: -0.5em;
&::before { &::before {
border-radius: 100px 0 0 100px; border-radius: 100px 0 0 100px;
} }
} }
&:last-of-type { &:last-of-type {
&::before { &::before {
border-radius: 0 100px 100px 0; border-radius: 0 100px 100px 0;
} }
} }
&:hover { &:hover {
color: var(--fgHighlighted); color: var(--fgHighlighted);
} }
@ -873,25 +899,32 @@ defineExpose({
font-size: 0.975em; font-size: 0.975em;
--avatarSize: 46px; --avatarSize: 46px;
padding-top: 6px; padding-top: 6px;
> .note-context { > .note-context {
padding-inline: 16px; padding-inline: 16px;
margin-top: 8px; margin-top: 8px;
> :not(.line) { > :not(.line) {
margin-top: 0px; margin-top: 0px;
} }
> .line { > .line {
margin-right: 10px; margin-right: 10px;
&::before { &::before {
margin-top: 8px; margin-top: 8px;
} }
} }
} }
> .article { > .article {
padding: 18px 16px 8px; padding: 18px 16px 8px;
&:first-child, &:first-child,
&:nth-child(2) { &:nth-child(2) {
padding-top: 104px; padding-top: 104px;
} }
> .main > .header-container > .avatar { > .main > .header-container > .avatar {
margin-right: 10px; margin-right: 10px;
// top: calc(14px + var(--stickyTop, 0px)); // top: calc(14px + var(--stickyTop, 0px));

View File

@ -12,7 +12,9 @@
<span v-if="note.user.is_bot" class="is-bot">bot</span> <span v-if="note.user.is_bot" class="is-bot">bot</span>
</MkUserName> </MkUserName>
</MkA> </MkA>
<div class="username"><MkAcct :user="note.user" /></div> <div class="username">
<MkAcct :user="note.user" />
</div>
</div> </div>
<div> <div>
<div class="info"> <div class="info">
@ -23,10 +25,10 @@
v-tooltip.noDelay=" v-tooltip.noDelay="
i18n.t('edited', { i18n.t('edited', {
date: new Date( date: new Date(
note.updated_at, note.updated_at
).toLocaleDateString(), ).toLocaleDateString(),
time: new Date( time: new Date(
note.updated_at, note.updated_at
).toLocaleTimeString(), ).toLocaleTimeString(),
}) })
" "
@ -48,7 +50,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import MkVisibility from "@/components/MkVisibility.vue"; import MkVisibility from "@/components/MagVisibility.vue";
import MkInstanceTicker from "@/components/MkInstanceTicker.vue"; import MkInstanceTicker from "@/components/MkInstanceTicker.vue";
import { notePage } from "@/filters/note"; import { notePage } from "@/filters/note";
import { userPage } from "@/filters/user"; import { userPage } from "@/filters/user";
@ -76,17 +78,20 @@ const showTicker =
border-radius: 100px; border-radius: 100px;
font-size: 0.8em; font-size: 0.8em;
text-shadow: 0 2px 2px var(--shadow); text-shadow: 0 2px 2px var(--shadow);
> .avatar { > .avatar {
width: 3.7em; width: 3.7em;
height: 3.7em; height: 3.7em;
margin-right: 1em; margin-right: 1em;
} }
> .user-info { > .user-info {
width: 0; width: 0;
flex-grow: 1; flex-grow: 1;
line-height: 1.5; line-height: 1.5;
display: flex; display: flex;
font-size: 1.2em; font-size: 1.2em;
> div { > div {
&:first-child { &:first-child {
flex-grow: 1; flex-grow: 1;
@ -95,22 +100,27 @@ const showTicker =
text-overflow: ellipsis; text-overflow: ellipsis;
gap: 0.1em 0; gap: 0.1em 0;
} }
&:last-child { &:last-child {
max-width: 50%; max-width: 50%;
gap: 0.3em 0.5em; gap: 0.3em 0.5em;
} }
.article > .main & { .article > .main & {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
&:last-child { &:last-child {
align-items: flex-end; align-items: flex-end;
} }
> * { > * {
max-width: 100%; max-width: 100%;
} }
} }
} }
.name { .name {
// flex: 1 1 0px; // flex: 1 1 0px;
display: inline; display: inline;
@ -150,6 +160,7 @@ const showTicker =
flex-shrink: 0; flex-shrink: 0;
margin-left: 0.5em; margin-left: 0.5em;
font-size: 0.9em; font-size: 0.9em;
.created-at { .created-at {
max-width: 100%; max-width: 100%;
overflow: hidden; overflow: hidden;
@ -161,6 +172,7 @@ const showTicker =
display: inline-flex; display: inline-flex;
margin-left: 0.5em; margin-left: 0.5em;
vertical-align: middle; vertical-align: middle;
> .name { > .name {
display: none; display: none;
} }

View File

@ -3,7 +3,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, watch } from "vue"; import { reactive, watch } from "vue";
import gsap from "gsap"; import gsap from "gsap";
import number from "@/filters/number"; import number from "@/filters/number";
@ -22,6 +22,6 @@ watch(
}, },
{ {
immediate: true, immediate: true,
}, }
); );
</script> </script>

View File

@ -101,8 +101,8 @@
<XNoteSimple v-if="renote" class="preview" :note="renote" /> <XNoteSimple v-if="renote" class="preview" :note="renote" />
<div v-if="quoteId" class="with-quote"> <div v-if="quoteId" class="with-quote">
<i class="ph-quotes ph-bold ph-lg"></i> <i class="ph-quotes ph-bold ph-lg"></i>
{{ i18n.ts.quoteAttached {{ i18n.ts.quoteAttached }}
}}<button class="_button" @click="quoteId = null"> <button class="_button" @click="quoteId = null">
<i class="ph-x ph-bold ph-lg"></i> <i class="ph-x ph-bold ph-lg"></i>
</button> </button>
</div> </div>
@ -127,8 +127,8 @@
>{{ i18n.ts.notSpecifiedMentionWarning }} - >{{ i18n.ts.notSpecifiedMentionWarning }} -
<button class="_textButton" @click="addMissingMention()"> <button class="_textButton" @click="addMissingMention()">
{{ i18n.ts.add }} {{ i18n.ts.add }}
</button></MkInfo </button>
> </MkInfo>
<input <input
v-show="useCw" v-show="useCw"
ref="cwInputEl" ref="cwInputEl"
@ -236,13 +236,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { import {
computed,
defineAsyncComponent, defineAsyncComponent,
inject, inject,
nextTick, nextTick,
onBeforeUnmount,
onMounted, onMounted,
watch,
ref, ref,
computed, watch,
} from "vue"; } from "vue";
import * as mfm from "mfm-js"; import * as mfm from "mfm-js";
import * as misskey from "calckey-js"; import * as misskey from "calckey-js";
@ -285,7 +286,7 @@ const props = withDefaults(
mention?: misskey.entities.User; mention?: misskey.entities.User;
specified?: misskey.entities.User; specified?: misskey.entities.User;
initialText?: string; initialText?: string;
initialVisibility?: typeof misskey.noteVisibilities; initialVisibility?: (typeof misskey.noteVisibilities)[number];
initialFiles?: ( initialFiles?: (
| packed.PackDriveFileBase | packed.PackDriveFileBase
| misskey.entities.DriveFile | misskey.entities.DriveFile
@ -303,7 +304,7 @@ const props = withDefaults(
initialVisibleUsers: () => [], initialVisibleUsers: () => [],
autofocus: true, autofocus: true,
showMfmCheatSheet: true, showMfmCheatSheet: true,
}, }
); );
const emit = defineEmits<{ const emit = defineEmits<{
@ -332,20 +333,20 @@ let cw = ref<string | null>(null);
let localOnly = ref<boolean>( let localOnly = ref<boolean>(
props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility
? defaultStore.state.localOnly ? defaultStore.state.localOnly
: defaultStore.state.defaultNoteLocalOnly, : defaultStore.state.defaultNoteLocalOnly
); );
let visibility = ref( let visibility = ref(
props.initialVisibility ?? props.initialVisibility ??
((defaultStore.state.rememberNoteVisibility ((defaultStore.state.rememberNoteVisibility
? defaultStore.state.visibility ? defaultStore.state.visibility
: defaultStore.state : defaultStore.state
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number]), .defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number])
); );
let visibleUsers = ref([]); let visibleUsers = ref([]);
if (props.initialVisibleUsers) { if (props.initialVisibleUsers) {
props.initialVisibleUsers.forEach(pushVisibleUser); props.initialVisibleUsers.forEach(pushVisibleUser);
} }
let autocomplete = ref(null); let autocomplete = ref<Autocomplete[] | null>(null);
let draghover = ref(false); let draghover = ref(false);
let quoteId = ref<string | null>(null); let quoteId = ref<string | null>(null);
let hasNotSpecifiedMentions = ref(false); let hasNotSpecifiedMentions = ref(false);
@ -419,9 +420,11 @@ const canPost = computed((): boolean => {
}); });
const withHashtags = computed( const withHashtags = computed(
defaultStore.makeGetterSetter("postFormWithHashtags"), defaultStore.makeGetterSetter("postFormWithHashtags")
);
const hashtags = computed<string>(
defaultStore.makeGetterSetter("postFormHashtags")
); );
const hashtags = computed(defaultStore.makeGetterSetter("postFormHashtags"));
watch(text, () => { watch(text, () => {
checkMissingMention(); checkMissingMention();
@ -434,7 +437,7 @@ watch(
}, },
{ {
deep: true, deep: true,
}, }
); );
if (props.mention) { if (props.mention) {
@ -482,7 +485,7 @@ if (props.reply && props.reply.text != null) {
if ( if (
props.reply && props.reply &&
["home", "followers", "specified"].includes( ["home", "followers", "specified"].includes(
magLegacyVisibility(props.reply.visibility), magLegacyVisibility(props.reply.visibility)
) )
) { ) {
if ( if (
@ -492,7 +495,7 @@ if (
visibility.value = "followers"; visibility.value = "followers";
} else if ( } else if (
["home", "followers"].includes( ["home", "followers"].includes(
magLegacyVisibility(props.reply.visibility), magLegacyVisibility(props.reply.visibility)
) && ) &&
visibility.value === "specified" visibility.value === "specified"
) { ) {
@ -505,7 +508,7 @@ if (
if (ids) { if (ids) {
os.api("users/show", { os.api("users/show", {
userIds: ids.filter( userIds: ids.filter(
(uid) => uid !== $i.id && uid !== props.reply!.user.id, (uid) => uid !== $i.id && uid !== props.reply!.user.id
), ),
}).then((users) => { }).then((users) => {
users.forEach(pushVisibleUser); users.forEach(pushVisibleUser);
@ -516,7 +519,7 @@ if (
os.api("users/show", { userId: props.reply.user.id }).then( os.api("users/show", { userId: props.reply.user.id }).then(
(user) => { (user) => {
pushVisibleUser(user); pushVisibleUser(user);
}, }
); );
} }
} }
@ -550,7 +553,7 @@ function checkMissingMention() {
for (const x of extractMentions(ast)) { for (const x of extractMentions(ast)) {
if ( if (
!visibleUsers.value.some( !visibleUsers.value.some(
(u) => u.username === x.username && u.host === x.host, (u) => u.username === x.username && u.host === x.host
) )
) { ) {
hasNotSpecifiedMentions.value = true; hasNotSpecifiedMentions.value = true;
@ -567,13 +570,13 @@ function addMissingMention() {
for (const x of extractMentions(ast)) { for (const x of extractMentions(ast)) {
if ( if (
!visibleUsers.value.some( !visibleUsers.value.some(
(u) => u.username === x.username && u.host === x.host, (u) => u.username === x.username && u.host === x.host
) )
) { ) {
os.api("users/show", { username: x.username, host: x.host }).then( os.api("users/show", { username: x.username, host: x.host }).then(
(user) => { (user) => {
visibleUsers.value.push(user); visibleUsers.value.push(user);
}, }
); );
} }
} }
@ -601,7 +604,7 @@ function focus() {
textareaEl.value.focus(); textareaEl.value.focus();
textareaEl.value.setSelectionRange( textareaEl.value.setSelectionRange(
textareaEl.value.value.length, textareaEl.value.value.length,
textareaEl.value.value.length, textareaEl.value.value.length
); );
} }
} }
@ -612,7 +615,7 @@ function chooseFileFrom(ev) {
for (const file of files_) { for (const file of files_) {
files.value.push(file); files.value.push(file);
} }
}, }
); );
} }
@ -644,7 +647,7 @@ function upload(file: File, name?: string) {
function setVisibility() { function setVisibility() {
os.popup( os.popup(
defineAsyncComponent( defineAsyncComponent(
() => import("@/components/MkVisibilityPicker.vue"), () => import("@/components/MkVisibilityPicker.vue")
), ),
{ {
currentVisibility: visibility.value, currentVisibility: visibility.value,
@ -665,14 +668,14 @@ function setVisibility() {
} }
}, },
}, },
"closed", "closed"
); );
} }
function pushVisibleUser(user) { function pushVisibleUser(user) {
if ( if (
!visibleUsers.value.some( !visibleUsers.value.some(
(u) => u.username === user.username && u.host === user.host, (u) => u.username === user.username && u.host === user.host
) )
) { ) {
visibleUsers.value.push(user); visibleUsers.value.push(user);
@ -697,13 +700,9 @@ function clear() {
} }
function onKeydown(ev: KeyboardEvent) { function onKeydown(ev: KeyboardEvent) {
if ( if (ev.key === "Enter" && (ev.ctrlKey || ev.metaKey) && canPost.value)
(ev.which === 10 || ev.which === 13) &&
(ev.ctrlKey || ev.metaKey) &&
canPost.value
)
post(); post();
if (ev.which === 27) emit("esc"); if (ev.key === "Escape") emit("esc");
} }
function onCompositionUpdate(ev: CompositionEvent) { function onCompositionUpdate(ev: CompositionEvent) {
@ -715,16 +714,24 @@ function onCompositionEnd(ev: CompositionEvent) {
} }
async function onPaste(ev: ClipboardEvent) { async function onPaste(ev: ClipboardEvent) {
if (!ev.clipboardData) {
return;
}
for (const { item, i } of Array.from(ev.clipboardData.items).map( for (const { item, i } of Array.from(ev.clipboardData.items).map(
(item, i) => ({ item, i }), (item, i) => ({ item, i })
)) { )) {
if (item.kind === "file") { if (item.kind === "file") {
const file = item.getAsFile(); const file = item.getAsFile();
if (!file) {
continue;
}
const lio = file.name.lastIndexOf("."); const lio = file.name.lastIndexOf(".");
const ext = lio >= 0 ? file.name.slice(lio) : ""; const ext = lio >= 0 ? file.name.slice(lio) : "";
const formatted = `${formatTimeString( const formatted = `${formatTimeString(
new Date(file.lastModified), new Date(file.lastModified),
defaultStore.state.pastedFileName, defaultStore.state.pastedFileName
).replace(/{{number}}/g, `${i + 1}`)}${ext}`; ).replace(/{{number}}/g, `${i + 1}`)}${ext}`;
upload(file, formatted); upload(file, formatted);
} }
@ -744,9 +751,9 @@ async function onPaste(ev: ClipboardEvent) {
return; return;
} }
quoteId.value = paste quoteId.value =
.substring(url.length) paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)?.[1] ??
.match(/^\/notes\/(.+?)\/?$/)[1]; null;
}); });
} }
} }
@ -872,7 +879,7 @@ async function post() {
if (postAccount.value) { if (postAccount.value) {
const storedAccounts = await getAccounts(); const storedAccounts = await getAccounts();
token = storedAccounts.find( token = storedAccounts.find(
(x) => x.id === postAccount.value.id, (x) => x.id === postAccount.value.id
)?.token; )?.token;
} }
@ -889,11 +896,11 @@ async function post() {
.filter((x) => x.type === "hashtag") .filter((x) => x.type === "hashtag")
.map((x) => x.props.hashtag); .map((x) => x.props.hashtag);
const history = JSON.parse( const history = JSON.parse(
localStorage.getItem("hashtags") || "[]", localStorage.getItem("hashtags") || "[]"
) as string[]; ) as string[];
localStorage.setItem( localStorage.setItem(
"hashtags", "hashtags",
JSON.stringify(unique(hashtags_.concat(history))), JSON.stringify(unique(hashtags_.concat(history)))
); );
} }
posting.value = false; posting.value = false;
@ -943,7 +950,7 @@ function openAccountMenu(ev: MouseEvent) {
} }
}, },
}, },
ev, ev
); );
} }
@ -957,9 +964,11 @@ onMounted(() => {
} }
// TODO: detach when unmount // TODO: detach when unmount
new Autocomplete(textareaEl.value, text); autocomplete.value = [
new Autocomplete(cwInputEl.value, cw); new Autocomplete(textareaEl.value!, text),
new Autocomplete(hashtagsInputEl.value, hashtags); new Autocomplete(cwInputEl.value!, cw),
new Autocomplete(hashtagsInputEl.value!, hashtags),
];
nextTick(() => { nextTick(() => {
// 稿 // 稿
@ -974,7 +983,7 @@ onMounted(() => {
visibility.value = draft.data.visibility; visibility.value = draft.data.visibility;
localOnly.value = draft.data.localOnly; localOnly.value = draft.data.localOnly;
files.value = (draft.data.files || []).filter( files.value = (draft.data.files || []).filter(
(draftFile) => draftFile, (draftFile) => draftFile
); );
if (draft.data.poll) { if (draft.data.poll) {
poll.value = draft.data.poll; poll.value = draft.data.poll;
@ -1007,6 +1016,10 @@ onMounted(() => {
nextTick(() => watchForDraft()); nextTick(() => watchForDraft());
}); });
}); });
onBeforeUnmount(() => {
autocomplete.value?.forEach((a) => a.detach());
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -64,15 +64,14 @@ const prependMany = async () => {
{ context: true, attachments: true }, { context: true, attachments: true },
{ {
id: note, id: note,
}, }
), )
), )
) )
) )
.filter((p) => p.status === "fulfilled") .filter((p) => p.status === "fulfilled")
.map( .map(
(p) => (p) => (p as PromiseFulfilledResult<packed.PackNoteMaybeFull>).value
(p as PromiseFulfilledResult<packed.PackNoteMaybeFull>).value,
); );
for (const n of notes) { for (const n of notes) {
@ -247,11 +246,4 @@ onUnmounted(() => {
connection.dispose(); connection.dispose();
if (connection2) connection2.dispose(); if (connection2) connection2.dispose();
}); });
/* TODO
const timetravel = (date?: Date) => {
this.date = date;
this.$refs.tl.reload();
};
*/
</script> </script>

View File

@ -27,12 +27,12 @@
:target="target" :target="target"
@click.stop @click.stop
> >
<img class="inner" :src="url" decoding="async" /> <img width="32" height="32" class="inner" :src="url" decoding="async" />
</MkA> </MkA>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch, computed, ref } from "vue"; import { computed, ref, watch } from "vue";
import { getStaticImageUrl } from "@/scripts/get-static-image-url"; import { getStaticImageUrl } from "@/scripts/get-static-image-url";
import { extractAvgColorFromBlurhash } from "@/scripts/extract-avg-color-from-blurhash"; import { extractAvgColorFromBlurhash } from "@/scripts/extract-avg-color-from-blurhash";
import { acct, userPage } from "@/filters/user"; import { acct, userPage } from "@/filters/user";
@ -50,7 +50,7 @@ const props = withDefaults(
target: null, target: null,
disableLink: false, disableLink: false,
disablePreview: false, disablePreview: false,
}, }
); );
const emit = defineEmits<{ const emit = defineEmits<{
@ -60,7 +60,7 @@ const emit = defineEmits<{
const url = computed(() => const url = computed(() =>
defaultStore.state.disableShowingAnimatedImages defaultStore.state.disableShowingAnimatedImages
? getStaticImageUrl(props.user.avatar_url) ? getStaticImageUrl(props.user.avatar_url)
: props.user.avatar_url, : props.user.avatar_url
); );
function onClick(ev: MouseEvent) { function onClick(ev: MouseEvent) {
@ -76,7 +76,7 @@ watch(
}, },
{ {
immediate: true, immediate: true,
}, }
); );
</script> </script>

View File

@ -8,7 +8,7 @@
import { h, shallowRef, VNodeArrayChildren, VNodeChild, watch } from "vue"; import { h, shallowRef, VNodeArrayChildren, VNodeChild, watch } from "vue";
import * as mfm from "mfm-js"; import * as mfm from "mfm-js";
import MkUrl from "@/components/global/MkUrl.vue"; import MkUrl from "@/components/global/MkUrl.vue";
import MkLink from "@/components/MkLink.vue"; import MkLink from "@/components/MagLink.vue";
import MagMention from "@/components/MagMention.vue"; import MagMention from "@/components/MagMention.vue";
import MagMatrixMention from "@/components/MagMatrixMention.vue"; import MagMatrixMention from "@/components/MagMatrixMention.vue";
import { concat } from "@/scripts/array"; import { concat } from "@/scripts/array";
@ -50,7 +50,7 @@ const props = withDefaults(
nowrap: false, nowrap: false,
author: null, author: null,
isNote: true, isNote: true,
}, }
); );
function render() { function render() {
@ -59,7 +59,7 @@ function render() {
let result: Result<MagNode[], MagnetarParseError> | null = null; let result: Result<MagNode[], MagnetarParseError> | null = null;
if (props.mm) { if (props.mm) {
result = parseMagnetarMarkdownXml(props.mm).flatMap( result = parseMagnetarMarkdownXml(props.mm).flatMap(
magnetarMarkdownToMfm, magnetarMarkdownToMfm
); );
} }
@ -98,7 +98,7 @@ function render() {
case "text": { case "text": {
const text = token.props.text.replace( const text = token.props.text.replace(
/(\r\n|\n|\r)/g, /(\r\n|\n|\r)/g,
"\n", "\n"
); );
if (!props.plain) { if (!props.plain) {
@ -129,7 +129,7 @@ function render() {
{ {
style: "font-style: oblique;", style: "font-style: oblique;",
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -277,7 +277,7 @@ function render() {
{ {
class: "mfm-x2", class: "mfm-x2",
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -288,7 +288,7 @@ function render() {
{ {
class: "mfm-x3", class: "mfm-x3",
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -299,7 +299,7 @@ function render() {
{ {
class: "mfm-x4", class: "mfm-x4",
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -327,7 +327,7 @@ function render() {
{ {
class: "_blur_text", class: "_blur_text",
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -344,26 +344,26 @@ function render() {
} }
case "position": { case "position": {
const x = parseFloat( const x = parseFloat(
"" + token.props.args.x ?? "0", "" + token.props.args.x ?? "0"
); );
const y = parseFloat( const y = parseFloat(
"" + token.props.args.y ?? "0", "" + token.props.args.y ?? "0"
); );
style = `transform: translateX(${x}em) translateY(${y}em);`; style = `transform: translateX(${x}em) translateY(${y}em);`;
break; break;
} }
case "crop": { case "crop": {
const top = parseFloat( const top = parseFloat(
"" + token.props.args.top ?? "0", "" + token.props.args.top ?? "0"
); );
const right = parseFloat( const right = parseFloat(
"" + token.props.args.right ?? "0", "" + token.props.args.right ?? "0"
); );
const bottom = parseFloat( const bottom = parseFloat(
"" + token.props.args.bottom ?? "0", "" + token.props.args.bottom ?? "0"
); );
const left = parseFloat( const left = parseFloat(
"" + token.props.args.left ?? "0", "" + token.props.args.left ?? "0"
); );
style = `clip-path: inset(${top}% ${right}% ${bottom}% ${left}%);`; style = `clip-path: inset(${top}% ${right}% ${bottom}% ${left}%);`;
break; break;
@ -371,11 +371,11 @@ function render() {
case "scale": { case "scale": {
const x = Math.min( const x = Math.min(
parseFloat("" + token.props.args.x ?? "1"), parseFloat("" + token.props.args.x ?? "1"),
5, 5
); );
const y = Math.min( const y = Math.min(
parseFloat("" + token.props.args.y ?? "1"), parseFloat("" + token.props.args.y ?? "1"),
5, 5
); );
style = `transform: scale(${x}, ${y});`; style = `transform: scale(${x}, ${y});`;
break; break;
@ -401,7 +401,7 @@ function render() {
{ {
style: "opacity: 0.7;", style: "opacity: 0.7;",
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -412,7 +412,7 @@ function render() {
{ {
style: "text-align: center;", style: "text-align: center;",
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -424,7 +424,7 @@ function render() {
{ {
style: `display: inline-block;${style}`, style: `display: inline-block;${style}`,
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -447,7 +447,7 @@ function render() {
{ {
style: "opacity: 0.7;", style: "opacity: 0.7;",
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -459,7 +459,7 @@ function render() {
{ {
style: "text-align: center;", style: "text-align: center;",
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -483,7 +483,7 @@ function render() {
url: token.props.url, url: token.props.url,
rel: "nofollow noopener", rel: "nofollow noopener",
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -520,11 +520,11 @@ function render() {
{ {
key: Math.random(), key: Math.random(),
to: `/tags/${encodeURIComponent( to: `/tags/${encodeURIComponent(
token.props.hashtag, token.props.hashtag
)}`, )}`,
style: "color:var(--hashtag);", style: "color:var(--hashtag);",
}, },
`#${token.props.hashtag}`, `#${token.props.hashtag}`
), ),
]; ];
} }
@ -559,7 +559,7 @@ function render() {
{ {
class: "quote", class: "quote",
}, },
genEl(token.children), genEl(token.children)
), ),
]; ];
} }
@ -576,12 +576,12 @@ function render() {
`:${magTransProperty( `:${magTransProperty(
e, e,
"shortcode", "shortcode",
"name", "name"
)}:`, )}:`
), ),
{ name, host, url: null! }, { name, host, url: null! }
), )
)?.url ?? null!, )?.url ?? null!
); );
if (magIsMissingEmoji(emoji)) { if (magIsMissingEmoji(emoji)) {
@ -630,7 +630,7 @@ function render() {
case "search": { case "search": {
const sentinel = "#"; const sentinel = "#";
let ast2 = (props.plain ? mfm.parseSimple : mfm.parse)( let ast2 = (props.plain ? mfm.parseSimple : mfm.parse)(
token.props.content + sentinel, token.props.content + sentinel
); );
const lastNode = ast2[ast2.length - 1]; const lastNode = ast2[ast2.length - 1];
@ -644,7 +644,7 @@ function render() {
) { ) {
lastNode.props.text = lastNode.props.text.slice( lastNode.props.text = lastNode.props.text.slice(
0, 0,
-1, -1
); );
} }
@ -672,13 +672,13 @@ function render() {
default: { default: {
console.error( console.error(
"unrecognized ast type:", "unrecognized ast type:",
(token as any)?.type, (token as any)?.type
); );
return []; return [];
} }
} }
}), })
); );
return h("span", genEl(ast)); return h("span", genEl(ast));
@ -692,6 +692,6 @@ watch(
}, },
{ {
immediate: true, immediate: true,
}, }
); );
</script> </script>

View File

@ -27,7 +27,7 @@ import { ComponentProps } from "vue-component-type-helpers";
export const pendingApiRequestsCount = ref(0); export const pendingApiRequestsCount = ref(0);
const apiClient = new Misskey.api.APIClient({ const apiClient = new Misskey.api.APIClient({
origin: url, origin: url
}); });
const magnetarApiClient = new MagApiClient(`${url}/mag/v1`); const magnetarApiClient = new MagApiClient(`${url}/mag/v1`);
@ -49,7 +49,7 @@ export async function magApi<
| string | string
| number; | number;
}, },
token?: string | null | undefined, token?: string | null | undefined
): Promise< ): Promise<
T["paginated"] extends true ? PaginatedResult<T["response"]> : T["response"] T["paginated"] extends true ? PaginatedResult<T["response"]> : T["response"]
> { > {
@ -60,7 +60,7 @@ export async function magApi<
endpoint, endpoint,
data, data,
pathParams, pathParams,
token || $i?.token, token || $i?.token
); );
} finally { } finally {
pendingApiRequestsCount.value--; pendingApiRequestsCount.value--;
@ -75,7 +75,7 @@ export async function feApi<T extends keyof FrontendApiEndpoints & string>(
FrontendApiEndpoints[T]["response"] FrontendApiEndpoints[T]["response"]
>, >,
data: FrontendApiEndpoints[T]["request"], data: FrontendApiEndpoints[T]["request"],
token?: string | null | undefined, token?: string | null | undefined
): Promise<FrontendApiEndpoints[T]["response"]> { ): Promise<FrontendApiEndpoints[T]["response"]> {
type Response = FrontendApiEndpoints[T]["response"]; type Response = FrontendApiEndpoints[T]["response"];
@ -104,7 +104,7 @@ export async function feApi<T extends keyof FrontendApiEndpoints & string>(
endpointDef.method !== "GET" ? JSON.stringify(data) : undefined, endpointDef.method !== "GET" ? JSON.stringify(data) : undefined,
credentials: "omit", credentials: "omit",
cache: "no-cache", cache: "no-cache",
headers: authorization ? { authorization } : {}, headers: authorization ? { authorization } : {}
}) })
.then(async (res) => { .then(async (res) => {
const body = res.status === 204 ? null : await res.json(); const body = res.status === 204 ? null : await res.json();
@ -130,7 +130,7 @@ export async function feApi<T extends keyof FrontendApiEndpoints & string>(
export const api = (( export const api = ((
endpoint: string, endpoint: string,
data: Record<string, any> = {}, data: Record<string, any> = {},
token?: string | null | undefined, token?: string | null | undefined
) => { ) => {
pendingApiRequestsCount.value++; pendingApiRequestsCount.value++;
@ -151,8 +151,8 @@ export const api = ((
body: JSON.stringify(data), body: JSON.stringify(data),
credentials: "omit", credentials: "omit",
cache: "no-cache", cache: "no-cache",
headers: authorization ? { authorization } : {}, headers: authorization ? { authorization } : {}
}, }
) )
.then(async (res) => { .then(async (res) => {
const body = res.status === 204 ? null : await res.json(); const body = res.status === 204 ? null : await res.json();
@ -176,7 +176,7 @@ export const api = ((
export const apiGet = (( export const apiGet = ((
endpoint: string, endpoint: string,
data: Record<string, any> = {}, data: Record<string, any> = {},
token?: string | null | undefined, token?: string | null | undefined
) => { ) => {
pendingApiRequestsCount.value++; pendingApiRequestsCount.value++;
@ -197,7 +197,7 @@ export const apiGet = ((
method: "GET", method: "GET",
credentials: "omit", credentials: "omit",
cache: "default", cache: "default",
headers: authorization ? { authorization } : {}, headers: authorization ? { authorization } : {}
}) })
.then(async (res) => { .then(async (res) => {
const body = res.status === 204 ? null : await res.json(); const body = res.status === 204 ? null : await res.json();
@ -221,13 +221,13 @@ export const apiGet = ((
export const apiWithDialog = (( export const apiWithDialog = ((
endpoint: string, endpoint: string,
data: Record<string, any> = {}, data: Record<string, any> = {},
token?: string | null | undefined, token?: string | null | undefined
) => { ) => {
const promise = api(endpoint, data, token); const promise = api(endpoint, data, token);
promiseDialog(promise, null, (err) => { promiseDialog(promise, null, (err) => {
alert({ alert({
type: "error", type: "error",
text: err.message + "\n" + (err as any).id, text: err.message + "\n" + (err as any).id
}); });
}); });
@ -238,7 +238,7 @@ export function promiseDialog<T extends Promise<any>>(
promise: T, promise: T,
onSuccess?: ((res: any) => void) | null, onSuccess?: ((res: any) => void) | null,
onFailure?: ((err: Error) => void) | null, onFailure?: ((err: Error) => void) | null,
text?: string, text?: string
): T { ): T {
const showing = ref(true); const showing = ref(true);
const success = ref(false); const success = ref(false);
@ -262,7 +262,7 @@ export function promiseDialog<T extends Promise<any>>(
} else { } else {
alert({ alert({
type: "error", type: "error",
text: err, text: err
}); });
} }
}); });
@ -273,10 +273,10 @@ export function promiseDialog<T extends Promise<any>>(
{ {
success: success, success: success,
showing: showing, showing: showing,
text: text, text: text
}, },
{}, {},
"closed", "closed"
); );
return promise; return promise;
@ -294,16 +294,18 @@ export const popups = ref([]) as Ref<
const zIndexes = { const zIndexes = {
low: 1000000, low: 1000000,
middle: 2000000, middle: 2000000,
high: 3000000, high: 3000000
}; };
export function claimZIndex( export function claimZIndex(
priority: "low" | "middle" | "high" = "low", priority: "low" | "middle" | "high" = "low"
): number { ): number {
zIndexes[priority] += 100; zIndexes[priority] += 100;
return zIndexes[priority]; return zIndexes[priority];
} }
let uniqueId = 0; let uniqueId = 0;
export function getUniqueId(): string { export function getUniqueId(): string {
return uniqueId++ + ""; return uniqueId++ + "";
} }
@ -350,7 +352,7 @@ export async function popup<C extends Component>(
component: C, component: C,
props: ComponentPropsRef<C>, props: ComponentPropsRef<C>,
events: Partial<ComponentEmit<C>> = {} as ComponentEmit<C>, events: Partial<ComponentEmit<C>> = {} as ComponentEmit<C>,
disposeEvent?: keyof ComponentEmit<C>, disposeEvent?: keyof ComponentEmit<C>
) { ) {
markRaw(component); markRaw(component);
@ -367,16 +369,16 @@ export async function popup<C extends Component>(
events: disposeEvent events: disposeEvent
? { ? {
...events, ...events,
[disposeEvent]: dispose, [disposeEvent]: dispose
} }
: events, : events,
id, id
}; };
popups.value.push(state); popups.value.push(state);
return { return {
dispose, dispose
}; };
} }
@ -385,13 +387,13 @@ export function pageWindow(path: string) {
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkPageWindow.vue"), loader: () => import("@/components/MkPageWindow.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
initialPath: path, initialPath: path
}, },
{}, {},
"closed", "closed"
); );
} }
@ -400,13 +402,13 @@ export function modalPageWindow(path: string) {
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkModalPageWindow.vue"), loader: () => import("@/components/MkModalPageWindow.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
initialPath: path, initialPath: path
}, },
{}, {},
"closed", "closed"
); );
} }
@ -414,10 +416,10 @@ export function toast(message: string) {
popup( popup(
MkToast, MkToast,
{ {
message, message
}, },
{}, {},
"closed", "closed"
); );
} }
@ -436,9 +438,9 @@ export function alert(props: {
{ {
done: (result) => { done: (result) => {
resolve(); resolve();
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -455,14 +457,14 @@ export function confirm(props: {
MkDialog, MkDialog,
{ {
...props, ...props,
showCancelButton: true, showCancelButton: true
}, },
{ {
done: (result) => { done: (result) => {
resolve(result ? result : { canceled: true }); resolve(result ? result : { canceled: true });
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -477,19 +479,19 @@ export function yesno(props: {
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkDialog.vue"), loader: () => import("@/components/MkDialog.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
...props, ...props,
showCancelButton: true, showCancelButton: true,
isYesNo: true, isYesNo: true
}, },
{ {
done: (result) => { done: (result) => {
resolve(result ? result : { canceled: true }); resolve(result ? result : { canceled: true });
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -522,15 +524,15 @@ export function inputText(props: {
autocomplete: props.autocomplete, autocomplete: props.autocomplete,
default: props.default, default: props.default,
minLength: props.minLength, minLength: props.minLength,
maxLength: props.maxLength, maxLength: props.maxLength
}, }
}, },
{ {
done: (result) => { done: (result) => {
resolve(result ? result : { canceled: true }); resolve(result ? result : { canceled: true });
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -552,7 +554,7 @@ export function inputParagraph(props: {
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkDialog.vue"), loader: () => import("@/components/MkDialog.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
title: props.title, title: props.title,
@ -560,15 +562,15 @@ export function inputParagraph(props: {
input: { input: {
type: "paragraph", type: "paragraph",
placeholder: props.placeholder, placeholder: props.placeholder,
default: props.default, default: props.default
}, }
}, },
{ {
done: (result) => { done: (result) => {
resolve(result ? result : { canceled: true }); resolve(result ? result : { canceled: true });
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -591,7 +593,7 @@ export function inputNumber(props: {
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkDialog.vue"), loader: () => import("@/components/MkDialog.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
title: props.title, title: props.title,
@ -600,15 +602,15 @@ export function inputNumber(props: {
type: "number", type: "number",
placeholder: props.placeholder, placeholder: props.placeholder,
autocomplete: props.autocomplete, autocomplete: props.autocomplete,
default: props.default, default: props.default
}, }
}, },
{ {
done: (result) => { done: (result) => {
resolve(result ? result : { canceled: true }); resolve(result ? result : { canceled: true });
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -634,8 +636,8 @@ export function inputDate(props: {
input: { input: {
type: "date", type: "date",
placeholder: props.placeholder, placeholder: props.placeholder,
default: props.default, default: props.default
}, }
}, },
{ {
done: (result) => { done: (result) => {
@ -643,13 +645,13 @@ export function inputDate(props: {
result result
? { ? {
result: new Date(result.result), result: new Date(result.result),
canceled: false, canceled: false
} }
: { canceled: true }, : { canceled: true }
); );
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -675,7 +677,7 @@ export function select<C = any>(
}[]; }[];
}[]; }[];
} }
), )
): Promise< ): Promise<
| { canceled: true; result: undefined } | { canceled: true; result: undefined }
| { | {
@ -692,15 +694,15 @@ export function select<C = any>(
select: { select: {
items: props.items, items: props.items,
groupedItems: props.groupedItems, groupedItems: props.groupedItems,
default: props.default, default: props.default
}, }
}, },
{ {
done: (result) => { done: (result) => {
resolve(result ? result : { canceled: true }); resolve(result ? result : { canceled: true });
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -715,12 +717,12 @@ export function success(): Promise<void> {
MkWaitingDialog, MkWaitingDialog,
{ {
success: true, success: true,
showing: showing, showing: showing
}, },
{ {
done: () => resolve(), done: () => resolve()
}, },
"closed", "closed"
); );
}); });
} }
@ -732,12 +734,12 @@ export function waiting(): Promise<void> {
MkWaitingDialog, MkWaitingDialog,
{ {
success: false, success: false,
showing: showing, showing: showing
}, },
{ {
done: () => resolve(), done: () => resolve()
}, },
"closed", "closed"
); );
}); });
} }
@ -748,34 +750,34 @@ export function form(title, form) {
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkFormDialog.vue"), loader: () => import("@/components/MkFormDialog.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ title, form }, { title, form },
{ {
done: (result) => { done: (result) => {
resolve(result); resolve(result);
}
}, },
}, "closed"
"closed",
); );
}); });
} }
export async function selectUser() { export async function selectUser(): Promise<Misskey.entities.User> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
popup( popup(
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkUserSelectDialog.vue"), loader: () => import("@/components/MkUserSelectDialog.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{}, {},
{ {
ok: (user) => { ok: (user) => {
resolve(user); resolve(user);
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -786,15 +788,15 @@ export async function selectInstance(): Promise<Misskey.entities.Instance> {
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkInstanceSelectDialog.vue"), loader: () => import("@/components/MkInstanceSelectDialog.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{}, {},
{ {
ok: (instance) => { ok: (instance) => {
resolve(instance); resolve(instance);
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -805,20 +807,20 @@ export async function selectDriveFile(multiple: boolean) {
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkDriveSelectDialog.vue"), loader: () => import("@/components/MkDriveSelectDialog.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
type: "file", type: "file",
multiple, multiple
}, },
{ {
done: (files) => { done: (files) => {
if (files) { if (files) {
resolve(multiple ? files : files[0]); resolve(multiple ? files : files[0]);
} }
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -829,20 +831,20 @@ export async function selectDriveFolder(multiple: boolean) {
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkDriveSelectDialog.vue"), loader: () => import("@/components/MkDriveSelectDialog.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
type: "folder", type: "folder",
multiple, multiple
}, },
{ {
done: (folders) => { done: (folders) => {
if (folders) { if (folders) {
resolve(multiple ? folders : folders[0]); resolve(multiple ? folders : folders[0]);
} }
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -853,18 +855,18 @@ export async function pickEmoji(src: HTMLElement | null, opts) {
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkEmojiPickerDialog.vue"), loader: () => import("@/components/MkEmojiPickerDialog.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
src, src,
...opts, ...opts
}, },
{ {
done: (emoji: types.Reaction) => { done: (emoji: types.Reaction) => {
resolve(magReactionToLegacy(emoji)); resolve(magReactionToLegacy(emoji));
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -873,25 +875,25 @@ export async function cropImage(
image: Misskey.entities.DriveFile, image: Misskey.entities.DriveFile,
options: { options: {
aspectRatio: number; aspectRatio: number;
}, }
): Promise<Misskey.entities.DriveFile> { ): Promise<Misskey.entities.DriveFile> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
popup( popup(
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkCropperDialog.vue"), loader: () => import("@/components/MkCropperDialog.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
file: image, file: image,
aspectRatio: options.aspectRatio, aspectRatio: options.aspectRatio
}, },
{ {
ok: (x) => { ok: (x) => {
resolve(x); resolve(x);
}
}, },
}, "closed"
"closed",
); );
}); });
} }
@ -904,10 +906,11 @@ type AwaitType<T> =
: T; : T;
let openingEmojiPicker: AwaitType<ReturnType<typeof popup>> | null = null; let openingEmojiPicker: AwaitType<ReturnType<typeof popup>> | null = null;
let activeTextarea: HTMLTextAreaElement | HTMLInputElement | null = null; let activeTextarea: HTMLTextAreaElement | HTMLInputElement | null = null;
export async function openEmojiPicker( export async function openEmojiPicker(
src?: HTMLElement, src?: HTMLElement,
opts, opts,
initialTextarea: typeof activeTextarea, initialTextarea: typeof activeTextarea
) { ) {
if (openingEmojiPicker) return; if (openingEmojiPicker) return;
@ -923,11 +926,11 @@ export async function openEmojiPicker(
const observer = new MutationObserver((records) => { const observer = new MutationObserver((records) => {
for (const record of records) { for (const record of records) {
for (const node of Array.from(record.addedNodes).filter( for (const node of Array.from(record.addedNodes).filter(
(node) => node instanceof HTMLElement, (node) => node instanceof HTMLElement
) as HTMLElement[]) { ) as HTMLElement[]) {
const textareas = node.querySelectorAll("textarea, input"); const textareas = node.querySelectorAll("textarea, input");
for (const textarea of Array.from(textareas).filter( for (const textarea of Array.from(textareas).filter(
(textarea) => textarea.dataset.preventEmojiInsert == null, (textarea) => textarea.dataset.preventEmojiInsert == null
)) { )) {
if (document.activeElement === textarea) if (document.activeElement === textarea)
activeTextarea = textarea; activeTextarea = textarea;
@ -943,18 +946,18 @@ export async function openEmojiPicker(
childList: true, childList: true,
subtree: true, subtree: true,
attributes: false, attributes: false,
characterData: false, characterData: false
}); });
openingEmojiPicker = await popup( openingEmojiPicker = await popup(
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkEmojiPickerDialog.vue"), loader: () => import("@/components/MkEmojiPickerDialog.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
src, src,
...opts, ...opts
}, },
{ {
chosen: (emoji: types.Reaction) => { chosen: (emoji: types.Reaction) => {
@ -967,8 +970,8 @@ export async function openEmojiPicker(
openingEmojiPicker!.dispose(); openingEmojiPicker!.dispose();
openingEmojiPicker = null; openingEmojiPicker = null;
observer.disconnect(); observer.disconnect();
}, }
}, }
); );
} }
@ -980,7 +983,7 @@ export function popupMenu(
width?: number; width?: number;
viaKeyboard?: boolean; viaKeyboard?: boolean;
noReturnFocus?: boolean; noReturnFocus?: boolean;
}, }
) { ) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let dispose; let dispose;
@ -988,7 +991,7 @@ export function popupMenu(
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkPopupMenu.vue"), loader: () => import("@/components/MkPopupMenu.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
items, items,
@ -996,14 +999,14 @@ export function popupMenu(
width: options?.width, width: options?.width,
align: options?.align, align: options?.align,
viaKeyboard: options?.viaKeyboard, viaKeyboard: options?.viaKeyboard,
noReturnFocus: options?.noReturnFocus, noReturnFocus: options?.noReturnFocus
}, },
{ {
closed: () => { closed: () => {
resolve(); resolve();
dispose(); dispose();
}, }
}, }
).then((res) => { ).then((res) => {
dispose = res.dispose; dispose = res.dispose;
}); });
@ -1012,7 +1015,7 @@ export function popupMenu(
export function contextMenu( export function contextMenu(
items: MenuItem[] | Ref<MenuItem[]>, items: MenuItem[] | Ref<MenuItem[]>,
ev: MouseEvent, ev: MouseEvent
) { ) {
ev.preventDefault(); ev.preventDefault();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -1021,18 +1024,18 @@ export function contextMenu(
defineAsyncComponent({ defineAsyncComponent({
loader: () => import("@/components/MkContextMenu.vue"), loader: () => import("@/components/MkContextMenu.vue"),
loadingComponent: MkWaitingDialog, loadingComponent: MkWaitingDialog,
delay: 1000, delay: 1000
}), }),
{ {
items, items,
ev, ev
}, },
{ {
closed: () => { closed: () => {
resolve(); resolve();
dispose(); dispose();
}, }
}, }
).then((res) => { ).then((res) => {
dispose = res.dispose; dispose = res.dispose;
}); });
@ -1051,7 +1054,7 @@ export function post(props: Record<string, any> = {}) {
closed: () => { closed: () => {
resolve(); resolve();
dispose(); dispose();
}, }
}).then((res) => { }).then((res) => {
dispose = res.dispose; dispose = res.dispose;
}); });

View File

@ -137,10 +137,8 @@
<template #caption> <template #caption>
<MkLink <MkLink
url="https://codeberg.org/calckey/calckey/activity" url="https://codeberg.org/calckey/calckey/activity"
>{{ >{{ i18n.ts._aboutMisskey.allContributors }}
i18n.ts._aboutMisskey.allContributors </MkLink>
}}</MkLink
>
</template> </template>
</FormSection> </FormSection>
</div> </div>
@ -150,11 +148,11 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onBeforeUnmount, ref, computed } from "vue"; import { computed, nextTick, onBeforeUnmount, ref } from "vue";
import { version } from "@/config"; import { version } from "@/config";
import FormLink from "@/components/form/link.vue"; import FormLink from "@/components/form/link.vue";
import FormSection from "@/components/form/section.vue"; import FormSection from "@/components/form/section.vue";
import MkLink from "@/components/MkLink.vue"; import MkLink from "@/components/MagLink.vue";
import { physics } from "@/scripts/physics"; import { physics } from "@/scripts/physics";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";

View File

@ -1,11 +1,12 @@
<template> <template>
<MkStickyContainer> <MkStickyContainer>
<template #header <template #header>
><MkPageHeader <MkPageHeader
v-model:tab="tab" v-model:tab="tab"
:actions="headerActions" :actions="headerActions"
:tabs="headerTabs" :tabs="headerTabs"
/></template> />
</template>
<MkSpacer <MkSpacer
v-if="instance" v-if="instance"
:content-max="600" :content-max="600"
@ -64,9 +65,9 @@
> >
</MkKeyValue> </MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0"> <MkKeyValue oneline style="margin: 1em 0">
<template #key>{{ <template #key
i18n.ts.administrator >{{ i18n.ts.administrator }}
}}</template> </template>
<template #value <template #value
>{{ >{{
instance.maintainerName || instance.maintainerName ||
@ -75,14 +76,14 @@
({{ ({{
instance.maintainerEmail || instance.maintainerEmail ||
`(${i18n.ts.unknown})` `(${i18n.ts.unknown})`
}})</template }})
> </template>
</MkKeyValue> </MkKeyValue>
<MkKeyValue> <MkKeyValue>
<template #key>{{ i18n.ts.description }}</template> <template #key>{{ i18n.ts.description }}</template>
<template #value>{{ <template #value
instance.description >{{ instance.description }}
}}</template> </template>
</MkKeyValue> </MkKeyValue>
<FormSection v-if="iAmAdmin"> <FormSection v-if="iAmAdmin">
@ -92,84 +93,86 @@
v-model="suspended" v-model="suspended"
class="_formBlock" class="_formBlock"
@update:modelValue="toggleSuspend" @update:modelValue="toggleSuspend"
>{{ >{{ i18n.ts.stopActivityDelivery }}
i18n.ts.stopActivityDelivery </FormSwitch>
}}</FormSwitch
>
<FormSwitch <FormSwitch
v-model="isBlocked" v-model="isBlocked"
class="_formBlock" class="_formBlock"
@update:modelValue="toggleBlock" @update:modelValue="toggleBlock"
>{{ i18n.ts.blockThisInstance }}</FormSwitch >{{ i18n.ts.blockThisInstance }}
> </FormSwitch>
<FormSwitch <FormSwitch
v-model="isSilenced" v-model="isSilenced"
class="_formBlock" class="_formBlock"
@update:modelValue="toggleSilence" @update:modelValue="toggleSilence"
>{{ >{{ i18n.ts.silenceThisInstance }}
i18n.ts.silenceThisInstance </FormSwitch>
}}</FormSwitch
>
</FormSuspense> </FormSuspense>
<MkButton @click="refreshMetadata" <MkButton @click="refreshMetadata"
><i ><i
class="ph-arrows-clockwise ph-bold ph-lg" class="ph-arrows-clockwise ph-bold ph-lg"
></i> ></i>
Refresh metadata</MkButton Refresh metadata
> </MkButton>
</FormSection> </FormSection>
<FormSection> <FormSection>
<MkKeyValue oneline style="margin: 1em 0"> <MkKeyValue oneline style="margin: 1em 0">
<template #key>{{ <template #key
i18n.ts.registeredAt >{{ i18n.ts.registeredAt }}
}}</template> </template>
<template #value <template #value>
><MkTime <MkTime
mode="detail" mode="detail"
:time="instance.caughtAt" :time="instance.caughtAt"
/></template> />
</template>
</MkKeyValue> </MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0"> <MkKeyValue oneline style="margin: 1em 0">
<template #key>{{ <template #key
i18n.ts.updatedAt >{{ i18n.ts.updatedAt }}
}}</template> </template>
<template #value <template #value>
><MkTime <MkTime
mode="detail" mode="detail"
:time="instance.infoUpdatedAt" :time="instance.infoUpdatedAt"
/></template> />
</template>
</MkKeyValue> </MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0"> <MkKeyValue oneline style="margin: 1em 0">
<template #key>{{ <template #key
i18n.ts.latestRequestSentAt >{{ i18n.ts.latestRequestSentAt }}
}}</template> </template>
<template #value <template #value>
><MkTime <MkTime
v-if="instance.latestRequestSentAt" v-if="instance.latestRequestSentAt"
:time="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"
/><span v-else>N/A</span></template />
<span v-else>N/A</span></template
> >
</MkKeyValue> </MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0"> <MkKeyValue oneline style="margin: 1em 0">
<template #key>{{ <template #key
i18n.ts.latestStatus >{{ i18n.ts.latestStatus }}
}}</template> </template>
<template #value>{{ <template #value
>{{
instance.latestStatus instance.latestStatus
? instance.latestStatus ? instance.latestStatus
: "N/A" : "N/A"
}}</template> }}
</template>
</MkKeyValue> </MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0"> <MkKeyValue oneline style="margin: 1em 0">
<template #key>{{ <template #key
i18n.ts.latestRequestReceivedAt >{{ i18n.ts.latestRequestReceivedAt }}
}}</template> </template>
<template #value <template #value>
><MkTime <MkTime
v-if="instance.latestRequestReceivedAt" v-if="instance.latestRequestReceivedAt"
:time="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"
/><span v-else>N/A</span></template />
<span v-else>N/A</span></template
> >
</MkKeyValue> </MkKeyValue>
</FormSection> </FormSection>
@ -177,15 +180,15 @@
<FormSection> <FormSection>
<MkKeyValue oneline style="margin: 1em 0"> <MkKeyValue oneline style="margin: 1em 0">
<template #key>Following (Pub)</template> <template #key>Following (Pub)</template>
<template #value>{{ <template #value
number(instance.followingCount) >{{ number(instance.followingCount) }}
}}</template> </template>
</MkKeyValue> </MkKeyValue>
<MkKeyValue oneline style="margin: 1em 0"> <MkKeyValue oneline style="margin: 1em 0">
<template #key>Followers (Sub)</template> <template #key>Followers (Sub)</template>
<template #value>{{ <template #value
number(instance.followersCount) >{{ number(instance.followersCount) }}
}}</template> </template>
</MkKeyValue> </MkKeyValue>
</FormSection> </FormSection>
@ -195,32 +198,32 @@
:to="`https://${host}/.well-known/host-meta`" :to="`https://${host}/.well-known/host-meta`"
external external
style="margin-bottom: 8px" style="margin-bottom: 8px"
>host-meta</FormLink >host-meta
> </FormLink>
<FormLink <FormLink
:to="`https://${host}/.well-known/host-meta.json`" :to="`https://${host}/.well-known/host-meta.json`"
external external
style="margin-bottom: 8px" style="margin-bottom: 8px"
>host-meta.json</FormLink >host-meta.json
> </FormLink>
<FormLink <FormLink
:to="`https://${host}/.well-known/nodeinfo`" :to="`https://${host}/.well-known/nodeinfo`"
external external
style="margin-bottom: 8px" style="margin-bottom: 8px"
>nodeinfo</FormLink >nodeinfo
> </FormLink>
<FormLink <FormLink
:to="`https://${host}/robots.txt`" :to="`https://${host}/robots.txt`"
external external
style="margin-bottom: 8px" style="margin-bottom: 8px"
>robots.txt</FormLink >robots.txt
> </FormLink>
<FormLink <FormLink
:to="`https://${host}/manifest.json`" :to="`https://${host}/manifest.json`"
external external
style="margin-bottom: 8px" style="margin-bottom: 8px"
>manifest.json</FormLink >manifest.json
> </FormLink>
</FormSection> </FormSection>
</div> </div>
</swiper-slide> </swiper-slide>
@ -316,7 +319,7 @@
:key="user.id" :key="user.id"
v-tooltip.mfm=" v-tooltip.mfm="
`Last posted: ${new Date( `Last posted: ${new Date(
user.updatedAt, user.updatedAt
).toLocaleString()}` ).toLocaleString()}`
" "
class="user" class="user"
@ -338,14 +341,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch, ref, computed } from "vue"; import { computed, ref, watch } from "vue";
import { Virtual } from "swiper"; import { Virtual } from "swiper";
import { Swiper, SwiperSlide } from "swiper/vue"; import { Swiper, SwiperSlide } from "swiper/vue";
import type * as calckey from "calckey-js"; import type * as calckey from "calckey-js";
import MkChart from "@/components/MkChart.vue"; import MkChart from "@/components/MkChart.vue";
import MkObjectView from "@/components/MkObjectView.vue"; import MkObjectView from "@/components/MkObjectView.vue";
import FormLink from "@/components/form/link.vue"; import FormLink from "@/components/form/link.vue";
import MkLink from "@/components/MkLink.vue"; import MkLink from "@/components/MagLink.vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
import FormSection from "@/components/form/section.vue"; import FormSection from "@/components/form/section.vue";
import MkKeyValue from "@/components/MkKeyValue.vue"; import MkKeyValue from "@/components/MkKeyValue.vue";
@ -425,7 +428,7 @@ async function toggleBlock() {
blockedHosts = meta.value.blockedHosts.concat([instance.value.host]); blockedHosts = meta.value.blockedHosts.concat([instance.value.host]);
} else { } else {
blockedHosts = meta.value.blockedHosts.filter( blockedHosts = meta.value.blockedHosts.filter(
(x) => x !== instance.value!.host, (x) => x !== instance.value!.host
); );
} }
await os.api("admin/update-meta", { await os.api("admin/update-meta", {
@ -443,7 +446,7 @@ async function toggleSilence() {
silencedHosts = meta.value.silencedHosts.concat([instance.value.host]); silencedHosts = meta.value.silencedHosts.concat([instance.value.host]);
} else { } else {
silencedHosts = meta.value.silencedHosts.filter( silencedHosts = meta.value.silencedHosts.filter(
(x) => x !== instance.value!.host, (x) => x !== instance.value!.host
); );
} }
await os.api("admin/update-meta", { await os.api("admin/update-meta", {
@ -503,7 +506,7 @@ if (iAmAdmin) {
key: "raw", key: "raw",
title: "Raw", title: "Raw",
icon: "ph-code ph-bold ph-lg", icon: "ph-code ph-bold ph-lg",
}, }
); );
} }

View File

@ -9,8 +9,8 @@
<I18n :src="i18n.ts.i18nInfo" tag="span"> <I18n :src="i18n.ts.i18nInfo" tag="span">
<template #link> <template #link>
<MkLink url="https://hosted.weblate.org/engage/calckey/" <MkLink url="https://hosted.weblate.org/engage/calckey/"
>Weblate</MkLink >Weblate
> </MkLink>
</template> </template>
</I18n> </I18n>
</template> </template>
@ -34,33 +34,33 @@
<FormSection> <FormSection>
<template #label>{{ i18n.ts.behavior }}</template> <template #label>{{ i18n.ts.behavior }}</template>
<FormSwitch v-model="imageNewTab" class="_formBlock">{{ <FormSwitch v-model="imageNewTab" class="_formBlock"
i18n.ts.openImageInNewTab >{{ i18n.ts.openImageInNewTab }}
}}</FormSwitch> </FormSwitch>
<FormSwitch v-model="enableInfiniteScroll" class="_formBlock">{{ <FormSwitch v-model="enableInfiniteScroll" class="_formBlock"
i18n.ts.enableInfiniteScroll >{{ i18n.ts.enableInfiniteScroll }}
}}</FormSwitch> </FormSwitch>
<FormSwitch <FormSwitch
v-model="useReactionPickerForContextMenu" v-model="useReactionPickerForContextMenu"
class="_formBlock" class="_formBlock"
>{{ i18n.ts.useReactionPickerForContextMenu }}</FormSwitch >{{ i18n.ts.useReactionPickerForContextMenu }}
> </FormSwitch>
<FormSwitch v-model="swipeOnDesktop" class="_formBlock">{{ <FormSwitch v-model="swipeOnDesktop" class="_formBlock"
i18n.ts.swipeOnDesktop >{{ i18n.ts.swipeOnDesktop }}
}}</FormSwitch> </FormSwitch>
<FormSwitch v-model="enterSendsMessage" class="_formBlock">{{ <FormSwitch v-model="enterSendsMessage" class="_formBlock"
i18n.ts.enterSendsMessage >{{ i18n.ts.enterSendsMessage }}
}}</FormSwitch> </FormSwitch>
<FormSwitch v-model="disablePagesScript" class="_formBlock">{{ <FormSwitch v-model="disablePagesScript" class="_formBlock"
i18n.ts.disablePagesScript >{{ i18n.ts.disablePagesScript }}
}}</FormSwitch> </FormSwitch>
<FormSwitch v-model="showTimelineReplies" class="_formBlock" <FormSwitch v-model="showTimelineReplies" class="_formBlock"
>{{ i18n.ts.flagShowTimelineReplies >{{ i18n.ts.flagShowTimelineReplies }}
}}<template #caption <template #caption
>{{ i18n.ts.flagShowTimelineRepliesDescription }} >{{ i18n.ts.flagShowTimelineRepliesDescription }}
{{ i18n.ts.reflectMayTakeTime }}</template {{ i18n.ts.reflectMayTakeTime }}
></FormSwitch </template>
> </FormSwitch>
<FormSelect v-model="serverDisconnectedBehavior" class="_formBlock"> <FormSelect v-model="serverDisconnectedBehavior" class="_formBlock">
<template #label>{{ i18n.ts.whenServerDisconnected }}</template> <template #label>{{ i18n.ts.whenServerDisconnected }}</template>
@ -82,16 +82,16 @@
<FormSection> <FormSection>
<template #label>{{ i18n.ts.accessibility }}</template> <template #label>{{ i18n.ts.accessibility }}</template>
<FormSwitch v-model="expandOnNoteClick" class="_formBlock" <FormSwitch v-model="expandOnNoteClick" class="_formBlock"
>{{ i18n.ts.expandOnNoteClick >{{ i18n.ts.expandOnNoteClick }}
}}<template #caption>{{ <template #caption
i18n.ts.expandOnNoteClickDesc >{{ i18n.ts.expandOnNoteClickDesc }}
}}</template> </template>
</FormSwitch> </FormSwitch>
<FormSwitch v-model="advancedMfm" class="_formBlock"> <FormSwitch v-model="advancedMfm" class="_formBlock">
{{ i18n.ts._mfm.advanced {{ i18n.ts._mfm.advanced }}
}}<template #caption>{{ <template #caption
i18n.ts._mfm.advancedDescription >{{ i18n.ts._mfm.advancedDescription }}
}}</template> </template>
</FormSwitch> </FormSwitch>
<FormSwitch v-model="autoplayMfm" class="_formBlock"> <FormSwitch v-model="autoplayMfm" class="_formBlock">
{{ i18n.ts._mfm.alwaysPlay }} {{ i18n.ts._mfm.alwaysPlay }}
@ -103,14 +103,14 @@
{{ i18n.ts._mfm.warn }} {{ i18n.ts._mfm.warn }}
</template> </template>
</FormSwitch> </FormSwitch>
<FormSwitch v-model="reduceAnimation" class="_formBlock">{{ <FormSwitch v-model="reduceAnimation" class="_formBlock"
i18n.ts.reduceUiAnimation >{{ i18n.ts.reduceUiAnimation }}
}}</FormSwitch> </FormSwitch>
<FormSwitch <FormSwitch
v-model="disableShowingAnimatedImages" v-model="disableShowingAnimatedImages"
class="_formBlock" class="_formBlock"
>{{ i18n.ts.disableShowingAnimatedImages }}</FormSwitch >{{ i18n.ts.disableShowingAnimatedImages }}
> </FormSwitch>
<FormRadios v-model="fontSize" class="_formBlock"> <FormRadios v-model="fontSize" class="_formBlock">
<template #label>{{ i18n.ts.fontSize }}</template> <template #label>{{ i18n.ts.fontSize }}</template>
<option :value="null"> <option :value="null">
@ -146,44 +146,44 @@
<FormSection> <FormSection>
<template #label>{{ i18n.ts.appearance }}</template> <template #label>{{ i18n.ts.appearance }}</template>
<FormSwitch v-model="useBlurEffect" class="_formBlock">{{ <FormSwitch v-model="useBlurEffect" class="_formBlock"
i18n.ts.useBlurEffect >{{ i18n.ts.useBlurEffect }}
}}</FormSwitch> </FormSwitch>
<FormSwitch v-model="useBlurEffectForModal" class="_formBlock">{{ <FormSwitch v-model="useBlurEffectForModal" class="_formBlock"
i18n.ts.useBlurEffectForModal >{{ i18n.ts.useBlurEffectForModal }}
}}</FormSwitch> </FormSwitch>
<FormSwitch <FormSwitch
v-model="showGapBetweenNotesInTimeline" v-model="showGapBetweenNotesInTimeline"
class="_formBlock" class="_formBlock"
>{{ i18n.ts.showGapBetweenNotesInTimeline }}</FormSwitch >{{ i18n.ts.showGapBetweenNotesInTimeline }}
> </FormSwitch>
<FormSwitch v-model="loadRawImages" class="_formBlock">{{ <FormSwitch v-model="loadRawImages" class="_formBlock"
i18n.ts.loadRawImages >{{ i18n.ts.loadRawImages }}
}}</FormSwitch> </FormSwitch>
<FormSwitch v-model="squareAvatars" class="_formBlock">{{ <FormSwitch v-model="squareAvatars" class="_formBlock"
i18n.ts.squareAvatars >{{ i18n.ts.squareAvatars }}
}}</FormSwitch> </FormSwitch>
<FormSwitch v-model="seperateRenoteQuote" class="_formBlock">{{ <FormSwitch v-model="seperateRenoteQuote" class="_formBlock"
i18n.ts.seperateRenoteQuote >{{ i18n.ts.seperateRenoteQuote }}
}}</FormSwitch> </FormSwitch>
<FormSwitch v-model="useSystemFont" class="_formBlock">{{ <FormSwitch v-model="useSystemFont" class="_formBlock"
i18n.ts.useSystemFont >{{ i18n.ts.useSystemFont }}
}}</FormSwitch> </FormSwitch>
<FormSwitch v-model="useOsNativeEmojis" class="_formBlock"> <FormSwitch v-model="useOsNativeEmojis" class="_formBlock">
{{ i18n.ts.useOsNativeEmojis }} {{ i18n.ts.useOsNativeEmojis }}
<div> <div>
<Mfm :key="useOsNativeEmojis" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪" /> <Mfm :key="useOsNativeEmojis" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪" />
</div> </div>
</FormSwitch> </FormSwitch>
<FormSwitch v-model="disableDrawer" class="_formBlock">{{ <FormSwitch v-model="disableDrawer" class="_formBlock"
i18n.ts.disableDrawer >{{ i18n.ts.disableDrawer }}
}}</FormSwitch> </FormSwitch>
<FormSwitch v-model="showUpdates" class="_formBlock">{{ <FormSwitch v-model="showUpdates" class="_formBlock"
i18n.ts.showUpdates >{{ i18n.ts.showUpdates }}
}}</FormSwitch> </FormSwitch>
<FormSwitch v-model="showFixedPostForm" class="_formBlock">{{ <FormSwitch v-model="showFixedPostForm" class="_formBlock"
i18n.ts.showFixedPostForm >{{ i18n.ts.showFixedPostForm }}
}}</FormSwitch> </FormSwitch>
<FormSelect v-model="instanceTicker" class="_formBlock"> <FormSelect v-model="instanceTicker" class="_formBlock">
<template #label>{{ i18n.ts.instanceTicker }}</template> <template #label>{{ i18n.ts.instanceTicker }}</template>
<option value="none">{{ i18n.ts._instanceTicker.none }}</option> <option value="none">{{ i18n.ts._instanceTicker.none }}</option>
@ -212,19 +212,19 @@
class="_formBlock" class="_formBlock"
> >
<template #label>{{ i18n.ts.numberOfPageCache }}</template> <template #label>{{ i18n.ts.numberOfPageCache }}</template>
<template #caption>{{ <template #caption
i18n.ts.numberOfPageCacheDescription >{{ i18n.ts.numberOfPageCacheDescription }}
}}</template> </template>
</FormRange> </FormRange>
<FormLink to="/settings/deck" class="_formBlock">{{ <FormLink to="/settings/deck" class="_formBlock"
i18n.ts.deck >{{ i18n.ts.deck }}
}}</FormLink> </FormLink>
<FormLink to="/settings/custom-katex-macro" class="_formBlock" <FormLink to="/settings/custom-katex-macro" class="_formBlock">
><template #icon><i class="ph-radical ph-bold ph-lg"></i></template <template #icon><i class="ph-radical ph-bold ph-lg"></i></template>
>{{ i18n.ts.customKaTeXMacro }}</FormLink {{ i18n.ts.customKaTeXMacro }}
> </FormLink>
</div> </div>
</template> </template>
@ -236,7 +236,7 @@ import FormRadios from "@/components/form/radios.vue";
import FormRange from "@/components/form/range.vue"; import FormRange from "@/components/form/range.vue";
import FormSection from "@/components/form/section.vue"; import FormSection from "@/components/form/section.vue";
import FormLink from "@/components/form/link.vue"; import FormLink from "@/components/form/link.vue";
import MkLink from "@/components/MkLink.vue"; import MkLink from "@/components/MagLink.vue";
import { langs } from "@/config"; import { langs } from "@/config";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import * as os from "@/os"; import * as os from "@/os";
@ -259,24 +259,24 @@ async function reloadAsk() {
} }
const overridedDeviceKind = computed( const overridedDeviceKind = computed(
defaultStore.makeGetterSetter("overridedDeviceKind"), defaultStore.makeGetterSetter("overridedDeviceKind")
); );
const serverDisconnectedBehavior = computed( const serverDisconnectedBehavior = computed(
defaultStore.makeGetterSetter("serverDisconnectedBehavior"), defaultStore.makeGetterSetter("serverDisconnectedBehavior")
); );
const reduceAnimation = computed( const reduceAnimation = computed(
defaultStore.makeGetterSetter( defaultStore.makeGetterSetter(
"animation", "animation",
(v) => !v, (v) => !v,
(v) => !v, (v) => !v
), )
); );
const useBlurEffectForModal = computed( const useBlurEffectForModal = computed(
defaultStore.makeGetterSetter("useBlurEffectForModal"), defaultStore.makeGetterSetter("useBlurEffectForModal")
); );
const useBlurEffect = computed(defaultStore.makeGetterSetter("useBlurEffect")); const useBlurEffect = computed(defaultStore.makeGetterSetter("useBlurEffect"));
const showGapBetweenNotesInTimeline = computed( const showGapBetweenNotesInTimeline = computed(
defaultStore.makeGetterSetter("showGapBetweenNotesInTimeline"), defaultStore.makeGetterSetter("showGapBetweenNotesInTimeline")
); );
const showAds = computed(defaultStore.makeGetterSetter("showAds")); const showAds = computed(defaultStore.makeGetterSetter("showAds"));
const advancedMfm = computed(defaultStore.makeGetterSetter("advancedMfm")); const advancedMfm = computed(defaultStore.makeGetterSetter("advancedMfm"));
@ -284,53 +284,53 @@ const autoplayMfm = computed(
defaultStore.makeGetterSetter( defaultStore.makeGetterSetter(
"animatedMfm", "animatedMfm",
(v) => !v, (v) => !v,
(v) => !v, (v) => !v
), )
); );
const useOsNativeEmojis = computed( const useOsNativeEmojis = computed(
defaultStore.makeGetterSetter("useOsNativeEmojis"), defaultStore.makeGetterSetter("useOsNativeEmojis")
); );
const disableDrawer = computed(defaultStore.makeGetterSetter("disableDrawer")); const disableDrawer = computed(defaultStore.makeGetterSetter("disableDrawer"));
const disableShowingAnimatedImages = computed( const disableShowingAnimatedImages = computed(
defaultStore.makeGetterSetter("disableShowingAnimatedImages"), defaultStore.makeGetterSetter("disableShowingAnimatedImages")
); );
const loadRawImages = computed(defaultStore.makeGetterSetter("loadRawImages")); const loadRawImages = computed(defaultStore.makeGetterSetter("loadRawImages"));
const imageNewTab = computed(defaultStore.makeGetterSetter("imageNewTab")); const imageNewTab = computed(defaultStore.makeGetterSetter("imageNewTab"));
const nsfw = computed(defaultStore.makeGetterSetter("nsfw")); const nsfw = computed(defaultStore.makeGetterSetter("nsfw"));
const disablePagesScript = computed( const disablePagesScript = computed(
defaultStore.makeGetterSetter("disablePagesScript"), defaultStore.makeGetterSetter("disablePagesScript")
); );
const expandOnNoteClick = computed( const expandOnNoteClick = computed(
defaultStore.makeGetterSetter("expandOnNoteClick"), defaultStore.makeGetterSetter("expandOnNoteClick")
); );
const showFixedPostForm = computed( const showFixedPostForm = computed(
defaultStore.makeGetterSetter("showFixedPostForm"), defaultStore.makeGetterSetter("showFixedPostForm")
); );
const numberOfPageCache = computed( const numberOfPageCache = computed(
defaultStore.makeGetterSetter("numberOfPageCache"), defaultStore.makeGetterSetter("numberOfPageCache")
); );
const instanceTicker = computed( const instanceTicker = computed(
defaultStore.makeGetterSetter("instanceTicker"), defaultStore.makeGetterSetter("instanceTicker")
); );
const enableInfiniteScroll = computed( const enableInfiniteScroll = computed(
defaultStore.makeGetterSetter("enableInfiniteScroll"), defaultStore.makeGetterSetter("enableInfiniteScroll")
); );
const enterSendsMessage = computed( const enterSendsMessage = computed(
defaultStore.makeGetterSetter("enterSendsMessage"), defaultStore.makeGetterSetter("enterSendsMessage")
); );
const useReactionPickerForContextMenu = computed( const useReactionPickerForContextMenu = computed(
defaultStore.makeGetterSetter("useReactionPickerForContextMenu"), defaultStore.makeGetterSetter("useReactionPickerForContextMenu")
); );
const seperateRenoteQuote = computed( const seperateRenoteQuote = computed(
defaultStore.makeGetterSetter("seperateRenoteQuote"), defaultStore.makeGetterSetter("seperateRenoteQuote")
); );
const squareAvatars = computed(defaultStore.makeGetterSetter("squareAvatars")); const squareAvatars = computed(defaultStore.makeGetterSetter("squareAvatars"));
const showUpdates = computed(defaultStore.makeGetterSetter("showUpdates")); const showUpdates = computed(defaultStore.makeGetterSetter("showUpdates"));
const swipeOnDesktop = computed( const swipeOnDesktop = computed(
defaultStore.makeGetterSetter("swipeOnDesktop"), defaultStore.makeGetterSetter("swipeOnDesktop")
); );
const showTimelineReplies = computed( const showTimelineReplies = computed(
defaultStore.makeGetterSetter("showTimelineReplies"), defaultStore.makeGetterSetter("showTimelineReplies")
); );
watch(lang, () => { watch(lang, () => {
@ -374,7 +374,7 @@ watch(
], ],
async () => { async () => {
await reloadAsk(); await reloadAsk();
}, }
); );
const headerActions = computed(() => []); const headerActions = computed(() => []);

View File

@ -8,7 +8,7 @@
> >
<div class="main"> <div class="main">
<div class="profile"> <div class="profile">
<MkMoved <MagMoved
v-if="user.moved_to" v-if="user.moved_to"
:host="user.moved_to.host" :host="user.moved_to.host"
:acct="user.moved_to.username" :acct="user.moved_to.username"
@ -274,10 +274,12 @@
<dd class="value"> <dd class="value">
{{ {{
new Date( new Date(
user.created_at, user.created_at
).toLocaleString() ).toLocaleString()
}} }}
(<MkTime :time="user.created_at" />) (
<MkTime :time="user.created_at" />
)
</dd> </dd>
</dl> </dl>
</div> </div>
@ -337,8 +339,8 @@
<MkInfo <MkInfo
v-else-if="$i && $i.id === user.id" v-else-if="$i && $i.id === user.id"
style="margin: 12px 0" style="margin: 12px 0"
>{{ i18n.ts.userPagePinTip }}</MkInfo >{{ i18n.ts.userPagePinTip }}
> </MkInfo>
<template v-if="narrow"> <template v-if="narrow">
<XPhotos :key="user.id" :user="user" /> <XPhotos :key="user.id" :user="user" />
<XActivity <XActivity
@ -366,11 +368,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { import {
computed,
defineAsyncComponent, defineAsyncComponent,
onMounted, onMounted,
onUnmounted, onUnmounted,
ref, ref,
computed,
} from "vue"; } from "vue";
import calcAge from "s-age"; import calcAge from "s-age";
import cityTimezones from "city-timezones"; import cityTimezones from "city-timezones";
@ -378,7 +380,7 @@ import XUserTimeline from "./index.timeline.vue";
import MkFollowButton from "@/components/MkFollowButton.vue"; import MkFollowButton from "@/components/MkFollowButton.vue";
import MkRemoteCaution from "@/components/MkRemoteCaution.vue"; import MkRemoteCaution from "@/components/MkRemoteCaution.vue";
import MkInfo from "@/components/MkInfo.vue"; import MkInfo from "@/components/MkInfo.vue";
import MkMoved from "@/components/MkMoved.vue"; import MagMoved from "@/components/MagMoved.vue";
import XNote from "@/components/MagNote.vue"; import XNote from "@/components/MagNote.vue";
import { getScrollPosition } from "@/scripts/scroll"; import { getScrollPosition } from "@/scripts/scroll";
import { userPage } from "@/filters/user"; import { userPage } from "@/filters/user";
@ -400,7 +402,7 @@ const props = withDefaults(
defineProps<{ defineProps<{
user: packed.PackUserMaybeAll; user: packed.PackUserMaybeAll;
}>(), }>(),
{}, {}
); );
let parallaxAnimationId = ref<null | number>(null); let parallaxAnimationId = ref<null | number>(null);
@ -425,16 +427,16 @@ const timeForThem = computed(() => {
props.user props.user
.location!.replace( .location!.replace(
/[^A-Za-z0-9ÁĆÉǴÍḰĹḾŃÓṔŔŚÚÝŹáćéǵíḱĺḿńóṕŕśúýź\-'.s].*/, /[^A-Za-z0-9ÁĆÉǴÍḰĹḾŃÓṔŔŚÚÝŹáćéǵíḱĺḿńóṕŕśúýź\-'.s].*/,
"", ""
) )
.trim(), .trim(),
props.user.location!.replace( props.user.location!.replace(
/[^A-Za-zÁĆÉǴÍḰĹḾŃÓṔŔŚÚÝŹáćéǵíḱĺḿńóṕŕśúýź\-'.].*/, /[^A-Za-zÁĆÉǴÍḰĹḾŃÓṔŔŚÚÝŹáćéǵíḱĺḿńóṕŕśúýź\-'.].*/,
"", ""
), ),
props.user.location!.replace( props.user.location!.replace(
/[^A-Za-zÁĆÉǴÍḰĹḾŃÓṔŔŚÚÝŹáćéǵíḱĺḿńóṕŕśúýź].*/, /[^A-Za-zÁĆÉǴÍḰĹḾŃÓṔŔŚÚÝŹáćéǵíḱĺḿńóṕŕśúýź].*/,
"", ""
), ),
]; ];
@ -668,6 +670,7 @@ onUnmounted(() => {
> .nameColumn { > .nameColumn {
display: block; display: block;
> .name { > .name {
margin: 0; margin: 0;
align-content: center; align-content: center;
@ -876,6 +879,7 @@ onUnmounted(() => {
height: auto; height: auto;
border-bottom: 1px solid var(--divider); border-bottom: 1px solid var(--divider);
padding-bottom: 5px; padding-bottom: 5px;
> .actions { > .actions {
position: static; position: static;
} }

View File

@ -1,4 +1,4 @@
import { nextTick, Ref, ref, defineAsyncComponent } from "vue"; import { defineAsyncComponent, nextTick, ref, Ref } from "vue";
import getCaretCoordinates from "textarea-caret"; import getCaretCoordinates from "textarea-caret";
import { toASCII } from "punycode/"; import { toASCII } from "punycode/";
import { popup } from "@/os"; import { popup } from "@/os";
@ -28,7 +28,7 @@ export class Autocomplete {
*/ */
constructor( constructor(
textarea: HTMLInputElement | HTMLTextAreaElement, textarea: HTMLInputElement | HTMLTextAreaElement,
textRef: Ref<string>, textRef: Ref<string>
) { ) {
//#region BIND //#region BIND
this.onInput = this.onInput.bind(this); this.onInput = this.onInput.bind(this);
@ -63,8 +63,9 @@ export class Autocomplete {
* *
*/ */
private onInput() { private onInput() {
const caretPos = this.textarea.selectionStart; const caretPos = this.textarea.selectionStart ?? undefined;
const text = this.text.substr(0, caretPos).split("\n").pop()!;
const text = this.text.substring(0, caretPos).split("\n").pop()!;
const mentionIndex = text.lastIndexOf("@"); const mentionIndex = text.lastIndexOf("@");
const hashtagIndex = text.lastIndexOf("#"); const hashtagIndex = text.lastIndexOf("#");
@ -75,7 +76,7 @@ export class Autocomplete {
mentionIndex, mentionIndex,
hashtagIndex, hashtagIndex,
emojiIndex, emojiIndex,
mfmTagIndex, mfmTagIndex
); );
if (max === -1) { if (max === -1) {
@ -96,7 +97,7 @@ export class Autocomplete {
let opened = false; let opened = false;
if (isMention) { if (isMention) {
const username = text.substr(mentionIndex + 1); const username = text.substring(mentionIndex + 1);
if (username !== "" && username.match(/^[a-zA-Z0-9_]+$/)) { if (username !== "" && username.match(/^[a-zA-Z0-9_]+$/)) {
this.open("user", username); this.open("user", username);
opened = true; opened = true;
@ -107,7 +108,7 @@ export class Autocomplete {
} }
if (isHashtag && !opened) { if (isHashtag && !opened) {
const hashtag = text.substr(hashtagIndex + 1); const hashtag = text.substring(hashtagIndex + 1);
if (!hashtag.includes(" ")) { if (!hashtag.includes(" ")) {
this.open("hashtag", hashtag); this.open("hashtag", hashtag);
opened = true; opened = true;
@ -115,7 +116,7 @@ export class Autocomplete {
} }
if (isEmoji && !opened) { if (isEmoji && !opened) {
const emoji = text.substr(emojiIndex + 1); const emoji = text.substring(emojiIndex + 1);
if (!emoji.includes(" ")) { if (!emoji.includes(" ")) {
this.open("emoji", emoji); this.open("emoji", emoji);
opened = true; opened = true;
@ -123,7 +124,7 @@ export class Autocomplete {
} }
if (isMfmTag && !opened) { if (isMfmTag && !opened) {
const mfmTag = text.substr(mfmTagIndex + 1); const mfmTag = text.substring(mfmTagIndex + 1);
if (!mfmTag.includes(" ")) { if (!mfmTag.includes(" ")) {
this.open("mfmTag", mfmTag.replace("[", "")); this.open("mfmTag", mfmTag.replace("[", ""));
opened = true; opened = true;
@ -149,7 +150,7 @@ export class Autocomplete {
//#region サジェストを表示すべき位置を計算 //#region サジェストを表示すべき位置を計算
const caretPosition = getCaretCoordinates( const caretPosition = getCaretCoordinates(
this.textarea, this.textarea,
this.textarea.selectionStart, this.textarea.selectionStart
); );
const rect = this.textarea.getBoundingClientRect(); const rect = this.textarea.getBoundingClientRect();
@ -171,7 +172,7 @@ export class Autocomplete {
const { dispose } = await popup( const { dispose } = await popup(
defineAsyncComponent( defineAsyncComponent(
() => import("@/components/MkAutocomplete.vue"), () => import("@/components/MkAutocomplete.vue")
), ),
{ {
textarea: this.textarea, textarea: this.textarea,
@ -185,7 +186,7 @@ export class Autocomplete {
done: (res) => { done: (res) => {
this.complete(res); this.complete(res);
}, },
}, }
); );
this.suggestion = { this.suggestion = {
@ -217,14 +218,14 @@ export class Autocomplete {
private complete({ type, value }) { private complete({ type, value }) {
this.close(); this.close();
const caret = this.textarea.selectionStart; const caret = this.textarea.selectionStart ?? undefined;
if (type === "user") { if (type === "user") {
const source = this.text; const source = this.text;
const before = source.substr(0, caret); const before = source.substring(0, caret);
const trimmedBefore = before.substring(0, before.lastIndexOf("@")); const trimmedBefore = before.substring(0, before.lastIndexOf("@"));
const after = source.substr(caret); const after = source.substring(caret);
const acct = const acct =
value.host === null value.host === null
@ -243,9 +244,9 @@ export class Autocomplete {
} else if (type === "hashtag") { } else if (type === "hashtag") {
const source = this.text; const source = this.text;
const before = source.substr(0, caret); const before = source.substring(0, caret);
const trimmedBefore = before.substring(0, before.lastIndexOf("#")); const trimmedBefore = before.substring(0, before.lastIndexOf("#"));
const after = source.substr(caret); const after = source.substring(caret);
// 挿入 // 挿入
this.text = `${trimmedBefore}#${value} ${after}`; this.text = `${trimmedBefore}#${value} ${after}`;
@ -259,9 +260,9 @@ export class Autocomplete {
} else if (type === "emoji") { } else if (type === "emoji") {
const source = this.text; const source = this.text;
const before = source.substr(0, caret); const before = source.substring(0, caret);
const trimmedBefore = before.substring(0, before.lastIndexOf(":")); const trimmedBefore = before.substring(0, before.lastIndexOf(":"));
const after = source.substr(caret); const after = source.substring(caret);
// 挿入 // 挿入
this.text = trimmedBefore + value + after; this.text = trimmedBefore + value + after;
@ -275,9 +276,9 @@ export class Autocomplete {
} else if (type === "mfmTag") { } else if (type === "mfmTag") {
const source = this.text; const source = this.text;
const before = source.substr(0, caret); const before = source.substring(0, caret);
const trimmedBefore = before.substring(0, before.lastIndexOf("$")); const trimmedBefore = before.substring(0, before.lastIndexOf("$"));
const after = source.substr(caret); const after = source.substring(caret);
// 挿入 // 挿入
this.text = `${trimmedBefore}$[${value} ]${after}`; this.text = `${trimmedBefore}$[${value} ]${after}`;

View File

@ -118,13 +118,13 @@ importers:
version: 2.1.1 version: 2.1.1
'@rollup/plugin-alias': '@rollup/plugin-alias':
specifier: ^5.1.0 specifier: ^5.1.0
version: 5.1.0(rollup@4.17.1) version: 5.1.0(rollup@4.17.2)
'@rollup/plugin-json': '@rollup/plugin-json':
specifier: ^6.1.0 specifier: ^6.1.0
version: 6.1.0(rollup@4.17.1) version: 6.1.0(rollup@4.17.2)
'@rollup/pluginutils': '@rollup/pluginutils':
specifier: ^5.1.0 specifier: ^5.1.0
version: 5.1.0(rollup@4.17.1) version: 5.1.0(rollup@4.17.2)
'@types/escape-regexp': '@types/escape-regexp':
specifier: 0.0.1 specifier: 0.0.1
version: 0.0.1 version: 0.0.1
@ -273,8 +273,8 @@ importers:
specifier: 1.0.0 specifier: 1.0.0
version: 1.0.0 version: 1.0.0
rollup: rollup:
specifier: ^4.17.1 specifier: ^4.17.2
version: 4.17.1 version: 4.17.2
s-age: s-age:
specifier: 1.1.2 specifier: 1.1.2
version: 1.1.2 version: 1.1.2
@ -345,8 +345,8 @@ importers:
specifier: ^3.4.26 specifier: ^3.4.26
version: 3.4.26(typescript@5.4.5) version: 3.4.26(typescript@5.4.5)
vue-component-type-helpers: vue-component-type-helpers:
specifier: ^2.0.14 specifier: ^2.0.15
version: 2.0.14 version: 2.0.15
vue-isyourpasswordsafe: vue-isyourpasswordsafe:
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.0.0 version: 2.0.0
@ -426,13 +426,13 @@ packages:
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dev: true dev: true
/@babel/helper-validator-identifier@7.22.20: /@babel/helper-validator-identifier@7.22.5:
resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dev: true dev: true
/@babel/helper-validator-identifier@7.22.5: /@babel/helper-validator-identifier@7.24.5:
resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dev: true dev: true
@ -445,12 +445,12 @@ packages:
js-tokens: 4.0.0 js-tokens: 4.0.0
dev: true dev: true
/@babel/parser@7.24.4: /@babel/parser@7.24.5:
resolution: {integrity: sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==} resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
hasBin: true hasBin: true
dependencies: dependencies:
'@babel/types': 7.24.0 '@babel/types': 7.24.5
dev: true dev: true
/@babel/runtime@7.20.7: /@babel/runtime@7.20.7:
@ -460,19 +460,19 @@ packages:
regenerator-runtime: 0.13.11 regenerator-runtime: 0.13.11
dev: true dev: true
/@babel/runtime@7.24.4: /@babel/runtime@7.24.5:
resolution: {integrity: sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==} resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
regenerator-runtime: 0.14.1 regenerator-runtime: 0.14.1
dev: true dev: true
/@babel/types@7.24.0: /@babel/types@7.24.5:
resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
'@babel/helper-string-parser': 7.24.1 '@babel/helper-string-parser': 7.24.1
'@babel/helper-validator-identifier': 7.22.20 '@babel/helper-validator-identifier': 7.24.5
to-fast-properties: 2.0.0 to-fast-properties: 2.0.0
dev: true dev: true
@ -979,7 +979,7 @@ packages:
resolution: {integrity: sha512-QjrfbItu5Rb2i37GzsKxmrRHfZPTVk3oXSPBnQ2+oACDbQRWGAeB0AsvZw263n1nFouQuff+khOCtRbrc6+k+A==} resolution: {integrity: sha512-QjrfbItu5Rb2i37GzsKxmrRHfZPTVk3oXSPBnQ2+oACDbQRWGAeB0AsvZw263n1nFouQuff+khOCtRbrc6+k+A==}
dev: true dev: true
/@rollup/plugin-alias@5.1.0(rollup@4.17.1): /@rollup/plugin-alias@5.1.0(rollup@4.17.2):
resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==} resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
peerDependencies: peerDependencies:
@ -988,11 +988,11 @@ packages:
rollup: rollup:
optional: true optional: true
dependencies: dependencies:
rollup: 4.17.1 rollup: 4.17.2
slash: 4.0.0 slash: 4.0.0
dev: true dev: true
/@rollup/plugin-json@6.1.0(rollup@4.17.1): /@rollup/plugin-json@6.1.0(rollup@4.17.2):
resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
peerDependencies: peerDependencies:
@ -1001,11 +1001,11 @@ packages:
rollup: rollup:
optional: true optional: true
dependencies: dependencies:
'@rollup/pluginutils': 5.1.0(rollup@4.17.1) '@rollup/pluginutils': 5.1.0(rollup@4.17.2)
rollup: 4.17.1 rollup: 4.17.2
dev: true dev: true
/@rollup/pluginutils@5.1.0(rollup@4.17.1): /@rollup/pluginutils@5.1.0(rollup@4.17.2):
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
peerDependencies: peerDependencies:
@ -1017,131 +1017,131 @@ packages:
'@types/estree': 1.0.5 '@types/estree': 1.0.5
estree-walker: 2.0.2 estree-walker: 2.0.2
picomatch: 2.3.1 picomatch: 2.3.1
rollup: 4.17.1 rollup: 4.17.2
dev: true dev: true
/@rollup/rollup-android-arm-eabi@4.17.1: /@rollup/rollup-android-arm-eabi@4.17.2:
resolution: {integrity: sha512-P6Wg856Ou/DLpR+O0ZLneNmrv7QpqBg+hK4wE05ijbC/t349BRfMfx+UFj5Ha3fCFopIa6iSZlpdaB4agkWp2Q==} resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==}
cpu: [arm] cpu: [arm]
os: [android] os: [android]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-android-arm64@4.17.1: /@rollup/rollup-android-arm64@4.17.2:
resolution: {integrity: sha512-piwZDjuW2WiHr05djVdUkrG5JbjnGbtx8BXQchYCMfib/nhjzWoiScelZ+s5IJI7lecrwSxHCzW026MWBL+oJQ==} resolution: {integrity: sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-darwin-arm64@4.17.1: /@rollup/rollup-darwin-arm64@4.17.2:
resolution: {integrity: sha512-LsZXXIsN5Q460cKDT4Y+bzoPDhBmO5DTr7wP80d+2EnYlxSgkwdPfE3hbE+Fk8dtya+8092N9srjBTJ0di8RIA==} resolution: {integrity: sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-darwin-x64@4.17.1: /@rollup/rollup-darwin-x64@4.17.2:
resolution: {integrity: sha512-S7TYNQpWXB9APkxu/SLmYHezWwCoZRA9QLgrDeml+SR2A1LLPD2DBUdUlvmCF7FUpRMKvbeeWky+iizQj65Etw==} resolution: {integrity: sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-linux-arm-gnueabihf@4.17.1: /@rollup/rollup-linux-arm-gnueabihf@4.17.2:
resolution: {integrity: sha512-Lq2JR5a5jsA5um2ZoLiXXEaOagnVyCpCW7xvlcqHC7y46tLwTEgUSTM3a2TfmmTMmdqv+jknUioWXlmxYxE9Yw==} resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-linux-arm-musleabihf@4.17.1: /@rollup/rollup-linux-arm-musleabihf@4.17.2:
resolution: {integrity: sha512-9BfzwyPNV0IizQoR+5HTNBGkh1KXE8BqU0DBkqMngmyFW7BfuIZyMjQ0s6igJEiPSBvT3ZcnIFohZ19OqjhDPg==} resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-linux-arm64-gnu@4.17.1: /@rollup/rollup-linux-arm64-gnu@4.17.2:
resolution: {integrity: sha512-e2uWaoxo/rtzA52OifrTSXTvJhAXb0XeRkz4CdHBK2KtxrFmuU/uNd544Ogkpu938BzEfvmWs8NZ8Axhw33FDw==} resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-linux-arm64-musl@4.17.1: /@rollup/rollup-linux-arm64-musl@4.17.2:
resolution: {integrity: sha512-ekggix/Bc/d/60H1Mi4YeYb/7dbal1kEDZ6sIFVAE8pUSx7PiWeEh+NWbL7bGu0X68BBIkgF3ibRJe1oFTksQQ==} resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-linux-powerpc64le-gnu@4.17.1: /@rollup/rollup-linux-powerpc64le-gnu@4.17.2:
resolution: {integrity: sha512-UGV0dUo/xCv4pkr/C8KY7XLFwBNnvladt8q+VmdKrw/3RUd3rD0TptwjisvE2TTnnlENtuY4/PZuoOYRiGp8Gw==} resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-linux-riscv64-gnu@4.17.1: /@rollup/rollup-linux-riscv64-gnu@4.17.2:
resolution: {integrity: sha512-gEYmYYHaehdvX46mwXrU49vD6Euf1Bxhq9pPb82cbUU9UT2NV+RSckQ5tKWOnNXZixKsy8/cPGtiUWqzPuAcXQ==} resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-linux-s390x-gnu@4.17.1: /@rollup/rollup-linux-s390x-gnu@4.17.2:
resolution: {integrity: sha512-xeae5pMAxHFp6yX5vajInG2toST5lsCTrckSRUFwNgzYqnUjNBcQyqk1bXUxX5yhjWFl2Mnz3F8vQjl+2FRIcw==} resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-linux-x64-gnu@4.17.1: /@rollup/rollup-linux-x64-gnu@4.17.2:
resolution: {integrity: sha512-AsdnINQoDWfKpBzCPqQWxSPdAWzSgnYbrJYtn6W0H2E9It5bZss99PiLA8CgmDRfvKygt20UpZ3xkhFlIfX9zQ==} resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-linux-x64-musl@4.17.1: /@rollup/rollup-linux-x64-musl@4.17.2:
resolution: {integrity: sha512-KoB4fyKXTR+wYENkIG3fFF+5G6N4GFvzYx8Jax8BR4vmddtuqSb5oQmYu2Uu067vT/Fod7gxeQYKupm8gAcMSQ==} resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-win32-arm64-msvc@4.17.1: /@rollup/rollup-win32-arm64-msvc@4.17.2:
resolution: {integrity: sha512-J0d3NVNf7wBL9t4blCNat+d0PYqAx8wOoY+/9Q5cujnafbX7BmtYk3XvzkqLmFECaWvXGLuHmKj/wrILUinmQg==} resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-win32-ia32-msvc@4.17.1: /@rollup/rollup-win32-ia32-msvc@4.17.2:
resolution: {integrity: sha512-xjgkWUwlq7IbgJSIxvl516FJ2iuC/7ttjsAxSPpC9kkI5iQQFHKyEN5BjbhvJ/IXIZ3yIBcW5QDlWAyrA+TFag==} resolution: {integrity: sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==}
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
requiresBuild: true requiresBuild: true
dev: true dev: true
optional: true optional: true
/@rollup/rollup-win32-x64-msvc@4.17.1: /@rollup/rollup-win32-x64-msvc@4.17.2:
resolution: {integrity: sha512-0QbCkfk6cnnVKWqqlC0cUrrUMDMfu5ffvYMTUHf+qMN2uAb3MKP31LPcwiMXBNsvoFGs/kYdFOsuLmvppCopXA==} resolution: {integrity: sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
requiresBuild: true requiresBuild: true
@ -1567,7 +1567,7 @@ packages:
resolution: {integrity: sha512-pvZdJ004TpC4Ohk9l0CxEXzS9E0L72b5n6lkIEIaWUIy/RlqnkDMHVtEC6InDjd4rt0jZKcvTrDKxeT96WUYnw==} resolution: {integrity: sha512-pvZdJ004TpC4Ohk9l0CxEXzS9E0L72b5n6lkIEIaWUIy/RlqnkDMHVtEC6InDjd4rt0jZKcvTrDKxeT96WUYnw==}
dependencies: dependencies:
'@types/node': 20.12.7 '@types/node': 20.12.7
'@types/vinyl': 2.0.11 '@types/vinyl': 2.0.12
dev: true dev: true
/@types/gulp@4.0.11: /@types/gulp@4.0.11:
@ -1700,13 +1700,6 @@ packages:
'@types/vinyl': 2.0.12 '@types/vinyl': 2.0.12
dev: true dev: true
/@types/vinyl@2.0.11:
resolution: {integrity: sha512-vPXzCLmRp74e9LsP8oltnWKTH+jBwt86WgRUb4Pc9Lf3pkMVGyvIo2gm9bODeGfCay2DBB/hAWDuvf07JcK4rw==}
dependencies:
'@types/expect': 1.20.4
'@types/node': 20.8.10
dev: true
/@types/vinyl@2.0.12: /@types/vinyl@2.0.12:
resolution: {integrity: sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==} resolution: {integrity: sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==}
dependencies: dependencies:
@ -1753,7 +1746,7 @@ packages:
/@vue/compiler-core@3.4.26: /@vue/compiler-core@3.4.26:
resolution: {integrity: sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==} resolution: {integrity: sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==}
dependencies: dependencies:
'@babel/parser': 7.24.4 '@babel/parser': 7.24.5
'@vue/shared': 3.4.26 '@vue/shared': 3.4.26
entities: 4.5.0 entities: 4.5.0
estree-walker: 2.0.2 estree-walker: 2.0.2
@ -1770,7 +1763,7 @@ packages:
/@vue/compiler-sfc@3.4.26: /@vue/compiler-sfc@3.4.26:
resolution: {integrity: sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==} resolution: {integrity: sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==}
dependencies: dependencies:
'@babel/parser': 7.24.4 '@babel/parser': 7.24.5
'@vue/compiler-core': 3.4.26 '@vue/compiler-core': 3.4.26
'@vue/compiler-dom': 3.4.26 '@vue/compiler-dom': 3.4.26
'@vue/compiler-ssr': 3.4.26 '@vue/compiler-ssr': 3.4.26
@ -2245,7 +2238,7 @@ packages:
resolution: {integrity: sha512-WKExI/eSGgGAkWAO+wMVdFObZV7hQen54UpD1kCCTN3tvlL3W1jL4+lPP/M7MwoP7Q4RHzKtO3JQ4HxYEcd+xQ==} resolution: {integrity: sha512-WKExI/eSGgGAkWAO+wMVdFObZV7hQen54UpD1kCCTN3tvlL3W1jL4+lPP/M7MwoP7Q4RHzKtO3JQ4HxYEcd+xQ==}
dependencies: dependencies:
browserslist: 1.7.7 browserslist: 1.7.7
caniuse-db: 1.0.30001610 caniuse-db: 1.0.30001614
normalize-range: 0.1.2 normalize-range: 0.1.2
num2fraction: 1.2.2 num2fraction: 1.2.2
postcss: 5.2.18 postcss: 5.2.18
@ -2330,8 +2323,8 @@ packages:
find-versions: 5.1.0 find-versions: 5.1.0
dev: true dev: true
/binary-extensions@2.2.0: /binary-extensions@2.3.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
@ -2395,8 +2388,8 @@ packages:
deprecated: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools. deprecated: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.
hasBin: true hasBin: true
dependencies: dependencies:
caniuse-db: 1.0.30001610 caniuse-db: 1.0.30001614
electron-to-chromium: 1.4.736 electron-to-chromium: 1.4.751
dev: true dev: true
/browserslist@4.23.0: /browserslist@4.23.0:
@ -2480,13 +2473,13 @@ packages:
resolution: {integrity: sha512-SBTl70K0PkDUIebbkXrxWqZlHNs0wRgRD6QZ8guctShjbh63gEPfF+Wj0Yw+75f5Y8tSzqAI/NcisYv/cCah2Q==} resolution: {integrity: sha512-SBTl70K0PkDUIebbkXrxWqZlHNs0wRgRD6QZ8guctShjbh63gEPfF+Wj0Yw+75f5Y8tSzqAI/NcisYv/cCah2Q==}
dependencies: dependencies:
browserslist: 1.7.7 browserslist: 1.7.7
caniuse-db: 1.0.30001610 caniuse-db: 1.0.30001614
lodash.memoize: 4.1.2 lodash.memoize: 4.1.2
lodash.uniq: 4.5.0 lodash.uniq: 4.5.0
dev: true dev: true
/caniuse-db@1.0.30001610: /caniuse-db@1.0.30001614:
resolution: {integrity: sha512-GLdKwZR0S1EwOBIJlCze89enIbFf0Om/wzHF1GU8nIqf82NmxkB8JsfkOR5A6cZR57wiPJf0WBU+7gnNf0+EyQ==} resolution: {integrity: sha512-AVDISTXTRe7bHFnwAthHh1tNe3FRRap/Ce8rPw+kmNY0k4fV5RVtxvDudXoIyTjfOtRt0Jf7gZvQuBfsjLZI3A==}
dev: true dev: true
/caniuse-lite@1.0.30001606: /caniuse-lite@1.0.30001606:
@ -2975,7 +2968,7 @@ packages:
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
engines: {node: '>=0.11'} engines: {node: '>=0.11'}
dependencies: dependencies:
'@babel/runtime': 7.24.4 '@babel/runtime': 7.24.5
dev: true dev: true
/dayjs@1.11.11: /dayjs@1.11.11:
@ -3086,8 +3079,8 @@ packages:
resolution: {integrity: sha512-bx7+5Saea/qu14kmPTDHQxkp2UnziG3iajUQu3BxFvCOnpAJdDbMV4rSl+EqFDkkpNNVUFlR1kDfpL59xfy1HA==} resolution: {integrity: sha512-bx7+5Saea/qu14kmPTDHQxkp2UnziG3iajUQu3BxFvCOnpAJdDbMV4rSl+EqFDkkpNNVUFlR1kDfpL59xfy1HA==}
dev: true dev: true
/electron-to-chromium@1.4.736: /electron-to-chromium@1.4.751:
resolution: {integrity: sha512-Rer6wc3ynLelKNM4lOCg7/zPQj8tPOCB2hzD32PX9wd3hgRRi9MxEbmkFCokzcEhRVMiOVLjnL9ig9cefJ+6+Q==} resolution: {integrity: sha512-2DEPi++qa89SMGRhufWTiLmzqyuGmNF3SK4+PQetW1JKiZdEpF4XQonJXJCzyuYSA6mauiMhbyVhqYAP45Hvfw==}
dev: true dev: true
/emoji-regex@8.0.0: /emoji-regex@8.0.0:
@ -3854,7 +3847,7 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dependencies: dependencies:
'@types/node': 20.12.7 '@types/node': 20.12.7
'@types/vinyl': 2.0.11 '@types/vinyl': 2.0.12
istextorbinary: 3.3.0 istextorbinary: 3.3.0
replacestream: 4.0.3 replacestream: 4.0.3
yargs-parser: 21.1.1 yargs-parser: 21.1.1
@ -3865,7 +3858,7 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dependencies: dependencies:
plugin-error: 1.0.1 plugin-error: 1.0.1
terser: 5.30.3 terser: 5.31.0
through2: 4.0.2 through2: 4.0.2
vinyl-sourcemaps-apply: 0.2.1 vinyl-sourcemaps-apply: 0.2.1
dev: true dev: true
@ -4106,7 +4099,7 @@ packages:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'} engines: {node: '>=8'}
dependencies: dependencies:
binary-extensions: 2.2.0 binary-extensions: 2.3.0
dev: true dev: true
/is-ci@3.0.1: /is-ci@3.0.1:
@ -5596,29 +5589,29 @@ packages:
seedrandom: 2.4.2 seedrandom: 2.4.2
dev: true dev: true
/rollup@4.17.1: /rollup@4.17.2:
resolution: {integrity: sha512-0gG94inrUtg25sB2V/pApwiv1lUb0bQ25FPNuzO89Baa+B+c0ccaaBKM5zkZV/12pUUdH+lWCSm9wmHqyocuVQ==} resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'} engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true hasBin: true
dependencies: dependencies:
'@types/estree': 1.0.5 '@types/estree': 1.0.5
optionalDependencies: optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.17.1 '@rollup/rollup-android-arm-eabi': 4.17.2
'@rollup/rollup-android-arm64': 4.17.1 '@rollup/rollup-android-arm64': 4.17.2
'@rollup/rollup-darwin-arm64': 4.17.1 '@rollup/rollup-darwin-arm64': 4.17.2
'@rollup/rollup-darwin-x64': 4.17.1 '@rollup/rollup-darwin-x64': 4.17.2
'@rollup/rollup-linux-arm-gnueabihf': 4.17.1 '@rollup/rollup-linux-arm-gnueabihf': 4.17.2
'@rollup/rollup-linux-arm-musleabihf': 4.17.1 '@rollup/rollup-linux-arm-musleabihf': 4.17.2
'@rollup/rollup-linux-arm64-gnu': 4.17.1 '@rollup/rollup-linux-arm64-gnu': 4.17.2
'@rollup/rollup-linux-arm64-musl': 4.17.1 '@rollup/rollup-linux-arm64-musl': 4.17.2
'@rollup/rollup-linux-powerpc64le-gnu': 4.17.1 '@rollup/rollup-linux-powerpc64le-gnu': 4.17.2
'@rollup/rollup-linux-riscv64-gnu': 4.17.1 '@rollup/rollup-linux-riscv64-gnu': 4.17.2
'@rollup/rollup-linux-s390x-gnu': 4.17.1 '@rollup/rollup-linux-s390x-gnu': 4.17.2
'@rollup/rollup-linux-x64-gnu': 4.17.1 '@rollup/rollup-linux-x64-gnu': 4.17.2
'@rollup/rollup-linux-x64-musl': 4.17.1 '@rollup/rollup-linux-x64-musl': 4.17.2
'@rollup/rollup-win32-arm64-msvc': 4.17.1 '@rollup/rollup-win32-arm64-msvc': 4.17.2
'@rollup/rollup-win32-ia32-msvc': 4.17.1 '@rollup/rollup-win32-ia32-msvc': 4.17.2
'@rollup/rollup-win32-x64-msvc': 4.17.1 '@rollup/rollup-win32-x64-msvc': 4.17.2
fsevents: 2.3.3 fsevents: 2.3.3
dev: true dev: true
@ -6187,6 +6180,17 @@ packages:
source-map-support: 0.5.21 source-map-support: 0.5.21
dev: true dev: true
/terser@5.31.0:
resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==}
engines: {node: '>=10'}
hasBin: true
dependencies:
'@jridgewell/source-map': 0.3.6
acorn: 8.11.3
commander: 2.20.3
source-map-support: 0.5.21
dev: true
/textarea-caret@3.1.0: /textarea-caret@3.1.0:
resolution: {integrity: sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q==} resolution: {integrity: sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q==}
dev: true dev: true
@ -6723,14 +6727,14 @@ packages:
'@types/node': 20.12.7 '@types/node': 20.12.7
esbuild: 0.20.2 esbuild: 0.20.2
postcss: 8.4.38 postcss: 8.4.38
rollup: 4.17.1 rollup: 4.17.2
sass: 1.62.1 sass: 1.62.1
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
dev: true dev: true
/vue-component-type-helpers@2.0.14: /vue-component-type-helpers@2.0.15:
resolution: {integrity: sha512-DInfgOyXlMyliyqAAD9frK28tTfch0+tMi4qoWJcZlRxUf+NFAtraJBnAsKLep+FOyLMiajkhfyEb3xLK08i7w==} resolution: {integrity: sha512-jR/Hw52gzNQxMovJBsOQ/F9E1UQ8K1Np0CVG3RnueLkaCKqWuyL9XHl/5tUBAGJx+bk5xZ+co7vK23+Pzt75Lg==}
dev: true dev: true
/vue-demi@0.14.7(vue@3.4.26): /vue-demi@0.14.7(vue@3.4.26):