Merge branch 'develop' into swn

This commit is contained in:
tamaina 2021-10-16 15:30:57 +09:00
commit 8899cda211
29 changed files with 484 additions and 218 deletions

View file

@ -14,17 +14,18 @@
### Improvements ### Improvements
- アカウント登録にメールアドレスの設定を必須にするオプション - アカウント登録にメールアドレスの設定を必須にするオプション
- クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように - クライアント: 全体的なUIのブラッシュアップ
- クライアント: MFM関数構文のサジェストを実装 - クライアント: MFM関数構文のサジェストを実装
- クライアント: ノート本文を投稿フォーム内でプレビューできるように
- クライアント: 未読の通知のみ表示する機能 - クライアント: 未読の通知のみ表示する機能
- クライアント: 通知ページで通知の種類によるフィルタ - クライアント: 通知ページで通知の種類によるフィルタ
- クライアント: アニメーションを減らす設定の適用範囲を拡充
- クライアント: 新しいダークテーマを追加 - クライアント: 新しいダークテーマを追加
- クライアント: テーマコンパイラに hue と saturate 関数を追加 - クライアント: テーマコンパイラに hue と saturate 関数を追加
- ActivityPub: HTML -> MFMの変換を強化 - ActivityPub: HTML -> MFMの変換を強化
- API: i/notifications に unreadOnly オプションを追加 - API: i/notifications に unreadOnly オプションを追加
- API: ap系のエンドポイントをログイン必須化+レートリミット追加 - API: ap系のエンドポイントをログイン必須化+レートリミット追加
- Misskeyのコマンドラインオプションを廃止 - MFM: Add tag syntaxes of bold <b></b> and strikethrough <s></s>
- 代わりに環境変数で設定することができます
### Bugfixes ### Bugfixes
- Fix createDeleteAccountJob - Fix createDeleteAccountJob
@ -33,7 +34,17 @@
- クライアント: ヘッダーにタブが表示されている状態でタイトルをクリックしたときにタブ選択が表示されるのを修正 - クライアント: ヘッダーにタブが表示されている状態でタイトルをクリックしたときにタブ選択が表示されるのを修正
- クライアント: ユーザーページのタブが機能していない問題を修正 - クライアント: ユーザーページのタブが機能していない問題を修正
- クライアント: ピン留めユーザーの設定項目がない問題を修正 - クライアント: ピン留めユーザーの設定項目がない問題を修正
- クライアント: Deck UIにおいて、重ねたカラムの片方を畳んだ状態で右に出すと表示が壊れる問題を修正
- API: 管理者およびモデレーターをブロックできてしまう問題を修正 - API: 管理者およびモデレーターをブロックできてしまう問題を修正
- MFM: Mentions in the link label are parsed as text
- MFM: Add a property to the URL node indicating whether it was enclosed in <>
- MFM: Disallows < and > in hashtags
### Changes
- 保守性やユーザビリティの観点から、Misskeyのコマンドラインオプションが削除されました。
- 必要であれば、代わりに環境変数で設定することができます
- MFM: パフォーマンス、保守性、構文誤認識抑制の観点から、旧関数構文のサポートが削除されました。
- 旧構文(`[foo bar]`)を使用せず、現行の構文(`$[foo bar]`)を使用してください。
## 12.91.0 (2021/09/22) ## 12.91.0 (2021/09/22)

View file

@ -766,6 +766,7 @@ middle: "中"
low: "低" low: "低"
emailNotConfiguredWarning: "メールアドレスの設定がされていません。" emailNotConfiguredWarning: "メールアドレスの設定がされていません。"
ratio: "比率" ratio: "比率"
previewNoteText: "本文をプレビュー"
customCss: "カスタムCSS" customCss: "カスタムCSS"
customCssWarn: "この設定は必ず知識のある方が行ってください。不適切な設定を行うとクライアントが正常に使用できなくなる恐れがあります。" customCssWarn: "この設定は必ず知識のある方が行ってください。不適切な設定を行うとクライアントが正常に使用できなくなる恐れがあります。"
global: "グローバル" global: "グローバル"

View file

