summaryrefslogtreecommitdiff
path: root/src/client/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/components')
-rw-r--r--src/client/components/abuse-report-window.vue18
-rw-r--r--src/client/components/channel-preview.vue2
-rw-r--r--src/client/components/date-separated-list.vue31
-rw-r--r--src/client/components/drive.file.vue4
-rw-r--r--src/client/components/drive.vue7
-rw-r--r--src/client/components/emoji-picker-dialog.vue4
-rw-r--r--src/client/components/emoji-picker-window.vue4
-rw-r--r--src/client/components/emoji-picker.vue6
-rw-r--r--src/client/components/form/base.vue10
-rw-r--r--src/client/components/form/form.scss34
-rw-r--r--src/client/components/form/info.vue49
-rw-r--r--src/client/components/form/input.vue2
-rw-r--r--src/client/components/form/link.vue1
-rw-r--r--src/client/components/form/radios.vue4
-rw-r--r--src/client/components/form/range.vue2
-rw-r--r--src/client/components/form/select.vue2
-rw-r--r--src/client/components/form/switch.vue2
-rw-r--r--src/client/components/global/a.vue6
-rw-r--r--src/client/components/global/acct.vue15
-rw-r--r--src/client/components/global/url.vue2
-rw-r--r--src/client/components/launch-pad.vue2
-rw-r--r--src/client/components/media-image.vue2
-rw-r--r--src/client/components/mention.vue2
-rw-r--r--src/client/components/mfm.ts89
-rw-r--r--src/client/components/modal-page-window.vue213
-rw-r--r--src/client/components/note-detailed.vue44
-rw-r--r--src/client/components/note-header.vue2
-rw-r--r--src/client/components/note.sub.vue2
-rw-r--r--src/client/components/note.vue52
-rw-r--r--src/client/components/notes.vue4
-rw-r--r--src/client/components/notification-setting-window.vue24
-rw-r--r--src/client/components/notifications.vue1
-rw-r--r--src/client/components/page-preview.vue3
-rw-r--r--src/client/components/page-window.vue10
-rw-r--r--src/client/components/page/page.text.vue10
-rw-r--r--src/client/components/poll.vue2
-rw-r--r--src/client/components/post-form.vue12
-rw-r--r--src/client/components/remote-caution.vue2
-rw-r--r--src/client/components/sidebar.vue458
-rwxr-xr-xsrc/client/components/signin.vue10
-rw-r--r--src/client/components/signup-dialog.vue6
-rw-r--r--src/client/components/signup.vue2
-rw-r--r--src/client/components/taskmanager.api-window.vue2
-rw-r--r--src/client/components/taskmanager.vue4
-rw-r--r--src/client/components/timeline.vue8
-rw-r--r--src/client/components/ui/container.vue12
-rw-r--r--src/client/components/ui/folder.vue17
-rw-r--r--src/client/components/ui/info.vue4
-rw-r--r--src/client/components/ui/modal-window.vue11
-rw-r--r--src/client/components/ui/modal.vue8
-rw-r--r--src/client/components/ui/window.vue2
-rw-r--r--src/client/components/user-info.vue4
-rw-r--r--src/client/components/user-list.vue2
-rw-r--r--src/client/components/user-select-dialog.vue2
-rw-r--r--src/client/components/visibility-picker.vue2
55 files changed, 560 insertions, 675 deletions
diff --git a/src/client/components/abuse-report-window.vue b/src/client/components/abuse-report-window.vue
index 7dbb9657bd..df5b594c0b 100644
--- a/src/client/components/abuse-report-window.vue
+++ b/src/client/components/abuse-report-window.vue
@@ -8,19 +8,15 @@
</template>
</I18n>
</template>
- <div class="dpvffvvy">
+ <div class="dpvffvvy _monolithic_">
<div class="_section">
- <div class="_content">
- <MkTextarea v-model:value="comment">
- <span>{{ $ts.details }}</span>
- <template #desc>{{ $ts.fillAbuseReportDescription }}</template>
- </MkTextarea>
- </div>
+ <MkTextarea v-model:value="comment">
+ <span>{{ $ts.details }}</span>
+ <template #desc>{{ $ts.fillAbuseReportDescription }}</template>
+ </MkTextarea>
</div>
<div class="_section">
- <div class="_content">
- <MkButton @click="send" primary full :disabled="comment.length === 0">{{ $ts.send }}</MkButton>
- </div>
+ <MkButton @click="send" primary full :disabled="comment.length === 0">{{ $ts.send }}</MkButton>
</div>
</div>
</XWindow>
@@ -80,6 +76,6 @@ export default defineComponent({
<style lang="scss" scoped>
.dpvffvvy {
- --section-padding: 16px;
+ --root-margin: 16px;
}
</style>
diff --git a/src/client/components/channel-preview.vue b/src/client/components/channel-preview.vue
index e222ad7ae7..4dc633bcb7 100644
--- a/src/client/components/channel-preview.vue
+++ b/src/client/components/channel-preview.vue
@@ -123,7 +123,7 @@ export default defineComponent({
> footer {
padding: 12px 16px;
- border-top: solid 1px var(--divider);
+ border-top: solid 0.5px var(--divider);
> span {
opacity: 0.7;
diff --git a/src/client/components/date-separated-list.vue b/src/client/components/date-separated-list.vue
index be9f01ca1f..433655d6ed 100644
--- a/src/client/components/date-separated-list.vue
+++ b/src/client/components/date-separated-list.vue
@@ -37,14 +37,16 @@ export default defineComponent({
});
}
+ const noGap = [...document.querySelectorAll('._noGap_')].some(el => el.contains(this.$parent.$el));
+
return h(this.$store.state.animation ? TransitionGroup : 'div', this.$store.state.animation ? {
- class: 'sqadhkmv _list_',
+ class: 'sqadhkmv' + (noGap ? ' _block' : ''),
name: 'list',
tag: 'div',
'data-direction': this.direction,
'data-reversed': this.reversed ? 'true' : 'false',
} : {
- class: 'sqadhkmv _list_',
+ class: 'sqadhkmv',
}, this.items.map((item, i) => {
const el = this.$slots.default({
item: item
@@ -117,11 +119,7 @@ export default defineComponent({
transform: translateY(-64px);
}
}
-}
-</style>
-<style lang="scss">
-.sqadhkmv {
> .separator {
text-align: center;
@@ -155,4 +153,25 @@ export default defineComponent({
}
}
}
+
+._noGap_ .sqadhkmv {
+ > * {
+ margin: 0 !important;
+ border: none;
+ border-radius: 0;
+ box-shadow: none;
+
+ &:not(:last-child) {
+ border-bottom: solid 0.5px var(--divider);
+ }
+ }
+}
+
+._inContainer_ .sqadhkmv > * {
+ margin: 0 !important;
+ border: none;
+ border-bottom: solid 0.5px var(--divider);
+ border-radius: 0;
+ box-shadow: none;
+}
</style>
diff --git a/src/client/components/drive.file.vue b/src/client/components/drive.file.vue
index 03f2da008d..fb8b50d25a 100644
--- a/src/client/components/drive.file.vue
+++ b/src/client/components/drive.file.vue
@@ -330,8 +330,8 @@ export default defineComponent({
}
> .thumbnail {
- width: 128px;
- height: 128px;
+ width: 110px;
+ height: 110px;
margin: auto;
}
diff --git a/src/client/components/drive.vue b/src/client/components/drive.vue
index 150d0d8774..103ae9c11e 100644
--- a/src/client/components/drive.vue
+++ b/src/client/components/drive.vue
@@ -11,7 +11,7 @@
<span class="folder current" v-if="folder != null">{{ folder.name }}</span>
</div>
</nav>
- <div class="main _section" :class="{ uploading: uploadings.length > 0, fetching }"
+ <div class="main" :class="{ uploading: uploadings.length > 0, fetching }"
ref="main"
@dragover.prevent.stop="onDragover"
@dragenter="onDragenter"
@@ -704,6 +704,7 @@ export default defineComponent({
> .main {
flex: 1;
overflow: auto;
+ padding: var(--margin);
&, * {
user-select: none;
@@ -735,7 +736,7 @@ export default defineComponent({
> .folder,
> .file {
flex-grow: 1;
- width: 144px;
+ width: 128px;
margin: 4px;
box-sizing: border-box;
}
@@ -743,7 +744,7 @@ export default defineComponent({
> .padding {
flex-grow: 1;
pointer-events: none;
- width: 144px + 8px;
+ width: 128px + 8px;
}
}
diff --git a/src/client/components/emoji-picker-dialog.vue b/src/client/components/emoji-picker-dialog.vue
index 5bdbc330ad..c4b12e2f61 100644
--- a/src/client/components/emoji-picker-dialog.vue
+++ b/src/client/components/emoji-picker-dialog.vue
@@ -123,7 +123,7 @@ export default defineComponent({
> .index {
min-height: var(--height);
position: relative;
- border-bottom: solid 1px var(--divider);
+ border-bottom: solid 0.5px var(--divider);
> .arrow {
position: absolute;
@@ -181,7 +181,7 @@ export default defineComponent({
}
&.result {
- border-bottom: solid 1px var(--divider);
+ border-bottom: solid 0.5px var(--divider);
&:empty {
display: none;
diff --git a/src/client/components/emoji-picker-window.vue b/src/client/components/emoji-picker-window.vue
index 5504eaecd6..53b6ae6b32 100644
--- a/src/client/components/emoji-picker-window.vue
+++ b/src/client/components/emoji-picker-window.vue
@@ -119,7 +119,7 @@ export default defineComponent({
> .index {
min-height: var(--height);
position: relative;
- border-bottom: solid 1px var(--divider);
+ border-bottom: solid 0.5px var(--divider);
> .arrow {
position: absolute;
@@ -177,7 +177,7 @@ export default defineComponent({
}
&.result {
- border-bottom: solid 1px var(--divider);
+ border-bottom: solid 0.5px var(--divider);
&:empty {
display: none;
diff --git a/src/client/components/emoji-picker.vue b/src/client/components/emoji-picker.vue
index 573833b9d3..a212c15049 100644
--- a/src/client/components/emoji-picker.vue
+++ b/src/client/components/emoji-picker.vue
@@ -402,7 +402,7 @@ export default defineComponent({
> .tab {
flex: 1;
height: 38px;
- border-top: solid 1px var(--divider);
+ border-top: solid 0.5px var(--divider);
&.active {
border-top: solid 1px var(--accent);
@@ -425,7 +425,7 @@ export default defineComponent({
> div {
&:not(.index) {
padding: 4px 0 8px 0;
- border-top: solid 1px var(--divider);
+ border-top: solid 0.5px var(--divider);
}
> header {
@@ -492,7 +492,7 @@ export default defineComponent({
}
&.result {
- border-bottom: solid 1px var(--divider);
+ border-bottom: solid 0.5px var(--divider);
&:empty {
display: none;
diff --git a/src/client/components/form/base.vue b/src/client/components/form/base.vue
index 249b49c675..84438a5b32 100644
--- a/src/client/components/form/base.vue
+++ b/src/client/components/form/base.vue
@@ -20,12 +20,16 @@ export default defineComponent({
<style lang="scss" scoped>
.rbusrurv {
- line-height: 1.4em;
+ // 他のCSSからも参照されるので消さないように
+ --formXPadding: 32px;
+ --formYPadding: 32px;
+
+ line-height: 1.3em;
background: var(--bg);
- padding: 32px;
+ padding: var(--formYPadding) var(--formXPadding);
&:not(.wide).max-width_400px {
- padding: 32px 0;
+ --formXPadding: 0px;
> ::v-deep(*) {
._formPanel {
diff --git a/src/client/components/form/form.scss b/src/client/components/form/form.scss
index c7f4373544..8c01fad727 100644
--- a/src/client/components/form/form.scss
+++ b/src/client/components/form/form.scss
@@ -1,32 +1,48 @@
._formPanel {
background: var(--panel);
border-radius: var(--radius);
+ transition: background 0.2s ease;
&._formClickable {
&:hover {
//background: var(--panelHighlight);
}
+
+ &:active {
+ background: var(--panelHighlight);
+ transition: background 0s;
+ }
}
}
-._formLabel {
+._formLabel,
+._formCaption {
font-size: 80%;
- padding: 0 16px 8px 16px;
- opacity: 0.8;
+ color: var(--fgTransparentWeak);
&:empty {
display: none;
}
}
+._formLabel {
+ position: sticky;
+ top: var(--stickyTop, 0px);
+ z-index: 2;
+ margin: -8px calc(var(--formXPadding) * -1) 0 calc(var(--formXPadding) * -1);
+ padding: 8px calc(16px + var(--formXPadding)) 8px calc(16px + var(--formXPadding));
+ background: var(--X17);
+ -webkit-backdrop-filter: blur(10px);
+ backdrop-filter: blur(10px);
+}
+
+._themeChanging_ ._formLabel {
+ transition: none !important;
+ background: transparent;
+}
+
._formCaption {
- font-size: 80%;
padding: 8px 16px 0 16px;
- opacity: 0.8;
-
- &:empty {
- display: none;
- }
}
._formItem {
diff --git a/src/client/components/form/info.vue b/src/client/components/form/info.vue
new file mode 100644
index 0000000000..a9224c7e65
--- /dev/null
+++ b/src/client/components/form/info.vue
@@ -0,0 +1,49 @@
+<template>
+<div class="fzenkabp _formItem">
+ <div class="_formPanel" :class="{ warn }">
+ <i v-if="warn"><Fa :icon="faExclamationTriangle"/></i>
+ <i v-else><Fa :icon="faInfoCircle"/></i>
+ <slot></slot>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { faInfoCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
+
+export default defineComponent({
+ props: {
+ warn: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ },
+ data() {
+ return {
+ faInfoCircle, faExclamationTriangle
+ };
+ }
+});
+</script>
+
+<style lang="scss" scoped>
+.fzenkabp {
+ > div {
+ padding: 14px 16px;
+ font-size: 90%;
+ background: var(--infoBg);
+ color: var(--infoFg);
+
+ &.warn {
+ background: var(--infoWarnBg);
+ color: var(--infoWarnFg);
+ }
+
+ > i {
+ margin-right: 4px;
+ }
+ }
+}
+</style>
diff --git a/src/client/components/form/input.vue b/src/client/components/form/input.vue
index f0aa6b0534..c0fa3e716e 100644
--- a/src/client/components/form/input.vue
+++ b/src/client/components/form/input.vue
@@ -215,7 +215,7 @@ export default defineComponent({
}
> .input {
- $height: 52px;
+ $height: 48px;
position: relative;
> input {
diff --git a/src/client/components/form/link.vue b/src/client/components/form/link.vue
index 2efc6b58c9..af36bcf22c 100644
--- a/src/client/components/form/link.vue
+++ b/src/client/components/form/link.vue
@@ -66,6 +66,7 @@ export default defineComponent({
&.active {
color: var(--accent);
+ background: var(--panelHighlight);
}
> .icon {
diff --git a/src/client/components/form/radios.vue b/src/client/components/form/radios.vue
index 4561df32e1..3daa7e5bbd 100644
--- a/src/client/components/form/radios.vue
+++ b/src/client/components/form/radios.vue
@@ -69,8 +69,8 @@ export default defineComponent({
display: inline-block;
vertical-align: bottom;
position: relative;
- width: 20px;
- height: 20px;
+ width: 16px;
+ height: 16px;
margin-right: 8px;
background: none;
border: 2px solid var(--inputBorder);
diff --git a/src/client/components/form/range.vue b/src/client/components/form/range.vue
index 3452184c55..65d665c70a 100644
--- a/src/client/components/form/range.vue
+++ b/src/client/components/form/range.vue
@@ -69,7 +69,7 @@ export default defineComponent({
position: relative;
> .main {
- padding: 24px 16px;
+ padding: 22px 16px;
> input {
display: block;
diff --git a/src/client/components/form/select.vue b/src/client/components/form/select.vue
index b865372f56..01f28587dc 100644
--- a/src/client/components/form/select.vue
+++ b/src/client/components/form/select.vue
@@ -97,7 +97,7 @@ export default defineComponent({
font: inherit;
font-weight: normal;
font-size: 1em;
- height: 52px;
+ height: 48px;
background: none;
border: none;
border-radius: 0;
diff --git a/src/client/components/form/switch.vue b/src/client/components/form/switch.vue
index a2941c5996..e7ef714c49 100644
--- a/src/client/components/form/switch.vue
+++ b/src/client/components/form/switch.vue
@@ -57,7 +57,7 @@ export default defineComponent({
> .main {
position: relative;
display: flex;
- padding: 16px;
+ padding: 14px 16px;
cursor: pointer;
> * {
diff --git a/src/client/components/global/a.vue b/src/client/components/global/a.vue
index a8a597b2bb..7ad62a7310 100644
--- a/src/client/components/global/a.vue
+++ b/src/client/components/global/a.vue
@@ -93,6 +93,10 @@ export default defineComponent({
os.pageWindow(this.to);
},
+ modalWindow() {
+ os.modalPageWindow(this.to);
+ },
+
popout() {
popout(this.to);
},
@@ -111,6 +115,8 @@ export default defineComponent({
if (this.behavior) {
if (this.behavior === 'window') {
return this.window();
+ } else if (this.behavior === 'modalWindow') {
+ return this.modalWindow();
}
}
diff --git a/src/client/components/global/acct.vue b/src/client/components/global/acct.vue
index a969636a7e..70f2954cb0 100644
--- a/src/client/components/global/acct.vue
+++ b/src/client/components/global/acct.vue
@@ -1,5 +1,5 @@
<template>
-<span class="mk-acct" v-once>
+<span class="mk-acct">
<span class="name">@{{ user.username }}</span>
<span class="host" v-if="user.host || detail || $store.state.showFullAcct">@{{ user.host || host }}</span>
</span>
@@ -7,11 +7,20 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import { toUnicode } from 'punycode';
+import { toUnicode } from 'punycode/';
import { host } from '@client/config';
export default defineComponent({
- props: ['user', 'detail'],
+ props: {
+ user: {
+ type: Object,
+ required: true
+ },
+ detail: {
+ type: Boolean,
+ default: false
+ },
+ },
data() {
return {
host: toUnicode(host),
diff --git a/src/client/components/global/url.vue b/src/client/components/global/url.vue
index c89536ebdb..f68a3c00be 100644
--- a/src/client/components/global/url.vue
+++ b/src/client/components/global/url.vue
@@ -22,7 +22,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
-import { toUnicode as decodePunycode } from 'punycode';
+import { toUnicode as decodePunycode } from 'punycode/';
import { url as local } from '@client/config';
import { isDeviceTouch } from '@client/scripts/is-device-touch';
import * as os from '@client/os';
diff --git a/src/client/components/launch-pad.vue b/src/client/components/launch-pad.vue
index a81320954c..7610b44eb5 100644
--- a/src/client/components/launch-pad.vue
+++ b/src/client/components/launch-pad.vue
@@ -146,7 +146,7 @@ export default defineComponent({
> .sub {
margin-top: 8px;
padding-top: 8px;
- border-top: solid 1px var(--divider);
+ border-top: solid 0.5px var(--divider);
}
}
</style>
diff --git a/src/client/components/media-image.vue b/src/client/components/media-image.vue
index 41760d98d7..4de5daa84f 100644
--- a/src/client/components/media-image.vue
+++ b/src/client/components/media-image.vue
@@ -123,7 +123,7 @@ export default defineComponent({
.gqnyydlz {
position: relative;
- border: solid 1px var(--divider);
+ border: solid 0.5px var(--divider);
> i {
display: block;
diff --git a/src/client/components/mention.vue b/src/client/components/mention.vue
index 322e56d957..b9bd6b320b 100644
--- a/src/client/components/mention.vue
+++ b/src/client/components/mention.vue
@@ -16,7 +16,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
-import { toUnicode } from 'punycode';
+import { toUnicode } from 'punycode/';
import { host as localHost } from '@client/config';
import { wellKnownServices } from '../../well-known-services';
import * as os from '@client/os';
diff --git a/src/client/components/mfm.ts b/src/client/components/mfm.ts
index 28ac9b8942..b8e948a188 100644
--- a/src/client/components/mfm.ts
+++ b/src/client/components/mfm.ts
@@ -1,6 +1,5 @@
import { VNode, defineComponent, h } from 'vue';
-import { MfmForest } from '@client/../mfm/prelude';
-import { parse, parsePlain } from '@client/../mfm/parse';
+import * as mfm from 'mfm-js';
import MkUrl from '@client/components/global/url.vue';
import MkLink from '@client/components/link.vue';
import MkMention from '@client/components/mention.vue';
@@ -46,17 +45,17 @@ export default defineComponent({
render() {
if (this.text == null || this.text == '') return;
- const ast = (this.plain ? parsePlain : parse)(this.text);
+ const ast = (this.plain ? mfm.parsePlain : mfm.parse)(this.text);
const validTime = (t: string | null | undefined) => {
if (t == null) return null;
return t.match(/^[0-9.]+s$/) ? t : null;
};
- const genEl = (ast: MfmForest) => concat(ast.map((token): VNode[] => {
- switch (token.node.type) {
+ const genEl = (ast: mfm.MfmNode[]) => concat(ast.map((token): VNode[] => {
+ switch (token.type) {
case 'text': {
- const text = token.node.props.text.replace(/(\r\n|\n|\r)/g, '\n');
+ const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
if (!this.plain) {
const x = text.split('\n')
@@ -83,38 +82,38 @@ export default defineComponent({
}
case 'fn': {
- // TODO: CSSを文字列で組み立てていくと token.node.props.args.~~~ 経由でCSSインジェクションできるのでよしなにやる
+ // TODO: CSSを文字列で組み立てていくと token.props.args.~~~ 経由でCSSインジェクションできるのでよしなにやる
let style;
- switch (token.node.props.name) {
+ switch (token.props.name) {
case 'tada': {
style = `font-size: 150%;` + (this.$store.state.animatedMfm ? 'animation: tada 1s linear infinite both;' : '');
break;
}
case 'jelly': {
- const speed = validTime(token.node.props.args.speed) || '1s';
+ const speed = validTime(token.props.args.speed) || '1s';
style = (this.$store.state.animatedMfm ? `animation: mfm-rubberBand ${speed} linear infinite both;` : '');
break;
}
case 'twitch': {
- const speed = validTime(token.node.props.args.speed) || '0.5s';
+ const speed = validTime(token.props.args.speed) || '0.5s';
style = this.$store.state.animatedMfm ? `animation: mfm-twitch ${speed} ease infinite;` : '';
break;
}
case 'shake': {
- const speed = validTime(token.node.props.args.speed) || '0.5s';
+ const speed = validTime(token.props.args.speed) || '0.5s';
style = this.$store.state.animatedMfm ? `animation: mfm-shake ${speed} ease infinite;` : '';
break;
}
case 'spin': {
const direction =
- token.node.props.args.left ? 'reverse' :
- token.node.props.args.alternate ? 'alternate' :
+ token.props.args.left ? 'reverse' :
+ token.props.args.alternate ? 'alternate' :
'normal';
const anime =
- token.node.props.args.x ? 'mfm-spinX' :
- token.node.props.args.y ? 'mfm-spinY' :
+ token.props.args.x ? 'mfm-spinX' :
+ token.props.args.y ? 'mfm-spinY' :
'mfm-spin';
- const speed = validTime(token.node.props.args.speed) || '1.5s';
+ const speed = validTime(token.props.args.speed) || '1.5s';
style = this.$store.state.animatedMfm ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : '';
break;
}
@@ -128,8 +127,8 @@ export default defineComponent({
}
case 'flip': {
const transform =
- (token.node.props.args.h && token.node.props.args.v) ? 'scale(-1, -1)' :
- token.node.props.args.v ? 'scaleY(-1)' :
+ (token.props.args.h && token.props.args.v) ? 'scale(-1, -1)' :
+ token.props.args.v ? 'scaleY(-1)' :
'scaleX(-1)';
style = `transform: ${transform};`;
break;
@@ -148,12 +147,12 @@ export default defineComponent({
}
case 'font': {
const family =
- token.node.props.args.serif ? 'serif' :
- token.node.props.args.monospace ? 'monospace' :
- token.node.props.args.cursive ? 'cursive' :
- token.node.props.args.fantasy ? 'fantasy' :
- token.node.props.args.emoji ? 'emoji' :
- token.node.props.args.math ? 'math' :
+ token.props.args.serif ? 'serif' :
+ token.props.args.monospace ? 'monospace' :
+ token.props.args.cursive ? 'cursive' :
+ token.props.args.fantasy ? 'fantasy' :
+ token.props.args.emoji ? 'emoji' :
+ token.props.args.math ? 'math' :
null;
if (family) style = `font-family: ${family};`;
break;
@@ -165,7 +164,7 @@ export default defineComponent({
}
}
if (style == null) {
- return h('span', {}, ['[', token.node.props.name, ...genEl(token.children), ']']);
+ return h('span', {}, ['[', token.props.name, ...genEl(token.children), ']']);
} else {
return h('span', {
style: 'display: inline-block;' + style,
@@ -188,7 +187,7 @@ export default defineComponent({
case 'url': {
return [h(MkUrl, {
key: Math.random(),
- url: token.node.props.url,
+ url: token.props.url,
rel: 'nofollow noopener',
})];
}
@@ -196,7 +195,7 @@ export default defineComponent({
case 'link': {
return [h(MkLink, {
key: Math.random(),
- url: token.node.props.url,
+ url: token.props.url,
rel: 'nofollow noopener',
}, genEl(token.children))];
}
@@ -204,32 +203,31 @@ export default defineComponent({
case 'mention': {
return [h(MkMention, {
key: Math.random(),
- host: (token.node.props.host == null && this.author && this.author.host != null ? this.author.host : token.node.props.host) || host,
- username: token.node.props.username
+ host: (token.props.host == null && this.author && this.author.host != null ? this.author.host : token.props.host) || host,
+ username: token.props.username
})];
}
case 'hashtag': {
return [h(MkA, {
key: Math.random(),
- to: this.isNote ? `/tags/${encodeURIComponent(token.node.props.hashtag)}` : `/explore/tags/${encodeURIComponent(token.node.props.hashtag)}`,
+ to: this.isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/explore/tags/${encodeURIComponent(token.props.hashtag)}`,
style: 'color:var(--hashtag);'
- }, `#${token.node.props.hashtag}`)];
+ }, `#${token.props.hashtag}`)];
}
case 'blockCode': {
return [h(MkCode, {
key: Math.random(),
- code: token.node.props.code,
- lang: token.node.props.lang,
+ code: token.props.code,
+ lang: token.props.lang,
})];
}
case 'inlineCode': {
return [h(MkCode, {
key: Math.random(),
- code: token.node.props.code,
- lang: token.node.props.lang,
+ code: token.props.code,
inline: true
})];
}
@@ -246,10 +244,19 @@ export default defineComponent({
}
}
- case 'emoji': {
+ case 'emojiCode': {
return [h(MkEmoji, {
key: Math.random(),
- emoji: token.node.props.name ? `:${token.node.props.name}:` : token.node.props.emoji,
+ emoji: `:${token.props.name}:`,
+ customEmojis: this.customEmojis,
+ normal: this.plain
+ })];
+ }
+
+ case 'unicodeEmoji': {
+ return [h(MkEmoji, {
+ key: Math.random(),
+ emoji: token.props.emoji,
customEmojis: this.customEmojis,
normal: this.plain
})];
@@ -258,7 +265,7 @@ export default defineComponent({
case 'mathInline': {
return [h(MkFormula, {
key: Math.random(),
- formula: token.node.props.formula,
+ formula: token.props.formula,
block: false
})];
}
@@ -266,7 +273,7 @@ export default defineComponent({
case 'mathBlock': {
return [h(MkFormula, {
key: Math.random(),
- formula: token.node.props.formula,
+ formula: token.props.formula,
block: true
})];
}
@@ -274,12 +281,12 @@ export default defineComponent({
case 'search': {
return [h(MkGoogle, {
key: Math.random(),
- q: token.node.props.query
+ q: token.props.query
})];
}
default: {
- console.error('unrecognized ast type:', token.node.type);
+ console.error('unrecognized ast type:', token.type);
return [];
}
diff --git a/src/client/components/modal-page-window.vue b/src/client/components/modal-page-window.vue
new file mode 100644
index 0000000000..474a67f985
--- /dev/null
+++ b/src/client/components/modal-page-window.vue
@@ -0,0 +1,213 @@
+<template>
+<MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')">
+ <div class="hrmcaedk _popup _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }">
+ <div class="header">
+ <button class="_button" @click="back()" v-if="history.length > 0"><Fa :icon="faChevronLeft"/></button>
+ <button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button>
+ <span class="title">
+ <XHeader :info="pageInfo" :with-back="false"/>
+ </span>
+ <button class="_button" @click="$refs.modal.close()"><Fa :icon="faTimes"/></button>
+ </div>
+ <div class="body _flat_">
+ <keep-alive>
+ <component :is="component" v-bind="props" :ref="changePage"/>
+ </keep-alive>
+ </div>
+ </div>
+</MkModal>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { faExternalLinkAlt, faExpandAlt, faLink, faChevronLeft, faColumns, faTimes } from '@fortawesome/free-solid-svg-icons';
+import MkModal from '@client/components/ui/modal.vue';
+import XHeader from '@client/ui/_common_/header.vue';
+import { popout } from '@client/scripts/popout';
+import copyToClipboard from '@client/scripts/copy-to-clipboard';
+import { resolve } from '@client/router';
+import { url } from '@client/config';
+import * as symbols from '@client/symbols';
+
+export default defineComponent({
+ components: {
+ MkModal,
+ XHeader,
+ },
+
+ inject: {
+ sideViewHook: {
+ default: null
+ }
+ },
+
+ provide() {
+ return {
+ navHook: (path) => {
+ this.navigate(path);
+ }
+ };
+ },
+
+ props: {
+ initialPath: {
+ type: String,
+ required: true,
+ },
+ initialComponent: {
+ type: Object,
+ required: true,
+ },
+ initialProps: {
+ type: Object,
+ required: false,
+ default: () => {},
+ },
+ },
+
+ emits: ['closed'],
+
+ data() {
+ return {
+ width: 860,
+ height: 660,
+ pageInfo: null,
+ path: this.initialPath,
+ component: this.initialComponent,
+ props: this.initialProps,
+ history: [],
+ faChevronLeft, faTimes,
+ };
+ },
+
+ computed: {
+ url(): string {
+ return url + this.path;
+ },
+
+ contextmenu() {
+ return [{
+ type: 'label',
+ text: this.path,
+ }, {
+ icon: faExpandAlt,
+ text: this.$ts.showInPage,
+ action: this.expand
+ }, this.sideViewHook ? {
+ icon: faColumns,
+ text: this.$ts.openInSideView,
+ action: () => {
+ this.sideViewHook(this.path);
+ this.$refs.window.close();
+ }
+ } : undefined, {
+ icon: faExternalLinkAlt,
+ text: this.$ts.popout,
+ action: this.popout
+ }, null, {
+ icon: faExternalLinkAlt,
+ text: this.$ts.openInNewTab,
+ action: () => {
+ window.open(this.url, '_blank');
+ this.$refs.window.close();
+ }
+ }, {
+ icon: faLink,
+ text: this.$ts.copyLink,
+ action: () => {
+ copyToClipboard(this.url);
+ }
+ }];
+ },
+ },
+
+ methods: {
+ changePage(page) {
+ if (page == null) return;
+ if (page[symbols.PAGE_INFO]) {
+ this.pageInfo = page[symbols.PAGE_INFO];
+ }
+ },
+
+ navigate(path, record = true) {
+ if (record) this.history.push(this.path);
+ this.path = path;
+ const { component, props } = resolve(path);
+ this.component = component;
+ this.props = props;
+ },
+
+ back() {
+ this.navigate(this.history.pop(), false);
+ },
+
+ expand() {
+ this.$router.push(this.path);
+ this.$refs.window.close();
+ },
+
+ popout() {
+ popout(this.path, this.$el);
+ this.$refs.window.close();
+ },
+ },
+});
+</script>
+
+<style lang="scss" scoped>
+.hrmcaedk {
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ contain: content;
+
+ --root-margin: 24px;
+
+ @media (max-width: 500px) {
+ --root-margin: 16px;
+ }
+
+ > .header {
+ $height: 52px;
+ $height-narrow: 42px;
+ display: flex;
+ flex-shrink: 0;
+ box-shadow: 0px 1px var(--divider);
+
+ > button {
+ height: $height;
+ width: $height;
+
+ @media (max-width: 500px) {
+ height: $height-narrow;
+ width: $height-narrow;
+ }
+ }
+
+ > .title {
+ flex: 1;
+ line-height: $height;
+ padding-left: 32px;
+ font-weight: bold;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ pointer-events: none;
+
+ @media (max-width: 500px) {
+ line-height: $height-narrow;
+ padding-left: 16px;
+ }
+ }
+
+ > button + .title {
+ padding-left: 0;
+ }
+ }
+
+ > .body {
+ overflow: auto;
+ background: var(--bg);
+ }
+}
+</style>
diff --git a/src/client/components/note-detailed.vue b/src/client/components/note-detailed.vue
index fb4f9502b3..b25c97543b 100644
--- a/src/client/components/note-detailed.vue
+++ b/src/client/components/note-detailed.vue
@@ -1,6 +1,6 @@
<template>
<div
- class="note _panel"
+ class="note _block"
v-if="!muted"
v-show="!isDeleted"
:tabindex="!isDeleted ? '-1' : null"
@@ -120,11 +120,11 @@
</template>
<script lang="ts">
-import { computed, defineAsyncComponent, defineComponent, markRaw, ref } from 'vue';
-import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug, faExclamationCircle, faPaperclip } from '@fortawesome/free-solid-svg-icons';
+import { defineAsyncComponent, defineComponent, markRaw } from 'vue';
+import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug, faExclamationCircle, faPaperclip, faShareAlt } from '@fortawesome/free-solid-svg-icons';
import { faCopy, faTrashAlt, faEdit, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
-import { parse } from '../../mfm/parse';
-import { sum, unique } from '../../prelude/array';
+import * as mfm from 'mfm-js';
+import { sum } from '../../prelude/array';
import XSub from './note.sub.vue';
import XNoteHeader from './note-header.vue';
import XNotePreview from './note-preview.vue';
@@ -141,6 +141,7 @@ import { userPage } from '@client/filters/user';
import * as os from '@client/os';
import { noteActions, noteViewInterruptors } from '@client/store';
import { reactionPicker } from '@client/scripts/reaction-picker';
+import { extractUrlFromMfm } from '@/misc/extract-url-from-mfm';
function markRawAll(...xs) {
for (const x of xs) {
@@ -252,21 +253,7 @@ export default defineComponent({
urls(): string[] {
if (this.appearNote.text) {
- const ast = parse(this.appearNote.text);
- // TODO: 再帰的にURL要素がないか調べる
- const urls = unique(ast
- .filter(t => ((t.node.type == 'url' || t.node.type == 'link') && t.node.props.url && !t.node.props.silent))
- .map(t => t.node.props.url));
-
- // unique without hash
- // [ http://a/#1, http://a/#2, http://b/#3 ] => [ http://a/#1, http://b/#3 ]
- const removeHash = x => x.replace(/#[^#]*$/, '');
-
- return urls.reduce((array, url) => {
- const removed = removeHash(url);
- if (!array.map(x => removeHash(x)).includes(removed)) array.push(url);
- return array;
- }, []);
+ return extractUrlFromMfm(mfm.parse(this.appearNote.text));
} else {
return null;
}
@@ -638,6 +625,11 @@ export default defineComponent({
window.open(this.appearNote.url || this.appearNote.uri, '_blank');
}
} : undefined,
+ {
+ icon: faShareAlt,
+ text: this.$ts.share,
+ action: this.share
+ },
null,
statePromise.then(state => state.isFavorited ? {
icon: faStar,
@@ -863,6 +855,14 @@ export default defineComponent({
});
},
+ share() {
+ navigator.share({
+ title: this.$t('noteOf', { user: this.appearNote.user.name }),
+ text: this.appearNote.text,
+ url: `${url}/notes/${this.appearNote.id}`
+ });
+ },
+
focus() {
this.$el.focus();
},
@@ -1020,7 +1020,7 @@ export default defineComponent({
margin: 0 0.5em;
padding: 4px 6px;
font-size: 80%;
- border: solid 1px var(--divider);
+ border: solid 0.5px var(--divider);
border-radius: 4px;
}
@@ -1123,7 +1123,7 @@ export default defineComponent({
}
> .reply {
- border-top: solid 1px var(--divider);
+ border-top: solid 0.5px var(--divider);
}
&.max-width_500px {
diff --git a/src/client/components/note-header.vue b/src/client/components/note-header.vue
index a6e9b6fe56..ab40c5fd4a 100644
--- a/src/client/components/note-header.vue
+++ b/src/client/components/note-header.vue
@@ -78,7 +78,7 @@ export default defineComponent({
margin: 0 .5em 0 0;
padding: 1px 6px;
font-size: 80%;
- border: solid 1px var(--divider);
+ border: solid 0.5px var(--divider);
border-radius: 3px;
}
diff --git a/src/client/components/note.sub.vue b/src/client/components/note.sub.vue
index 853d481406..899c4b2f16 100644
--- a/src/client/components/note.sub.vue
+++ b/src/client/components/note.sub.vue
@@ -139,7 +139,7 @@ export default defineComponent({
}
> .reply {
- border-left: solid 1px var(--divider);
+ border-left: solid 0.5px var(--divider);
margin-top: 10px;
}
}
diff --git a/src/client/components/note.vue b/src/client/components/note.vue
index b54cadfc80..0e153033ca 100644
--- a/src/client/components/note.vue
+++ b/src/client/components/note.vue
@@ -1,6 +1,6 @@
<template>
<div
- class="tkcbzcuz _panel"
+ class="tkcbzcuz"
v-if="!muted"
v-show="!isDeleted"
:tabindex="!isDeleted ? '-1' : null"
@@ -90,7 +90,7 @@
</div>
</article>
</div>
-<div v-else class="_panel muted" @click="muted = false">
+<div v-else class="muted" @click="muted = false">
<I18n :src="$ts.userSaysSomething" tag="small">
<template #name>
<MkA class="name" :to="userPage(appearNote.user)" v-user-preview="appearNote.userId">
@@ -102,11 +102,11 @@
</template>
<script lang="ts">
-import { computed, defineAsyncComponent, defineComponent, markRaw, ref } from 'vue';
-import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug, faExclamationCircle, faPaperclip } from '@fortawesome/free-solid-svg-icons';
+import { defineAsyncComponent, defineComponent, markRaw } from 'vue';
+import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug, faExclamationCircle, faPaperclip, faShareAlt } from '@fortawesome/free-solid-svg-icons';
import { faCopy, faTrashAlt, faEdit, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
-import { parse } from '../../mfm/parse';
-import { sum, unique } from '../../prelude/array';
+import * as mfm from 'mfm-js';
+import { sum } from '../../prelude/array';
import XSub from './note.sub.vue';
import XNoteHeader from './note-header.vue';
import XNotePreview from './note-preview.vue';
@@ -123,6 +123,7 @@ import { userPage } from '@client/filters/user';
import * as os from '@client/os';
import { noteActions, noteViewInterruptors } from '@client/store';
import { reactionPicker } from '@client/scripts/reaction-picker';
+import { extractUrlFromMfm } from '@/misc/extract-url-from-mfm';
function markRawAll(...xs) {
for (const x of xs) {
@@ -238,21 +239,7 @@ export default defineComponent({
urls(): string[] {
if (this.appearNote.text) {
- const ast = parse(this.appearNote.text);
- // TODO: 再帰的にURL要素がないか調べる
- const urls = unique(ast
- .filter(t => ((t.node.type == 'url' || t.node.type == 'link') && t.node.props.url && !t.node.props.silent))
- .map(t => t.node.props.url));
-
- // unique without hash
- // [ http://a/#1, http://a/#2, http://b/#3 ] => [ http://a/#1, http://b/#3 ]
- const removeHash = x => x.replace(/#[^#]*$/, '');
-
- return urls.reduce((array, url) => {
- const removed = removeHash(url);
- if (!array.map(x => removeHash(x)).includes(removed)) array.push(url);
- return array;
- }, []);
+ return extractUrlFromMfm(mfm.parse(this.appearNote.text));
} else {
return null;
}
@@ -613,6 +600,11 @@ export default defineComponent({
window.open(this.appearNote.url || this.appearNote.uri, '_blank');
}
} : undefined,
+ {
+ icon: faShareAlt,
+ text: this.$ts.share,
+ action: this.share
+ },
null,
statePromise.then(state => state.isFavorited ? {
icon: faStar,
@@ -838,6 +830,14 @@ export default defineComponent({
});
},
+ share() {
+ navigator.share({
+ title: this.$t('noteOf', { user: this.appearNote.user.name }),
+ text: this.appearNote.text,
+ url: `${url}/notes/${this.appearNote.id}`
+ });
+ },
+
focus() {
this.$el.focus();
},
@@ -863,7 +863,7 @@ export default defineComponent({
.tkcbzcuz {
position: relative;
transition: box-shadow 0.1s ease;
- overflow: hidden;
+ overflow: clip;
contain: content;
// これらの指定はパフォーマンス向上には有効だが、ノートの高さは一定でないため、
@@ -994,11 +994,12 @@ export default defineComponent({
> .avatar {
flex-shrink: 0;
display: block;
- //position: sticky;
- //top: 72px;
margin: 0 14px 8px 0;
width: 58px;
height: 58px;
+ position: sticky;
+ top: calc(22px + var(--stickyTop, 0px));
+ left: 0;
}
> .main {
@@ -1119,7 +1120,7 @@ export default defineComponent({
}
> .reply {
- border-top: solid 1px var(--divider);
+ border-top: solid 0.5px var(--divider);
}
&.max-width_500px {
@@ -1142,6 +1143,7 @@ export default defineComponent({
margin: 0 10px 8px 0;
width: 50px;
height: 50px;
+ top: calc(14px + var(--stickyTop, 0px));
}
}
}
diff --git a/src/client/components/notes.vue b/src/client/components/notes.vue
index 80a9502d5f..aedf11bc40 100644
--- a/src/client/components/notes.vue
+++ b/src/client/components/notes.vue
@@ -1,5 +1,5 @@
<template>
-<div class="_list_">
+<div>
<div class="_fullinfo" v-if="empty">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $ts.noNotes }}</div>
@@ -15,7 +15,7 @@
</div>
<XList ref="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed">
- <XNote :note="note" @update:note="updated(note, $event)" :key="note._featuredId_ || note._prId_ || note.id"/>
+ <XNote :note="note" class="_block" @update:note="updated(note, $event)" :key="note._featuredId_ || note._prId_ || note.id"/>
</XList>
<div v-show="more && !reversed" style="margin-top: var(--margin);">
diff --git a/src/client/components/notification-setting-window.vue b/src/client/components/notification-setting-window.vue
index a3e221f7b2..5f16c042bf 100644
--- a/src/client/components/notification-setting-window.vue
+++ b/src/client/components/notification-setting-window.vue
@@ -9,17 +9,19 @@
@closed="$emit('closed')"
>
<template #header>{{ $ts.notificationSetting }}</template>
- <div v-if="showGlobalToggle" class="_section">
- <MkSwitch v-model:value="useGlobalSetting">
- {{ $ts.useGlobalSetting }}
- <template #desc>{{ $ts.useGlobalSettingDesc }}</template>
- </MkSwitch>
- </div>
- <div v-if="!useGlobalSetting" class="_section">
- <MkInfo>{{ $ts.notificationSettingDesc }}</MkInfo>
- <MkButton inline @click="disableAll">{{ $ts.disableAll }}</MkButton>
- <MkButton inline @click="enableAll">{{ $ts.enableAll }}</MkButton>
- <MkSwitch v-for="type in notificationTypes" :key="type" v-model:value="typesMap[type]">{{ $t(`_notification._types.${type}`) }}</MkSwitch>
+ <div class="_monolithic_">
+ <div v-if="showGlobalToggle" class="_section">
+ <MkSwitch v-model:value="useGlobalSetting">
+ {{ $ts.useGlobalSetting }}
+ <template #desc>{{ $ts.useGlobalSettingDesc }}</template>
+ </MkSwitch>
+ </div>
+ <div v-if="!useGlobalSetting" class="_section">
+ <MkInfo>{{ $ts.notificationSettingDesc }}</MkInfo>
+ <MkButton inline @click="disableAll">{{ $ts.disableAll }}</MkButton>
+ <MkButton inline @click="enableAll">{{ $ts.enableAll }}</MkButton>
+ <MkSwitch v-for="type in notificationTypes" :key="type" v-model:value="typesMap[type]">{{ $t(`_notification._types.${type}`) }}</MkSwitch>
+ </div>
</div>
</XModalWindow>
</template>
diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue
index 01b3ae4396..baafa86f4d 100644
--- a/src/client/components/notifications.vue
+++ b/src/client/components/notifications.vue
@@ -1,5 +1,6 @@
<template>
<div class="mfcuwfyp _noGap_">
+ <div class="_magnet"></div>
<XList class="notifications" :items="items" v-slot="{ item: notification }">
<XNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :note="notification.note" @update:note="noteUpdated(notification.note, $event)" :key="notification.id"/>
<XNotification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/>
diff --git a/src/client/components/page-preview.vue b/src/client/components/page-preview.vue
index 2125ddc534..cd896445a7 100644
--- a/src/client/components/page-preview.vue
+++ b/src/client/components/page-preview.vue
@@ -1,5 +1,5 @@
<template>
-<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj _panel" tabindex="-1">
+<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj _block _isolated" tabindex="-1">
<div class="thumbnail" v-if="page.eyeCatchingImage" :style="`background-image: url('${page.eyeCatchingImage.thumbnailUrl}')`"></div>
<article>
<header>
@@ -35,7 +35,6 @@ export default defineComponent({
<style lang="scss" scoped>
.vhpxefrj {
display: block;
- width: 100%;
&:hover {
text-decoration: none;
diff --git a/src/client/components/page-window.vue b/src/client/components/page-window.vue
index ca6f4dd73e..1afde25501 100644
--- a/src/client/components/page-window.vue
+++ b/src/client/components/page-window.vue
@@ -14,7 +14,7 @@
<button class="_button" @click="back()" v-if="history.length > 0"><Fa :icon="faChevronLeft"/></button>
<button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button>
</template>
- <div class="yrolvcoq" style="min-height: 100%; background: var(--bg);">
+ <div class="yrolvcoq _flat_">
<component :is="component" v-bind="props" :ref="changePage"/>
</div>
</XWindow>
@@ -29,6 +29,7 @@ import { popout } from '@client/scripts/popout';
import copyToClipboard from '@client/scripts/copy-to-clipboard';
import { resolve } from '@client/router';
import { url } from '@client/config';
+import * as symbols from '@client/symbols';
export default defineComponent({
components: {
@@ -123,8 +124,8 @@ export default defineComponent({
methods: {
changePage(page) {
if (page == null) return;
- if (page.INFO) {
- this.pageInfo = page.INFO;
+ if (page[symbols.PAGE_INFO]) {
+ this.pageInfo = page[symbols.PAGE_INFO];
}
},
@@ -155,6 +156,7 @@ export default defineComponent({
<style lang="scss" scoped>
.yrolvcoq {
- --section-padding: 16px;
+ min-height: 100%;
+ background: var(--bg);
}
</style>
diff --git a/src/client/components/page/page.text.vue b/src/client/components/page/page.text.vue
index 491c62be26..580c5a93bf 100644
--- a/src/client/components/page/page.text.vue
+++ b/src/client/components/page/page.text.vue
@@ -9,8 +9,8 @@
import { TextBlock } from '@client/scripts/hpml/block';
import { Hpml } from '@client/scripts/hpml/evaluator';
import { defineAsyncComponent, defineComponent, PropType } from 'vue';
-import { parse } from '../../../mfm/parse';
-import { unique } from '../../../prelude/array';
+import * as mfm from 'mfm-js';
+import { extractUrlFromMfm } from '@/misc/extract-url-from-mfm';
export default defineComponent({
components: {
@@ -34,11 +34,7 @@ export default defineComponent({
computed: {
urls(): string[] {
if (this.text) {
- const ast = parse(this.text);
- // TODO: 再帰的にURL要素がないか調べる
- return unique(ast
- .filter(t => ((t.node.type == 'url' || t.node.type == 'link') && t.node.props.url && !t.node.props.silent))
- .map(t => t.node.props.url));
+ return extractUrlFromMfm(mfm.parse(this.text));
} else {
return [];
}
diff --git a/src/client/components/poll.vue b/src/client/components/poll.vue
index af3b3804ab..6cf6a8e918 100644
--- a/src/client/components/poll.vue
+++ b/src/client/components/poll.vue
@@ -110,7 +110,7 @@ export default defineComponent({
position: relative;
margin: 4px 0;
padding: 4px 8px;
- border: solid 1px var(--divider);
+ border: solid 0.5px var(--divider);
border-radius: 4px;
overflow: hidden;
cursor: pointer;
diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue
index 7d2355c190..ce79f34d62 100644
--- a/src/client/components/post-form.vue
+++ b/src/client/components/post-form.vue
@@ -56,12 +56,12 @@ import { faReply, faQuoteRight, faPaperPlane, faTimes, faUpload, faPollH, faGlob
import { faEyeSlash, faLaughSquint } from '@fortawesome/free-regular-svg-icons';
import insertTextAtCursor from 'insert-text-at-cursor';
import { length } from 'stringz';
-import { toASCII } from 'punycode';
+import { toASCII } from 'punycode/';
import XNotePreview from './note-preview.vue';
-import { parse } from '../../mfm/parse';
+import * as mfm from 'mfm-js';
import { host, url } from '@client/config';
import { erase, unique } from '../../prelude/array';
-import extractMentions from '@/misc/extract-mentions';
+import { extractMentions } from '@/misc/extract-mentions';
import getAcct from '@/misc/acct/render';
import { formatTimeString } from '@/misc/format-time-string';
import { Autocomplete } from '@client/scripts/autocomplete';
@@ -229,7 +229,7 @@ export default defineComponent({
}
if (this.reply && this.reply.text != null) {
- const ast = parse(this.reply.text);
+ const ast = mfm.parse(this.reply.text);
for (const x of extractMentions(ast)) {
const mention = x.host ? `@${x.username}@${toASCII(x.host)}` : `@${x.username}`;
@@ -580,7 +580,7 @@ export default defineComponent({
this.deleteDraft();
this.$emit('posted');
if (this.text && this.text != '') {
- const hashtags = parse(this.text).filter(x => x.node.type === 'hashtag').map(x => x.node.props.hashtag);
+ const hashtags = mfm.parse(this.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag);
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags.concat(history))));
}
@@ -767,7 +767,7 @@ export default defineComponent({
> .cw {
z-index: 1;
padding-bottom: 8px;
- border-bottom: solid 1px var(--divider);
+ border-bottom: solid 0.5px var(--divider);
}
> .text {
diff --git a/src/client/components/remote-caution.vue b/src/client/components/remote-caution.vue
index 98c7aaaa6e..c9c5ceea4c 100644
--- a/src/client/components/remote-caution.vue
+++ b/src/client/components/remote-caution.vue
@@ -1,5 +1,5 @@
<template>
-<div class="jmgmzlwq _panel"><Fa :icon="faExclamationTriangle" style="margin-right: 8px;"/>{{ $ts.remoteUserCaution }}<a :href="href" rel="nofollow noopener" target="_blank">{{ $ts.showOnRemote }}</a></div>
+<div class="jmgmzlwq _block"><Fa :icon="faExclamationTriangle" style="margin-right: 8px;"/>{{ $ts.remoteUserCaution }}<a :href="href" rel="nofollow noopener" target="_blank">{{ $ts.showOnRemote }}</a></div>
</template>
<script lang="ts">
diff --git a/src/client/components/sidebar.vue b/src/client/components/sidebar.vue
deleted file mode 100644
index 61439781b4..0000000000
--- a/src/client/components/sidebar.vue
+++ /dev/null
@@ -1,458 +0,0 @@
-<template>
-<div class="mvcprjjd">
- <transition name="nav-back">
- <div class="nav-back _modalBg"
- v-if="showing"
- @click="showing = false"
- @touchstart.passive="showing = false"
- ></div>
- </transition>
-
- <transition name="nav">
- <nav class="nav" :class="{ iconOnly, hidden }" v-show="showing">
- <div>
- <button class="item _button account" @click="openAccountMenu">
- <MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/>
- </button>
- <MkA class="item index" active-class="active" to="/" exact>
- <Fa :icon="faHome" fixed-width/><span class="text">{{ $ts.timeline }}</span>
- </MkA>
- <template v-for="item in menu">
- <div v-if="item === '-'" class="divider"></div>
- <component v-else-if="menuDef[item] && (menuDef[item].show !== false)" :is="menuDef[item].to ? 'MkA' : 'button'" class="item _button" :class="item" active-class="active" v-on="menuDef[item].action ? { click: menuDef[item].action } : {}" :to="menuDef[item].to">
- <Fa :icon="menuDef[item].icon" fixed-width/><span class="text">{{ $ts[menuDef[item].title] }}</span>
- <i v-if="menuDef[item].indicated"><Fa :icon="faCircle"/></i>
- </component>
- </template>
- <div class="divider"></div>
- <button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$i.isAdmin || $i.isModerator" @click="oepnInstanceMenu">
- <Fa :icon="faServer" fixed-width/><span class="text">{{ $ts.instance }}</span>
- </button>
- <button class="item _button" @click="more">
- <Fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $ts.more }}</span>
- <i v-if="otherNavItemIndicated"><Fa :icon="faCircle"/></i>
- </button>
- <MkA class="item" active-class="active" to="/settings">
- <Fa :icon="faCog" fixed-width/><span class="text">{{ $ts.settings }}</span>
- </MkA>
- <button class="item _button post" @click="post">
- <Fa :icon="faPencilAlt" fixed-width/><span class="text">{{ $ts.note }}</span>
- </button>
- </div>
- </nav>
- </transition>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-import { faGripVertical, faChevronLeft, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faListUl, faPlus, faUserClock, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer, faInfoCircle, faQuestionCircle, faProjectDiagram, faStream, faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
-import { faBell, faEnvelope, faLaugh, faComments } from '@fortawesome/free-regular-svg-icons';
-import { host } from '@client/config';
-import { search } from '@client/scripts/search';
-import * as os from '@client/os';
-import { sidebarDef } from '@client/sidebar';
-import { getAccounts, addAccount, login } from '@client/account';
-
-export default defineComponent({
- props: {
- defaultHidden: {
- type: Boolean,
- required: false,
- default: false,
- }
- },
-
- data() {
- return {
- host: host,
- showing: false,
- accounts: [],
- connection: null,
- menuDef: sidebarDef,
- iconOnly: false,
- hidden: this.defaultHidden,
- faGripVertical, faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer, faProjectDiagram
- };
- },
-
- computed: {
- menu(): string[] {
- return this.$store.state.menu;
- },
-
- otherNavItemIndicated(): boolean {
- for (const def in this.menuDef) {
- if (this.menu.includes(def)) continue;
- if (this.menuDef[def].indicated) return true;
- }
- return false;
- },
- },
-
- watch: {
- $route(to, from) {
- this.showing = false;
- },
-
- '$store.reactiveState.sidebarDisplay.value'() {
- this.calcViewState();
- },
-
- iconOnly() {
- this.$nextTick(() => {
- this.$emit('change-view-mode');
- });
- },
-
- hidden() {
- this.$nextTick(() => {
- this.$emit('change-view-mode');
- });
- }
- },
-
- created() {
- window.addEventListener('resize', this.calcViewState);
- this.calcViewState();
- },
-
- methods: {
- calcViewState() {
- this.iconOnly = (window.innerWidth <= 1279) || (this.$store.state.sidebarDisplay === 'icon');
- if (!this.defaultHidden) {
- this.hidden = (window.innerWidth <= 650);
- }
- },
-
- show() {
- this.showing = true;
- },
-
- post() {
- os.post();
- },
-
- search() {
- search();
- },
-
- async openAccountMenu(ev) {
- const storedAccounts = getAccounts().filter(x => x.id !== this.$i.id);
- const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) });
-
- const accountItemPromises = storedAccounts.map(a => new Promise(res => {
- accountsPromise.then(accounts => {
- const account = accounts.find(x => x.id === a.id);
- if (account == null) return res(null);
- res({
- type: 'user',
- user: account,
- action: () => { this.switchAccount(account); }
- });
- });
- }));
-
- os.modalMenu([...[{
- type: 'link',
- text: this.$ts.profile,
- to: `/@${ this.$i.username }`,
- avatar: this.$i,
- }, null, ...accountItemPromises, {
- icon: faPlus,
- text: this.$ts.addAcount,
- action: () => {
- os.modalMenu([{
- text: this.$ts.existingAcount,
- action: () => { this.addAcount(); },
- }, {
- text: this.$ts.createAccount,
- action: () => { this.createAccount(); },
- }], ev.currentTarget || ev.target);
- },
- }]], ev.currentTarget || ev.target, {
- align: 'left'
- });
- },
-
- oepnInstanceMenu(ev) {
- os.modalMenu([{
- type: 'link',
- text: this.$ts.dashboard,
- to: '/instance',
- icon: faTachometerAlt,
- }, null, this.$i.isAdmin ? {
- type: 'link',
- text: this.$ts.settings,
- to: '/instance/settings',
- icon: faCog,
- } : undefined, {
- type: 'link',
- text: this.$ts.customEmojis,
- to: '/instance/emojis',
- icon: faLaugh,
- }, {
- type: 'link',
- text: this.$ts.users,
- to: '/instance/users',
- icon: faUsers,
- }, {
- type: 'link',
- text: this.$ts.files,
- to: '/instance/files',
- icon: faCloud,
- }, {
- type: 'link',
- text: this.$ts.jobQueue,
- to: '/instance/queue',
- icon: faExchangeAlt,
- }, {
- type: 'link',
- text: this.$ts.federation,
- to: '/instance/federation',
- icon: faGlobe,
- }, {
- type: 'link',
- text: this.$ts.relays,
- to: '/instance/relays',
- icon: faProjectDiagram,
- }, {
- type: 'link',
- text: this.$ts.announcements,
- to: '/instance/announcements',
- icon: faBroadcastTower,
- }, {
- type: 'link',
- text: this.$ts.abuseReports,
- to: '/instance/abuses',
- icon: faExclamationCircle,
- }, {
- type: 'link',
- text: this.$ts.logs,
- to: '/instance/logs',
- icon: faStream,
- }], ev.currentTarget || ev.target);
- },
-
- more(ev) {
- os.popup(import('./launch-pad.vue'), {}, {
- }, 'closed');
- },
-
- addAcount() {
- os.popup(import('./signin-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- os.success();
- },
- }, 'closed');
- },
-
- createAccount() {
- os.popup(import('./signup-dialog.vue'), {}, {
- done: res => {
- addAccount(res.id, res.i);
- this.switchAccountWithToken(res.i);
- },
- }, 'closed');
- },
-
- switchAccount(account: any) {
- const storedAccounts = getAccounts();
- const token = storedAccounts.find(x => x.id === account.id).token;
- this.switchAccountWithToken(token);
- },
-
- switchAccountWithToken(token: string) {
- login(token);
- },
- }
-});
-</script>
-
-<style lang="scss" scoped>
-.nav-enter-active,
-.nav-leave-active {
- opacity: 1;
- transform: translateX(0);
- transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
-}
-.nav-enter-from,
-.nav-leave-active {
- opacity: 0;
- transform: translateX(-240px);
-}
-
-.nav-back-enter-active,
-.nav-back-leave-active {
- opacity: 1;
- transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
-}
-.nav-back-enter-from,
-.nav-back-leave-active {
- opacity: 0;
-}
-
-.mvcprjjd {
- $ui-font-size: 1em; // TODO: どこかに集約したい
- $nav-width: 250px;
- $nav-icon-only-width: 86px;
-
- > .nav-back {
- z-index: 1001;
- }
-
- > .nav {
- $avatar-size: 32px;
- $avatar-margin: 8px;
-
- flex: 0 0 $nav-width;
- width: $nav-width;
- box-sizing: border-box;
-
- &.iconOnly {
- flex: 0 0 $nav-icon-only-width;
- width: $nav-icon-only-width;
-
- &:not(.hidden) {
- > div {
- width: $nav-icon-only-width;
-
- > .divider {
- margin: 8px auto;
- width: calc(100% - 32px);
- }
-
- > .item {
- padding-left: 0;
- width: 100%;
- text-align: center;
- font-size: $ui-font-size * 1.1;
- line-height: 3.7rem;
-
- > [data-icon],
- > .avatar {
- margin-right: 0;
- }
-
- > i {
- left: 10px;
- }
-
- > .text {
- display: none;
- }
-
- &:first-child {
- margin-bottom: 8px;
- }
-
- &:last-child {
- margin-top: 8px;
- }
- }
- }
- }
- }
-
- &.hidden {
- position: fixed;
- top: 0;
- left: 0;
- z-index: 1001;
- }
-
- &:not(.hidden) {
- display: block !important;
- }
-
- > div {
- position: fixed;
- top: 0;
- left: 0;
- z-index: 1001;
- width: $nav-width;
- // ほんとは単に 100vh と書きたいところだが... https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
- height: calc(var(--vh, 1vh) * 100);
- box-sizing: border-box;
- overflow: auto;
- background: var(--navBg);
-
- > .divider {
- margin: 16px 0;
- border-top: solid 1px var(--divider);
- }
-
- > .item {
- position: relative;
- display: block;
- padding-left: 24px;
- font-size: $ui-font-size;
- line-height: 3rem;
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
- width: 100%;
- text-align: left;
- box-sizing: border-box;
- color: var(--navFg);
-
- > [data-icon] {
- width: 32px;
- }
-
- > [data-icon],
- > .avatar {
- margin-right: $avatar-margin;
- }
-
- > .avatar {
- width: $avatar-size;
- height: $avatar-size;
- vertical-align: middle;
- }
-
- > i {
- position: absolute;
- top: 0;
- left: 20px;
- color: var(--navIndicator);
- font-size: 8px;
- animation: blink 1s infinite;
- }
-
- &:hover {
- text-decoration: none;
- color: var(--navHoverFg);
- }
-
- &.active {
- color: var(--navActive);
- }
-
- &:first-child, &:last-child {
- position: sticky;
- z-index: 1;
- padding-top: 8px;
- padding-bottom: 8px;
- background: var(--X14);
- -webkit-backdrop-filter: blur(8px);
- backdrop-filter: blur(8px);
- }
-
- &:first-child {
- top: 0;
- margin-bottom: 16px;
- border-bottom: solid 1px var(--divider);
- }
-
- &:last-child {
- bottom: 0;
- margin-top: 16px;
- border-top: solid 1px var(--divider);
- }
- }
- }
- }
-}
-</style>
diff --git a/src/client/components/signin.vue b/src/client/components/signin.vue
index 120da63f30..1cd79bac1d 100755
--- a/src/client/components/signin.vue
+++ b/src/client/components/signin.vue
@@ -1,5 +1,5 @@
<template>
-<form class="eppvobhk" :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
+<form class="eppvobhk _monolithic_" :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
<div class="auth _section">
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
<div class="normal-signin" v-if="!totpLogin">
@@ -39,16 +39,16 @@
</div>
</div>
<div class="social _section">
- <a class="_borderButton _vMargin" v-if="meta && meta.enableTwitterIntegration" :href="`${apiUrl}/signin/twitter`"><Fa :icon="faTwitter" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Twitter' }) }}</a>
- <a class="_borderButton _vMargin" v-if="meta && meta.enableGithubIntegration" :href="`${apiUrl}/signin/github`"><Fa :icon="faGithub" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'GitHub' }) }}</a>
- <a class="_borderButton _vMargin" v-if="meta && meta.enableDiscordIntegration" :href="`${apiUrl}/signin/discord`"><Fa :icon="faDiscord" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Discord' }) }}</a>
+ <a class="_borderButton _gap" v-if="meta && meta.enableTwitterIntegration" :href="`${apiUrl}/signin/twitter`"><Fa :icon="faTwitter" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Twitter' }) }}</a>
+ <a class="_borderButton _gap" v-if="meta && meta.enableGithubIntegration" :href="`${apiUrl}/signin/github`"><Fa :icon="faGithub" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'GitHub' }) }}</a>
+ <a class="_borderButton _gap" v-if="meta && meta.enableDiscordIntegration" :href="`${apiUrl}/signin/discord`"><Fa :icon="faDiscord" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Discord' }) }}</a>
</div>
</form>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
-import { toUnicode } from 'punycode';
+import { toUnicode } from 'punycode/';
import { faLock, faGavel } from '@fortawesome/free-solid-svg-icons';
import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons';
import MkButton from './ui/button.vue';
diff --git a/src/client/components/signup-dialog.vue b/src/client/components/signup-dialog.vue
index 072a5ac19f..df1a525055 100644
--- a/src/client/components/signup-dialog.vue
+++ b/src/client/components/signup-dialog.vue
@@ -7,8 +7,10 @@
>
<template #header>{{ $ts.signup }}</template>
- <div class="_section">
- <XSignup :auto-set="autoSet" @signup="onSignup"/>
+ <div class="_monolithic_">
+ <div class="_section">
+ <XSignup :auto-set="autoSet" @signup="onSignup"/>
+ </div>
</div>
</XModalWindow>
</template>
diff --git a/src/client/components/signup.vue b/src/client/components/signup.vue
index 1ce9fc2789..66d01213bc 100644
--- a/src/client/components/signup.vue
+++ b/src/client/components/signup.vue
@@ -55,7 +55,7 @@
import { defineComponent, defineAsyncComponent } from 'vue';
import { faLock, faExclamationTriangle, faSpinner, faCheck, faKey } from '@fortawesome/free-solid-svg-icons';
const getPasswordStrength = require('syuilo-password-strength');
-import { toUnicode } from 'punycode';
+import { toUnicode } from 'punycode/';
import { host, url } from '@client/config';
import MkButton from './ui/button.vue';
import MkInput from './ui/input.vue';
diff --git a/src/client/components/taskmanager.api-window.vue b/src/client/components/taskmanager.api-window.vue
index 9b6c3f16d0..c9b2c43413 100644
--- a/src/client/components/taskmanager.api-window.vue
+++ b/src/client/components/taskmanager.api-window.vue
@@ -9,7 +9,7 @@
<template #header>Req Viewer</template>
<div class="rlkneywz">
- <MkTab v-model:value="tab" style="border-bottom: solid 1px var(--divider);">
+ <MkTab v-model:value="tab" style="border-bottom: solid 0.5px var(--divider);">
<option value="req">Request</option>
<option value="res">Response</option>
</MkTab>
diff --git a/src/client/components/taskmanager.vue b/src/client/components/taskmanager.vue
index af9033178e..1339e2e352 100644
--- a/src/client/components/taskmanager.vue
+++ b/src/client/components/taskmanager.vue
@@ -4,7 +4,7 @@
<Fa :icon="faTerminal" style="margin-right: 0.5em;"/>Task Manager
</template>
<div class="qljqmnzj _monospace">
- <MkTab v-model:value="tab" style="border-bottom: solid 1px var(--divider);">
+ <MkTab v-model:value="tab" style="border-bottom: solid 0.5px var(--divider);">
<option value="windows">Windows</option>
<option value="stream">Stream</option>
<option value="streamPool">Stream (Pool)</option>
@@ -215,7 +215,7 @@ export default defineComponent({
width: 100%;
padding: 8px 16px;
box-sizing: border-box;
- border-top: solid 1px var(--divider);
+ border-top: solid 0.5px var(--divider);
font-size: 0.9em;
> div {
diff --git a/src/client/components/timeline.vue b/src/client/components/timeline.vue
index c2ab0b005d..faa3984638 100644
--- a/src/client/components/timeline.vue
+++ b/src/client/components/timeline.vue
@@ -56,6 +56,7 @@ export default defineComponent({
includeLocalRenotes: this.$store.state.showLocalRenotes
},
query: {},
+ date: null
};
},
@@ -157,7 +158,7 @@ export default defineComponent({
endpoint: endpoint,
limit: 10,
params: init => ({
- untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
+ untilDate: this.date?.getTime(),
...this.baseQuery, ...this.query
})
};
@@ -171,6 +172,11 @@ export default defineComponent({
methods: {
focus() {
this.$refs.tl.focus();
+ },
+
+ timetravel(date?: Date) {
+ this.date = date;
+ this.$refs.tl.reload();
}
}
});
diff --git a/src/client/components/ui/container.vue b/src/client/components/ui/container.vue
index c3353cca89..427421af7d 100644
--- a/src/client/components/ui/container.vue
+++ b/src/client/components/ui/container.vue
@@ -1,5 +1,5 @@
<template>
-<div class="ukygtjoj _panel" :class="{ naked, hideHeader: !showHeader, scrollable, closed: !showBody }" v-size="{ max: [380] }">
+<div class="ukygtjoj _block" :class="{ naked, hideHeader: !showHeader, scrollable, closed: !showBody }" v-size="{ max: [380] }">
<header v-if="showHeader" ref="header">
<div class="title"><slot name="header"></slot></div>
<div class="sub">
@@ -116,7 +116,7 @@ export default defineComponent({
.ukygtjoj {
position: relative;
- overflow: hidden;
+ overflow: clip;
&.naked {
background: transparent !important;
@@ -133,10 +133,12 @@ export default defineComponent({
}
> header {
- position: relative;
+ position: sticky;
+ top: var(--stickyTop, 0px);
+ left: 0;
color: var(--panelHeaderFg);
background: var(--panelHeaderBg);
- box-shadow: 0 1px 0 0 var(--panelHeaderDivider);
+ border-bottom: solid 0.5px var(--panelHeaderDivider);
z-index: 2;
line-height: 1.4em;
@@ -172,7 +174,7 @@ export default defineComponent({
padding: 24px;
& + ._content {
- border-top: solid 1px var(--divider);
+ border-top: solid 0.5px var(--divider);
}
}
}
diff --git a/src/client/components/ui/folder.vue b/src/client/components/ui/folder.vue
index 1cd67c2521..aee3c0ccaa 100644
--- a/src/client/components/ui/folder.vue
+++ b/src/client/components/ui/folder.vue
@@ -98,11 +98,12 @@ export default defineComponent({
> header {
display: flex;
position: relative;
- z-index: 2;
- // TODO
- // position: sticky;
- // top: var(--stickyTopOffset);
- // backdrop-filter: blur(20px);
+ z-index: 10;
+ position: sticky;
+ top: var(--stickyTop, 0px);
+ background: var(--X17);
+ -webkit-backdrop-filter: blur(8px);
+ backdrop-filter: blur(20px);
> .title {
margin: 0;
@@ -137,4 +138,10 @@ export default defineComponent({
}
}
}
+
+._flat_ .ssazuxis {
+ > header {
+ padding: 0 16px;
+ }
+}
</style>
diff --git a/src/client/components/ui/info.vue b/src/client/components/ui/info.vue
index 12b3dbcfd1..ad1b9ebb58 100644
--- a/src/client/components/ui/info.vue
+++ b/src/client/components/ui/info.vue
@@ -53,4 +53,8 @@ export default defineComponent({
margin-right: 4px;
}
}
+
+._flat_ .fpezltsf {
+ border-radius: 0;
+}
</style>
diff --git a/src/client/components/ui/modal-window.vue b/src/client/components/ui/modal-window.vue
index 2cdf961379..90b803801d 100644
--- a/src/client/components/ui/modal-window.vue
+++ b/src/client/components/ui/modal-window.vue
@@ -1,6 +1,6 @@
<template>
<MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')">
- <div class="ebkgoccj _popup _narrow_" @keydown="onKeydown" :style="{ width: `${width}px`, height: height ? `${height}px` : null }">
+ <div class="ebkgoccj _popup _narrow_" @keydown="onKeydown" :style="{ width: `${width}px`, height: scroll ? (height ? `${height}px` : null) : (height ? `min(${height}px, 100%)` : '100%') }">
<div class="header">
<button class="_button" v-if="withOkButton" @click="$emit('close')"><Fa :icon="faTimes"/></button>
<span class="title">
@@ -61,6 +61,11 @@ export default defineComponent({
required: false,
default: true,
},
+ scroll: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
emits: ['click', 'close', 'closed', 'ok'],
@@ -94,10 +99,10 @@ export default defineComponent({
flex-direction: column;
contain: content;
- --section-padding: 24px;
+ --root-margin: 24px;
@media (max-width: 500px) {
- --section-padding: 16px;
+ --root-margin: 16px;
}
> .header {
diff --git a/src/client/components/ui/modal.vue b/src/client/components/ui/modal.vue
index db6564bacc..3b11213426 100644
--- a/src/client/components/ui/modal.vue
+++ b/src/client/components/ui/modal.vue
@@ -1,5 +1,5 @@
<template>
-<transition :name="$store.state.animation ? popup ? 'modal-popup' : 'modal' : ''" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered">
+<transition :name="$store.state.animation ? popup ? 'modal-popup' : 'modal' : ''" :duration="$store.state.animation ? popup ? 500 : 300 : 0" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered">
<div v-show="manualShowing != null ? manualShowing : showing" class="mk-modal" v-hotkey.global="keymap" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
<div class="bg _modalBg" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div>
<div class="content" :class="{ popup, fixed, top: position === 'top' }" @click.self="onBgClick" ref="content">
@@ -183,9 +183,6 @@ export default defineComponent({
<style lang="scss" scoped>
.modal-enter-active, .modal-leave-active {
- // CSS的には無意味だけどこれが無いとVueが認識しない
- transition: opacity 0.3s, transform 0.3s !important;
-
> .bg {
transition: opacity 0.3s !important;
}
@@ -207,9 +204,6 @@ export default defineComponent({
}
.modal-popup-enter-active, .modal-popup-leave-active {
- // CSS的には無意味だけどこれが無いとVueが認識しない
- transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), transform 0.5s cubic-bezier(0.16, 1, 0.3, 1) !important;
-
> .bg {
transition: opacity 0.3s !important;
}
diff --git a/src/client/components/ui/window.vue b/src/client/components/ui/window.vue
index 1613644c87..70676cdaf5 100644
--- a/src/client/components/ui/window.vue
+++ b/src/client/components/ui/window.vue
@@ -395,7 +395,7 @@ export default defineComponent({
position: fixed;
top: 0;
left: 0;
- z-index: 5000;
+ z-index: 10000; // mk-modalのと同じでなければならない
&.front {
z-index: 11000; // front指定の時は、mk-modalのよりも大きくなければならない
diff --git a/src/client/components/user-info.vue b/src/client/components/user-info.vue
index 34ea38c3b4..ac2f9a75a6 100644
--- a/src/client/components/user-info.vue
+++ b/src/client/components/user-info.vue
@@ -104,7 +104,7 @@ export default defineComponent({
> .description {
padding: 16px;
font-size: 0.8em;
- border-top: solid 1px var(--divider);
+ border-top: solid 0.5px var(--divider);
> .mfm {
display: -webkit-box;
@@ -116,7 +116,7 @@ export default defineComponent({
> .status {
padding: 10px 16px;
- border-top: solid 1px var(--divider);
+ border-top: solid 0.5px var(--divider);
> div {
display: inline-block;
diff --git a/src/client/components/user-list.vue b/src/client/components/user-list.vue
index 418c4127d9..38d9df63bb 100644
--- a/src/client/components/user-list.vue
+++ b/src/client/components/user-list.vue
@@ -1,7 +1,7 @@
<template>
<MkError v-if="error" @retry="init()"/>
-<div v-else class="efvhhmdq">
+<div v-else class="efvhhmdq _isolated">
<div class="no-users" v-if="empty">
<p>{{ $ts.noUsers }}</p>
</div>
diff --git a/src/client/components/user-select-dialog.vue b/src/client/components/user-select-dialog.vue
index e21deea178..05a43402a8 100644
--- a/src/client/components/user-select-dialog.vue
+++ b/src/client/components/user-select-dialog.vue
@@ -153,7 +153,7 @@ export default defineComponent({
> .user {
display: flex;
align-items: center;
- padding: 8px var(--section-padding);
+ padding: 8px var(--root-margin);
font-size: 14px;
&:hover {
diff --git a/src/client/components/visibility-picker.vue b/src/client/components/visibility-picker.vue
index 0b98d30b9d..caa2b116a6 100644
--- a/src/client/components/visibility-picker.vue
+++ b/src/client/components/visibility-picker.vue
@@ -97,7 +97,7 @@ export default defineComponent({
> .divider {
margin: 8px 0;
- border-top: solid 1px var(--divider);
+ border-top: solid 0.5px var(--divider);
}
> button {