@ -1,7 +1,7 @@
<template> <template>
<div class="fdidabkb" :class="{ slim: narrow, thin }" :style="{ background: bg }" @click="onClick" ref="el"> <div class="fdidabkb" :class="{ slim: narrow, thin: thin_ }" :style="{ background: bg }" @click="onClick" ref="el">
<template v-if="info"> <template v-if="info">
<div class="titleContainer" @click="showTabsPopup"> <div class="titleContainer" @click="showTabsPopup" v-if="!hideTitle">
<i v-if="info.icon" class="icon" :class="info.icon"></i> <i v-if="info.icon" class="icon" :class="info.icon"></i>
<MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/> <MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/>
@ -17,7 +17,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="tabs" v-if="!narrow"> <div class="tabs" v-if="!narrow || hideTitle">
<button class="tab _button" v-for="tab in info.tabs" :class="{ active: tab.active }" @click="tab.onClick" v-tooltip="tab.title"> <button class="tab _button" v-for="tab in info.tabs" :class="{ active: tab.active }" @click="tab.onClick" v-tooltip="tab.title">
<i v-if="tab.icon" class="icon" :class="tab.icon"></i> <i v-if="tab.icon" class="icon" :class="tab.icon"></i>
<span v-if="!tab.iconOnly" class="title">{{ tab.title }}</span> <span v-if="!tab.iconOnly" class="title">{{ tab.title }}</span>
@ -37,7 +37,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, onMounted, onUnmounted, PropType, ref } from 'vue'; import { computed, defineComponent, onMounted, onUnmounted, PropType, ref, inject } from 'vue';
import * as tinycolor from 'tinycolor2'; import * as tinycolor from 'tinycolor2';
import { popupMenu } from '@client/os'; import { popupMenu } from '@client/os';
import { url } from '@client/config'; import { url } from '@client/config';
@ -182,6 +182,8 @@ export default defineComponent({
showTabsPopup, showTabsPopup,
preventDrag, preventDrag,
onClick, onClick,
hideTitle: inject('shouldOmitHeaderTitle', false),
thin_: props.thin || inject('shouldHeaderThin', false)
}; };
}, },
}); });
@ -207,12 +209,16 @@ export default defineComponent({
text-align: center; text-align: center;
> .titleContainer { > .titleContainer {
flex: 1;
margin: 0 auto; margin: 0 auto;
margin-left: var(--height);
> *:first-child {
margin-left: auto;
} }
> .buttons { > *:last-child {
&.right { margin-right: auto;
margin-left: 0;
} }
} }
} }

View file

@ -0,0 +1,70 @@
<template>
<div ref="root" :class="$style.root" :style="{ padding: margin + 'px' }">
<div ref="content" :class="$style.content">
<slot></slot>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
export default defineComponent({
props: {
contentMax: {
type: Number,
required: false,
default: null,
}
},
setup(props, context) {
let ro: ResizeObserver;
const root = ref<HTMLElement>(null);
const content = ref<HTMLElement>(null);
const margin = ref(0);
const adjust = (rect: { width: number; height: number; }) => {
if (rect.width > (props.contentMax || 500)) {
margin.value = 32;
} else {
margin.value = 12;
}
};
onMounted(() => {
ro = new ResizeObserver((entries) => {
adjust({
width: entries[0].borderBoxSize[0].inlineSize,
height: entries[0].borderBoxSize[0].blockSize,
});
});
ro.observe(root.value);
if (props.contentMax) {
content.value.style.maxWidth = `${props.contentMax}px`;
}
});
onUnmounted(() => {
ro.disconnect();
});
return {
root,
content,
margin,
};
},
});
</script>
<style lang="scss" module>
.root {
box-sizing: border-box;
width: 100%;
}
.content {
margin: 0 auto;
}
</style>

View file

@ -14,6 +14,7 @@ import loading from './global/loading.vue';
import error from './global/error.vue'; import error from './global/error.vue';
import ad from './global/ad.vue'; import ad from './global/ad.vue';
import header from './global/header.vue'; import header from './global/header.vue';
import spacer from './global/spacer.vue';
export default function(app: App) { export default function(app: App) {
app.component('I18n', i18n); app.component('I18n', i18n);
@ -30,4 +31,5 @@ export default function(app: App) {
app.component('MkError', error); app.component('MkError', error);
app.component('MkAd', ad); app.component('MkAd', ad);
app.component('MkHeader', header); app.component('MkHeader', header);
app.component('MkSpacer', spacer);
} }

View file

@ -2,7 +2,7 @@
<MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')"> <MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')">
<div class="hrmcaedk _window _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }"> <div class="hrmcaedk _window _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }">
<div class="header" @contextmenu="onContextmenu"> <div class="header" @contextmenu="onContextmenu">
<button v-if="history.length > 0" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button> <button v-if="history.length > 0" class="_button" @click="back()" v-tooltip="$ts.goBack"><i class="fas fa-arrow-left"></i></button>
<span v-else style="display: inline-block; width: 20px"></span> <span v-else style="display: inline-block; width: 20px"></span>
<span v-if="pageInfo" class="title"> <span v-if="pageInfo" class="title">
<i v-if="pageInfo.icon" class="icon" :class="pageInfo.icon"></i> <i v-if="pageInfo.icon" class="icon" :class="pageInfo.icon"></i>
@ -44,7 +44,8 @@ export default defineComponent({
return { return {
navHook: (path) => { navHook: (path) => {
this.navigate(path); this.navigate(path);
} },
shouldHeaderThin: true,
}; };
}, },

View file

@ -80,7 +80,7 @@
</div> </div>
<XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/> <XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/>
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="true" class="url-preview"/> <MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="true" class="url-preview"/>
<div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div> <div class="renote" v-if="appearNote.renote"><XNoteSimple :note="appearNote.renote"/></div>
</div> </div>
<MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><i class="fas fa-satellite-dish"></i> {{ appearNote.channel.name }}</MkA> <MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><i class="fas fa-satellite-dish"></i> {{ appearNote.channel.name }}</MkA>
</div> </div>
@ -132,7 +132,7 @@ import * as mfm from 'mfm-js';
import { sum } from '../../prelude/array'; import { sum } from '../../prelude/array';
import XSub from './note.sub.vue'; import XSub from './note.sub.vue';
import XNoteHeader from './note-header.vue'; import XNoteHeader from './note-header.vue';
import XNotePreview from './note-preview.vue'; import XNoteSimple from './note-simple.vue';
import XReactionsViewer from './reactions-viewer.vue'; import XReactionsViewer from './reactions-viewer.vue';
import XMediaList from './media-list.vue'; import XMediaList from './media-list.vue';
import XCwButton from './cw-button.vue'; import XCwButton from './cw-button.vue';
@ -153,7 +153,7 @@ export default defineComponent({
components: { components: {
XSub, XSub,
XNoteHeader, XNoteHeader,
XNotePreview, XNoteSimple,
XReactionsViewer, XReactionsViewer,
XMediaList, XMediaList,
XCwButton, XCwButton,

View file

@ -1,15 +1,13 @@
<template> <template>
<div class="yohlumlk" v-size="{ min: [350, 500] }"> <div class="fefdfafb" v-size="{ min: [350, 500] }">
<MkAvatar class="avatar" :user="note.user"/> <MkAvatar class="avatar" :user="$i"/>
<div class="main"> <div class="main">
<XNoteHeader class="header" :note="note" :mini="true"/> <div class="header">
<MkUserName :user="$i"/>
</div>
<div class="body"> <div class="body">
<p v-if="note.cw != null" class="cw"> <div class="content">
<span class="text" v-if="note.cw != ''">{{ note.cw }}</span> <Mfm :text="text" :author="$i" :i="$i"/>
<XCwButton v-model="showContent" :note="note"/>
</p>
<div class="content" v-show="note.cw == null || showContent">
<XSubNote-content class="text" :note="note"/>
</div> </div>
</div> </div>
</div> </div>
@ -18,35 +16,22 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import XNoteHeader from './note-header.vue';
import XSubNoteContent from './sub-note-content.vue';
import XCwButton from './cw-button.vue';
import * as os from '@client/os';
export default defineComponent({ export default defineComponent({
components: { components: {
XNoteHeader,
XSubNoteContent,
XCwButton,
}, },
props: { props: {
note: { text: {
type: Object, type: String,
required: true required: true
} }
}, },
data() {
return {
showContent: false
};
}
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.yohlumlk { .fefdfafb {
display: flex; display: flex;
margin: 0; margin: 0;
padding: 0; padding: 0;

View file

@ -0,0 +1,113 @@
<template>
<div class="yohlumlk" v-size="{ min: [350, 500] }">
<MkAvatar class="avatar" :user="note.user"/>
<div class="main">
<XNoteHeader class="header" :note="note" :mini="true"/>
<div class="body">
<p v-if="note.cw != null" class="cw">
<span class="text" v-if="note.cw != ''">{{ note.cw }}</span>
<XCwButton v-model="showContent" :note="note"/>
</p>
<div class="content" v-show="note.cw == null || showContent">
<XSubNote-content class="text" :note="note"/>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XNoteHeader from './note-header.vue';
import XSubNoteContent from './sub-note-content.vue';
import XCwButton from './cw-button.vue';
import * as os from '@client/os';
export default defineComponent({
components: {
XNoteHeader,
XSubNoteContent,
XCwButton,
},
props: {
note: {
type: Object,
required: true
}
},
data() {
return {
showContent: false
};
}
});
</script>
<style lang="scss" scoped>
.yohlumlk {
display: flex;
margin: 0;
padding: 0;
overflow: clip;
font-size: 0.95em;
&.min-width_350px {
> .avatar {
margin: 0 10px 0 0;
width: 44px;
height: 44px;
}
}
&.min-width_500px {
> .avatar {
margin: 0 12px 0 0;
width: 48px;
height: 48px;
}
}
> .avatar {
flex-shrink: 0;
display: block;
margin: 0 10px 0 0;
width: 40px;
height: 40px;
border-radius: 8px;
}
> .main {
flex: 1;
min-width: 0;
> .header {
margin-bottom: 2px;
}
> .body {
> .cw {
cursor: default;
display: block;
margin: 0;
padding: 0;
overflow-wrap: break-word;
> .text {
margin-right: 8px;
}
}
> .content {
> .text {
cursor: default;
margin: 0;
padding: 0;
}
}
}
}
}
</style>

View file

@ -64,7 +64,7 @@
</div> </div>
<XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/> <XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/>
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="false" class="url-preview"/> <MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="false" class="url-preview"/>
<div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div> <div class="renote" v-if="appearNote.renote"><XNoteSimple :note="appearNote.renote"/></div>
<button v-if="collapsed" class="fade _button" @click="collapsed = false"> <button v-if="collapsed" class="fade _button" @click="collapsed = false">
<span>{{ $ts.showMore }}</span> <span>{{ $ts.showMore }}</span>
</button> </button>
@ -114,7 +114,7 @@ import * as mfm from 'mfm-js';
import { sum } from '../../prelude/array'; import { sum } from '../../prelude/array';
import XSub from './note.sub.vue'; import XSub from './note.sub.vue';
import XNoteHeader from './note-header.vue'; import XNoteHeader from './note-header.vue';
import XNotePreview from './note-preview.vue'; import XNoteSimple from './note-simple.vue';
import XReactionsViewer from './reactions-viewer.vue'; import XReactionsViewer from './reactions-viewer.vue';
import XMediaList from './media-list.vue'; import XMediaList from './media-list.vue';
import XCwButton from './cw-button.vue'; import XCwButton from './cw-button.vue';
@ -134,7 +134,7 @@ export default defineComponent({
components: { components: {
XSub, XSub,
XNoteHeader, XNoteHeader,
XNotePreview, XNoteSimple,
XReactionsViewer, XReactionsViewer,
XMediaList, XMediaList,
XCwButton, XCwButton,

View file

@ -14,7 +14,7 @@
</template> </template>
</template> </template>
<template #headerLeft> <template #headerLeft>
<button v-if="history.length > 0" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button> <button v-if="history.length > 0" class="_button" @click="back()" v-tooltip="$ts.goBack"><i class="fas fa-arrow-left"></i></button>
</template> </template>
<div class="yrolvcoq _flat_"> <div class="yrolvcoq _flat_">
<component :is="component" v-bind="props" :ref="changePage"/> <component :is="component" v-bind="props" :ref="changePage"/>
@ -46,7 +46,8 @@ export default defineComponent({
return { return {
navHook: (path) => { navHook: (path) => {
this.navigate(path); this.navigate(path);
} },
shouldHeaderThin: true,
}; };
}, },

View file

@ -1,6 +1,6 @@
<template> <template>
<div class="gafaadew" :class="{ modal, _popup: modal }" <div class="gafaadew" :class="{ modal, _popup: modal }"
v-size="{ max: [500] }" v-size="{ max: [310, 500] }"
@dragover.stop="onDragover" @dragover.stop="onDragover"
@dragenter="onDragenter" @dragenter="onDragenter"
@dragleave="onDragleave" @dragleave="onDragleave"
@ -17,12 +17,13 @@
<span v-if="visibility === 'followers'"><i class="fas fa-unlock"></i></span> <span v-if="visibility === 'followers'"><i class="fas fa-unlock"></i></span>
<span v-if="visibility === 'specified'"><i class="fas fa-envelope"></i></span> <span v-if="visibility === 'specified'"><i class="fas fa-envelope"></i></span>
</button> </button>
<button class="submit _buttonPrimary" :disabled="!canPost" @click="post" data-cy-open-post-form-submit>{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button> <button class="_button preview" @click="showPreview = !showPreview" :class="{ active: showPreview }" v-tooltip="$ts.previewNoteText"><i class="fas fa-file-code"></i></button>
<button class="submit _buttonGradate" :disabled="!canPost" @click="post" data-cy-open-post-form-submit>{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button>
</div> </div>
</header> </header>
<div class="form" :class="{ fixed }"> <div class="form" :class="{ fixed }">
<XNotePreview class="preview" v-if="reply" :note="reply"/> <XNoteSimple class="preview" v-if="reply" :note="reply"/>
<XNotePreview class="preview" v-if="renote" :note="renote"/> <XNoteSimple class="preview" v-if="renote" :note="renote"/>
<div class="with-quote" v-if="quoteId"><i class="fas fa-quote-left"></i> {{ $ts.quoteAttached }}<button @click="quoteId = null"><i class="fas fa-times"></i></button></div> <div class="with-quote" v-if="quoteId"><i class="fas fa-quote-left"></i> {{ $ts.quoteAttached }}<button @click="quoteId = null"><i class="fas fa-times"></i></button></div>
<div v-if="visibility === 'specified'" class="to-specified"> <div v-if="visibility === 'specified'" class="to-specified">
<span style="margin-right: 8px;">{{ $ts.recipient }}</span> <span style="margin-right: 8px;">{{ $ts.recipient }}</span>
@ -40,6 +41,7 @@
<input v-show="withHashtags" ref="hashtags" class="hashtags" v-model="hashtags" :placeholder="$ts.hashtags" list="hashtags"> <input v-show="withHashtags" ref="hashtags" class="hashtags" v-model="hashtags" :placeholder="$ts.hashtags" list="hashtags">
<XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/> <XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/>
<XPollEditor v-if="poll" :poll="poll" @destroyed="poll = null" @updated="onPollUpdate"/> <XPollEditor v-if="poll" :poll="poll" @destroyed="poll = null" @updated="onPollUpdate"/>
<XNotePreview class="preview" v-if="showPreview" :text="text"/>
<footer> <footer>
<button class="_button" @click="chooseFileFrom" v-tooltip="$ts.attachFile"><i class="fas fa-photo-video"></i></button> <button class="_button" @click="chooseFileFrom" v-tooltip="$ts.attachFile"><i class="fas fa-photo-video"></i></button>
<button class="_button" @click="togglePoll" :class="{ active: poll }" v-tooltip="$ts.poll"><i class="fas fa-poll-h"></i></button> <button class="_button" @click="togglePoll" :class="{ active: poll }" v-tooltip="$ts.poll"><i class="fas fa-poll-h"></i></button>
@ -61,6 +63,7 @@ import { defineComponent, defineAsyncComponent } from 'vue';
import insertTextAtCursor from 'insert-text-at-cursor'; import insertTextAtCursor from 'insert-text-at-cursor';
import { length } from 'stringz'; import { length } from 'stringz';
import { toASCII } from 'punycode/'; import { toASCII } from 'punycode/';
import XNoteSimple from './note-simple.vue';
import XNotePreview from './note-preview.vue'; import XNotePreview from './note-preview.vue';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import { host, url } from '@client/config'; import { host, url } from '@client/config';
@ -80,6 +83,7 @@ import { defaultStore } from '@client/store';
export default defineComponent({ export default defineComponent({
components: { components: {
XNoteSimple,
XNotePreview, XNotePreview,
XPostFormAttaches: defineAsyncComponent(() => import('./post-form-attaches.vue')), XPostFormAttaches: defineAsyncComponent(() => import('./post-form-attaches.vue')),
XPollEditor: defineAsyncComponent(() => import('./poll-editor.vue')), XPollEditor: defineAsyncComponent(() => import('./poll-editor.vue')),
@ -160,6 +164,7 @@ export default defineComponent({
files: [], files: [],
poll: null, poll: null,
useCw: false, useCw: false,
showPreview: false,
cw: null, cw: null,
localOnly: this.$store.state.rememberNoteVisibility ? this.$store.state.localOnly : this.$store.state.defaultNoteLocalOnly, localOnly: this.$store.state.rememberNoteVisibility ? this.$store.state.localOnly : this.$store.state.defaultNoteLocalOnly,
visibility: (this.$store.state.rememberNoteVisibility ? this.$store.state.visibility : this.$store.state.defaultNoteVisibility) as typeof noteVisibilities[number], visibility: (this.$store.state.rememberNoteVisibility ? this.$store.state.visibility : this.$store.state.defaultNoteVisibility) as typeof noteVisibilities[number],
@ -743,7 +748,7 @@ export default defineComponent({
> .visibility { > .visibility {
height: 34px; height: 34px;
width: 34px; width: 34px;
margin: 0 8px; margin: 0 0 0 8px;
& + .localOnly { & + .localOnly {
margin-left: 0 !important; margin-left: 0 !important;
@ -755,6 +760,24 @@ export default defineComponent({
opacity: 0.7; opacity: 0.7;
} }
> .preview {
display: inline-block;
padding: 0;
margin: 0 8px 0 0;
font-size: 16px;
width: 34px;
height: 34px;
border-radius: 6px;
&:hover {
background: var(--X5);
}
&.active {
color: var(--accent);
}
}
> .submit { > .submit {
margin: 16px 16px 16px 0; margin: 16px 16px 16px 0;
padding: 0 12px; padding: 0 12px;
@ -762,6 +785,7 @@ export default defineComponent({
font-weight: bold; font-weight: bold;
vertical-align: bottom; vertical-align: bottom;
border-radius: 4px; border-radius: 4px;
font-size: 0.9em;
&:disabled { &:disabled {
opacity: 0.7; opacity: 0.7;
@ -940,5 +964,17 @@ export default defineComponent({
} }
} }
} }
&.max-width_310px {
> .form {
> footer {
> button {
font-size: 14px;
width: 44px;
height: 44px;
}
}
}
}
} }
</style> </style>

View file

@ -188,11 +188,11 @@ export default defineComponent({
background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
&:not(:disabled):hover { &:not(:disabled):hover {
background: var(--X8); background: linear-gradient(90deg, var(--X8), var(--X8));
} }
&:not(:disabled):active { &:not(:disabled):active {
background: var(--X8); background: linear-gradient(90deg, var(--X8), var(--X8));
} }
} }

View file

@ -152,7 +152,7 @@ export default defineComponent({
> .item { > .item {
display: block; display: block;
position: relative; position: relative;
padding: 8px 16px; padding: 8px 18px;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
white-space: nowrap; white-space: nowrap;
@ -170,10 +170,9 @@ export default defineComponent({
left: 0; left: 0;
right: 0; right: 0;
margin: auto; margin: auto;
//width: calc(100% - 16px); width: calc(100% - 16px);
width: 100%;
height: 100%; height: 100%;
//border-radius: 6px; border-radius: 6px;
} }
> * { > * {
@ -243,12 +242,12 @@ export default defineComponent({
} }
> i { > i {
margin-right: 4px; margin-right: 5px;
width: 20px; width: 20px;
} }
> .avatar { > .avatar {
margin-right: 4px; margin-right: 5px;
width: 20px; width: 20px;
height: 20px; height: 20px;
} }

View file

@ -65,8 +65,8 @@ export default defineComponent({
align-items: center; align-items: center;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
padding: 10px 16px 10px 14px; padding: 10px 16px 10px 8px;
border-radius: 999px; border-radius: 9px;
font-size: 0.9em; font-size: 0.9em;
&:hover { &:hover {

View file

@ -23,12 +23,14 @@ export default defineComponent({
}, },
mounted() { mounted() {
if (this.$store.animation) {
VanillaTilt.init(this.$el, { VanillaTilt.init(this.$el, {
reverse: true, reverse: true,
gyroscope: false, gyroscope: false,
scale: 1.1, scale: 1.1,
speed: 500, speed: 500,
}); });
}
}, },
methods: { methods: {

View file

@ -2,7 +2,8 @@
<div> <div>
<MkHeader :info="header"/> <MkHeader :info="header"/>
<div class="lznhrdub _root"> <MkSpacer :content-max="1200">
<div class="lznhrdub">
<div v-if="tab === 'local'"> <div v-if="tab === 'local'">
<div class="localfedi7 _block _isolated" v-if="meta && stats && tag == null" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"> <div class="localfedi7 _block _isolated" v-if="meta && stats && tag == null" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }">
<header><span>{{ $t('explore', { host: meta.name || 'Misskey' }) }}</span></header> <header><span>{{ $t('explore', { host: meta.name || 'Misskey' }) }}</span></header>
@ -73,6 +74,7 @@
<XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/> <XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/>
</div> </div>
</div> </div>
</MkSpacer>
</div> </div>
</template> </template>
@ -214,12 +216,6 @@ export default defineComponent({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.lznhrdub {
max-width: 1400px;
margin: 0 auto;
padding: 16px;
}
.localfedi7 { .localfedi7 {
color: #fff; color: #fff;
padding: 16px; padding: 16px;

View file

@ -1,9 +1,11 @@
<template> <template>
<div> <div>
<MkHeader :info="header"/> <MkHeader :info="header"/>
<div class="clupoqwt" v-size="{ min: [800] }"> <MkSpacer :content-max="800">
<div class="clupoqwt">
<XNotifications class="notifications" @before="before" @after="after" :include-types="includeTypes" :unread-only="tab === 'unread'"/> <XNotifications class="notifications" @before="before" @after="after" :include-types="includeTypes" :unread-only="tab === 'unread'"/>
</div> </div>
</MkSpacer>
</div> </div>
</template> </template>
@ -90,14 +92,5 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
.clupoqwt { .clupoqwt {
&.min-width_800px {
background: var(--bg);
padding: 32px 0;
> .notifications {
max-width: 800px;
margin: 0 auto;
}
}
} }
</style> </style>

View file

@ -1,4 +1,7 @@
<template> <template>
<div>
<MkHeader :info="header"/>
<div class="_root"> <div class="_root">
<transition name="fade" mode="out-in"> <transition name="fade" mode="out-in">
<div v-if="page" class="xcukqgmh" :key="page.id" v-size="{ max: [450] }"> <div v-if="page" class="xcukqgmh" :key="page.id" v-size="{ max: [450] }">
@ -57,6 +60,7 @@
<MkLoading v-else/> <MkLoading v-else/>
</transition> </transition>
</div> </div>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -97,6 +101,10 @@ export default defineComponent({
[symbols.PAGE_INFO]: computed(() => this.page ? { [symbols.PAGE_INFO]: computed(() => this.page ? {
title: computed(() => this.page.title || this.page.name), title: computed(() => this.page.title || this.page.name),
avatar: this.page.user, avatar: this.page.user,
} : null),
header: computed(() => this.page ? {
title: computed(() => this.page.title || this.page.name),
avatar: this.page.user,
path: `/@${this.page.user.username}/pages/${this.page.name}`, path: `/@${this.page.user.username}/pages/${this.page.name}`,
share: { share: {
title: this.page.title || this.page.name, title: this.page.title || this.page.name,

View file

@ -271,6 +271,12 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
.vvcocwet { .vvcocwet {
> .nav { > .nav {
> .title {
margin: 16px;
font-size: 1.5em;
font-weight: bold;
}
> .info { > .info {
margin: 0 16px; margin: 0 16px;
} }
@ -295,6 +301,10 @@ export default defineComponent({
width: 32%; width: 32%;
box-sizing: border-box; box-sizing: border-box;
overflow: auto; overflow: auto;
> .title {
margin: 24px;
}
} }
> .main { > .main {

View file

@ -215,6 +215,10 @@ export default defineComponent({
} }
} }
> .post-form {
border-radius: var(--radius);
}
> .tl { > .tl {
background: var(--bg); background: var(--bg);
border-radius: var(--radius); border-radius: var(--radius);

View file

@ -202,6 +202,20 @@ hr {
} }
} }
._buttonGradate {
@extend ._buttonPrimary;
color: var(--fgOnAccent);
background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB));
&:not(:disabled):hover {
background: linear-gradient(90deg, var(--X8), var(--X8));
}
&:not(:disabled):active {
background: linear-gradient(90deg, var(--X8), var(--X8));
}
}
._help { ._help {
color: var(--accent); color: var(--accent);
cursor: help cursor: help

View file

@ -20,7 +20,7 @@
renote: '@accent', renote: '@accent',
mention: 'rgb(212, 153, 76)', mention: 'rgb(212, 153, 76)',
mentionMe: 'rgb(212, 210, 76)', mentionMe: 'rgb(212, 210, 76)',
hashtag: 'rgb(76, 212, 180)', hashtag: '#5bcbb0',
link: '@accent', link: '@accent',
}, },
} }

View file

@ -56,7 +56,7 @@
</div> </div>
<XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/> <XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/>
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="false" class="url-preview"/> <MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="false" class="url-preview"/>
<div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div> <div class="renote" v-if="appearNote.renote"><XNoteSimple :note="appearNote.renote"/></div>
<button v-if="collapsed" class="fade _button" @click="collapsed = false"> <button v-if="collapsed" class="fade _button" @click="collapsed = false">
<span>{{ $ts.showMore }}</span> <span>{{ $ts.showMore }}</span>
</button> </button>
@ -106,7 +106,7 @@ import * as mfm from 'mfm-js';
import { sum } from '../../../prelude/array'; import { sum } from '../../../prelude/array';
import XSub from './note.sub.vue'; import XSub from './note.sub.vue';
import XNoteHeader from './note-header.vue'; import XNoteHeader from './note-header.vue';
import XNotePreview from './note-preview.vue'; import XNoteSimple from './note-preview.vue';
import XReactionsViewer from '@client/components/reactions-viewer.vue'; import XReactionsViewer from '@client/components/reactions-viewer.vue';
import XMediaList from '@client/components/media-list.vue'; import XMediaList from '@client/components/media-list.vue';
import XCwButton from '@client/components/cw-button.vue'; import XCwButton from '@client/components/cw-button.vue';
@ -126,7 +126,7 @@ export default defineComponent({
components: { components: {
XSub, XSub,
XNoteHeader, XNoteHeader,
XNotePreview, XNoteSimple,
XReactionsViewer, XReactionsViewer,
XMediaList, XMediaList,
XCwButton, XCwButton,

View file

@ -37,6 +37,11 @@ import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownCo
import { deckStore } from './deck-store'; import { deckStore } from './deck-store';
export default defineComponent({ export default defineComponent({
provide: {
shouldHeaderThin: true,
shouldOmitHeaderTitle: true,
},
props: { props: {
column: { column: {
type: Object, type: Object,
@ -267,6 +272,7 @@ export default defineComponent({
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
contain: content; contain: content;
box-shadow: 0 0 8px 0 var(--shadow);
&.draghover { &.draghover {
box-shadow: 0 0 0 2px var(--focus); box-shadow: 0 0 0 2px var(--focus);
@ -320,15 +326,6 @@ export default defineComponent({
&.paged { &.paged {
background: var(--bg) !important; background: var(--bg) !important;
> header {
background: transparent;
box-shadow: none;
> button {
color: var(--fg);
}
}
} }
> header { > header {
@ -365,7 +362,7 @@ export default defineComponent({
} }
> .toggleActive, > .toggleActive,
> .action > *, > .action > ::v-deep(*),
> .menu { > .menu {
z-index: 1; z-index: 1;
width: var(--deckColumnHeaderHeight); width: var(--deckColumnHeaderHeight);

View file

@ -219,10 +219,20 @@ export function stackLeftColumn(id: Column['id']) {
export function popRightColumn(id: Column['id']) { export function popRightColumn(id: Column['id']) {
let layout = copy(deckStore.state.layout); let layout = copy(deckStore.state.layout);
const i = deckStore.state.layout.findIndex(ids => ids.includes(id)); const i = deckStore.state.layout.findIndex(ids => ids.includes(id));
const affected = layout[i];
layout = layout.map(ids => ids.filter(_id => _id !== id)); layout = layout.map(ids => ids.filter(_id => _id !== id));
layout.splice(i + 1, 0, [id]); layout.splice(i + 1, 0, [id]);
layout = layout.filter(ids => ids.length > 0); layout = layout.filter(ids => ids.length > 0);
deckStore.set('layout', layout); deckStore.set('layout', layout);
const columns = copy(deckStore.state.columns);
for (const column of columns) {
if (affected.includes(column.id)) {
column.active = true;
}
}
deckStore.set('columns', columns);
saveDeck(); saveDeck();
} }

View file

@ -2,6 +2,7 @@
<XColumn v-if="deckStore.state.alwaysShowMainColumn || $route.name !== 'index'" :column="column" :is-stacked="isStacked"> <XColumn v-if="deckStore.state.alwaysShowMainColumn || $route.name !== 'index'" :column="column" :is-stacked="isStacked">
<template #header> <template #header>
<template v-if="pageInfo"> <template v-if="pageInfo">
<i :class="pageInfo.icon"></i>
{{ pageInfo.title }} {{ pageInfo.title }}
</template> </template>
</template> </template>

View file

@ -80,6 +80,12 @@ export default defineComponent({
XWidgets: defineAsyncComponent(() => import('./default.widgets.vue')), XWidgets: defineAsyncComponent(() => import('./default.widgets.vue')),
}, },
provide() {
return {
shouldHeaderThin: this.showMenuOnTop,
};
},
data() { data() {
return { return {
pageInfo: null, pageInfo: null,

View file

@ -2,7 +2,7 @@
"short_name": "Misskey", "short_name": "Misskey",
"name": "Misskey", "name": "Misskey",
"start_url": "/", "start_url": "/",
"display": "standalone", "display": "minimal-ui",
"background_color": "#313a42", "background_color": "#313a42",
"theme_color": "#86b300", "theme_color": "#86b300",
"icons": [ "icons": [