Compare commits

...

15 Commits

Author SHA1 Message Date
taichan b53da62196
Merge 48232ca57b into daf9ae5d4a 2024-09-17 21:01:35 +09:00
FineArchs daf9ae5d4a
ScratchpadにUIインスペクターを追加 (#14565)
* add ui list

* Update scratchpad.vue

* experiment

* design change

* redesign

* redesign

* Update ja-JP.yml

* redesign

* component properties

* whole json

* use textarea

* fix import

* stringify function

* Update CHANGELOG.md

* UI Component Monitor -> UI Inspector

* uiInspectorOpenedFlags -> uiInspectorOpenedComponents

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>

* fix

* change key i -> c.value.id

---------

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2024-09-17 20:11:50 +09:00
syuilo a5e61b8c19 Revert "refactor"
This reverts commit 0134e6e420.
2024-09-17 17:05:52 +09:00
syuilo cacdf9d939 refactor
MkMisskeyFlavoredMarkdown -> MkMfm
2024-09-17 17:03:09 +09:00
syuilo 0134e6e420 refactor 2024-09-17 17:00:48 +09:00
かっこかり 6bd6af440f
fix(frontend): 絵文字関連のスタイルが崩れているのを修正 (#14559)
* fix(frontend): 絵文字関連のスタイルが崩れていたのを修正 (MisskeyIO#725)

(cherry picked from commit 00fd684a7b382aaeb3355a1c80dc24078a5caa61)

* Update Changelog

* ✌️

---------

Co-authored-by: Yuuki <yukikum57@gmail.com>
2024-09-17 15:41:52 +09:00
taichanne30 48232ca57b
ユーザーTLではFTTのソースが空の際にDBにFallbackしないように 2024-07-25 16:01:41 +09:00
taichanne30 3564bf5c66
Refactor: const naming 2024-07-25 14:37:09 +09:00
taichanne30 685fc2bd9d
Fix: shouldFallbackToDbがすでにtrueの場合にそれが無視される 2024-07-25 14:30:10 +09:00
taichanne30 6cc0138d1e
Merge branch 'develop' of https://github.com/misskey-dev/misskey into fix-stl-note-fetch 2024-07-25 14:23:31 +09:00
taichan d3228d5570
Merge branch 'develop' into fix-stl-note-fetch 2024-03-07 02:04:23 +09:00
taichanne30 4a8ffe20a7
Fix timeline fetch when using sinceId 2024-03-07 01:55:57 +09:00
taichanne30 5615675991
Update CHANGELOG.md 2024-03-02 15:35:39 +09:00
taichan 0c65b8058a
Merge branch 'develop' into fix-stl-note-fetch 2024-03-02 15:28:45 +09:00
taichanne30 82bec76cd4
fix(backend): DBフォールバック有効時、複数のFTTソースから取得するタイムラインで取得漏れが起きる現象の修正 2024-03-02 15:23:25 +09:00
13 changed files with 96 additions and 18 deletions

View File

@ -9,8 +9,11 @@
- Enhance: サイズ制限を超過するファイルをアップロードしようとした際にエラーを出すように - Enhance: サイズ制限を超過するファイルをアップロードしようとした際にエラーを出すように
- Enhance: アイコンデコレーション管理画面にプレビューを追加 - Enhance: アイコンデコレーション管理画面にプレビューを追加
- Enhance: コントロールパネル内のファイル一覧でセンシティブなファイルを区別しやすく - Enhance: コントロールパネル内のファイル一覧でセンシティブなファイルを区別しやすく
- Enhance: ScratchpadにUIインスペクターを追加
- Fix: サーバーメトリクスが2つ以上あるとリロード直後の表示がおかしくなる問題を修正 - Fix: サーバーメトリクスが2つ以上あるとリロード直後の表示がおかしくなる問題を修正
- Fix: 月の違う同じ日はセパレータが表示されないのを修正 - Fix: 月の違う同じ日はセパレータが表示されないのを修正
- Fix: 縦横比が極端なカスタム絵文字を表示する際にレイアウトが崩れる箇所があるのを修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/725)
### Server ### Server
- Fix: アンテナの書き込み時にキーワードが与えられなかった場合のエラーをApiErrorとして投げるように - Fix: アンテナの書き込み時にキーワードが与えられなかった場合のエラーをApiErrorとして投げるように
@ -299,7 +302,7 @@
- Fix: カスタム絵文字の画像読み込みに失敗した際はテキストではなくダミー画像を表示 #13487 - Fix: カスタム絵文字の画像読み込みに失敗した際はテキストではなくダミー画像を表示 #13487
### Server ### Server
- - Fix: FTT有効かつDBフォールバック有効時、STLのようにタイムラインのソースが複数だとFTTとDBのフォールバック間で取得されないートがある問題
## 2024.3.0 ## 2024.3.0

View File

@ -592,6 +592,8 @@ ascendingOrder: "昇順"
descendingOrder: "降順" descendingOrder: "降順"
scratchpad: "スクラッチパッド" scratchpad: "スクラッチパッド"
scratchpadDescription: "スクラッチパッドは、AiScriptの実験環境を提供します。Misskeyと対話するコードの記述、実行、結果の確認ができます。" scratchpadDescription: "スクラッチパッドは、AiScriptの実験環境を提供します。Misskeyと対話するコードの記述、実行、結果の確認ができます。"
uiInspector: "UIインスペクター"
uiInspectorDescription: "メモリ上に存在しているUIコンポーネントのインスタンスの一覧を見ることができます。UIコンポーネントはUi:C:系関数により生成されます。"
output: "出力" output: "出力"
script: "スクリプト" script: "スクリプト"
disablePagesScript: "Pagesのスクリプトを無効にする" disablePagesScript: "Pagesのスクリプトを無効にする"

View File

@ -34,6 +34,7 @@ type TimelineOptions = {
excludeReplies?: boolean; excludeReplies?: boolean;
excludePureRenotes: boolean; excludePureRenotes: boolean;
dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise<MiNote[]>, dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise<MiNote[]>,
preventEmptyTimelineDbFallback?: boolean;
}; };
@Injectable() @Injectable()
@ -63,12 +64,20 @@ export class FanoutTimelineEndpointService {
const redisResult = await this.fanoutTimelineService.getMulti(ps.redisTimelines, ps.untilId, ps.sinceId); const redisResult = await this.fanoutTimelineService.getMulti(ps.redisTimelines, ps.untilId, ps.sinceId);
// TODO: いい感じにgetMulti内でソート済だからuniqするときにredisResultが全てソート済なのを利用して再ソートを避けたい // オプション無効時、取得したredisResultのうち、2つ以上ソースがあり、1つでも空であればDBにフォールバックする
const redisResultIds = Array.from(new Set(redisResult.flat(1))).sort(idCompare); let shouldFallbackToDb = ps.useDbFallback &&
(ps.preventEmptyTimelineDbFallback !== true && redisResult.length > 1 && redisResult.some(ids => ids.length === 0));
// 取得したresultの中で最古のIDのうち、最も新しいものを取得
const fttThresholdId = redisResult.map(ids => ids[0]).sort()[0];
// TODO: いい感じにgetMulti内でソート済だからuniqするときにredisResultが全てソート済なのを利用して再ソートを避けたい
const redisResultIds = shouldFallbackToDb ? [] : Array.from(new Set(redisResult.flat(1))).sort(idCompare);
let noteIds = redisResultIds.filter(id => id >= fttThresholdId).slice(0, ps.limit);
let noteIds = redisResultIds.slice(0, ps.limit);
const oldestNoteId = ascending ? redisResultIds[0] : redisResultIds[redisResultIds.length - 1]; const oldestNoteId = ascending ? redisResultIds[0] : redisResultIds[redisResultIds.length - 1];
const shouldFallbackToDb = noteIds.length === 0 || ps.sinceId != null && ps.sinceId < oldestNoteId; shouldFallbackToDb ||= ps.useDbFallback && (noteIds.length === 0 || ps.sinceId != null && ps.sinceId < oldestNoteId);
if (!shouldFallbackToDb) { if (!shouldFallbackToDb) {
let filter = ps.noteFilter ?? (_note => true); let filter = ps.noteFilter ?? (_note => true);

View File

@ -143,6 +143,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withFiles: ps.withFiles, withFiles: ps.withFiles,
withRenotes: ps.withRenotes, withRenotes: ps.withRenotes,
}, me), }, me),
preventEmptyTimelineDbFallback: true,
}); });
return timeline; return timeline;

View File

@ -611,6 +611,7 @@ defineExpose({
width: auto; width: auto;
height: auto; height: auto;
min-width: 0; min-width: 0;
padding: 0;
&:disabled { &:disabled {
cursor: not-allowed; cursor: not-allowed;
@ -717,7 +718,7 @@ defineExpose({
> .item { > .item {
position: relative; position: relative;
padding: 0; padding: 0 3px;
width: var(--eachSize); width: var(--eachSize);
height: var(--eachSize); height: var(--eachSize);
contain: strict; contain: strict;

View File

@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:withTooltip="true" :withTooltip="true"
:reaction="notification.reaction.replace(/^:(\w+):$/, ':$1@.:')" :reaction="notification.reaction.replace(/^:(\w+):$/, ':$1@.:')"
:noStyle="true" :noStyle="true"
style="width: 100%; height: 100%;" style="width: 100%; height: 100% !important; object-fit: contain;"
/> />
</div> </div>
</div> </div>
@ -122,7 +122,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:withTooltip="true" :withTooltip="true"
:reaction="reaction.reaction.replace(/^:(\w+):$/, ':$1@.:')" :reaction="reaction.reaction.replace(/^:(\w+):$/, ':$1@.:')"
:noStyle="true" :noStyle="true"
style="width: 100%; height: 100%;" style="width: 100%; height: 100% !important; object-fit: contain;"
/> />
</div> </div>
</div> </div>

View File

@ -36,6 +36,7 @@ const emit = defineEmits<{
.icon { .icon {
display: block; display: block;
width: 60px; width: 60px;
max-height: 60px;
font-size: 60px; // unicodewidth font-size: 60px; // unicodewidth
margin: 0 auto; margin: 0 auto;
object-fit: contain; object-fit: contain;

View File

@ -63,6 +63,7 @@ function getReactionName(reaction: string): string {
.reactionIcon { .reactionIcon {
display: block; display: block;
width: 60px; width: 60px;
max-height: 60px;
font-size: 60px; // unicodewidth font-size: 60px; // unicodewidth
object-fit: contain; object-fit: contain;
margin: 0 auto; margin: 0 auto;

View File

@ -3,15 +3,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3'; import { StoryObj } from '@storybook/vue3';
import { expect, within } from '@storybook/test'; import { expect, within } from '@storybook/test';
import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js'; import MkMfm from './MkMfm.js';
export const Default = { export const Default = {
render(args) { render(args) {
return { return {
components: { components: {
MkMisskeyFlavoredMarkdown, MkMfm,
}, },
setup() { setup() {
return { return {
@ -25,7 +24,7 @@ export const Default = {
}; };
}, },
}, },
template: '<MkMisskeyFlavoredMarkdown v-bind="props" />', template: '<MkMfm v-bind="props" />',
}; };
}, },
async play({ canvasElement, args }) { async play({ canvasElement, args }) {
@ -54,25 +53,25 @@ export const Default = {
parameters: { parameters: {
layout: 'centered', layout: 'centered',
}, },
} satisfies StoryObj<typeof MkMisskeyFlavoredMarkdown>; } satisfies StoryObj<typeof MkMfm>;
export const Plain = { export const Plain = {
...Default, ...Default,
args: { args: {
...Default.args, ...Default.args,
plain: true, plain: true,
}, },
} satisfies StoryObj<typeof MkMisskeyFlavoredMarkdown>; } satisfies StoryObj<typeof MkMfm>;
export const Nowrap = { export const Nowrap = {
...Default, ...Default,
args: { args: {
...Default.args, ...Default.args,
nowrap: true, nowrap: true,
}, },
} satisfies StoryObj<typeof MkMisskeyFlavoredMarkdown>; } satisfies StoryObj<typeof MkMfm>;
export const IsNotNote = { export const IsNotNote = {
...Default, ...Default,
args: { args: {
...Default.args, ...Default.args,
isNote: false, isNote: false,
}, },
} satisfies StoryObj<typeof MkMisskeyFlavoredMarkdown>; } satisfies StoryObj<typeof MkMfm>;

View File

@ -5,7 +5,7 @@
import { App } from 'vue'; import { App } from 'vue';
import Mfm from './global/MkMisskeyFlavoredMarkdown.js'; import Mfm from './global/MkMfm.js';
import MkA from './global/MkA.vue'; import MkA from './global/MkA.vue';
import MkAcct from './global/MkAcct.vue'; import MkAcct from './global/MkAcct.vue';
import MkAvatar from './global/MkAvatar.vue'; import MkAvatar from './global/MkAvatar.vue';

View File

@ -344,6 +344,7 @@ definePageMetadata(() => ({
> .img { > .img {
width: 42px; width: 42px;
height: 42px; height: 42px;
object-fit: contain;
} }
> .body { > .body {
@ -390,6 +391,7 @@ definePageMetadata(() => ({
> .img { > .img {
width: 32px; width: 32px;
height: 32px; height: 32px;
object-fit: contain;
} }
> .body { > .body {

View File

@ -30,6 +30,24 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</MkContainer> </MkContainer>
<MkContainer :foldable="true" :expanded="false">
<template #header>{{ i18n.ts.uiInspector }}</template>
<div :class="$style.uiInspector">
<div v-for="c in components" :key="c.value.id">
<div :class="$style.uiInspectorType">{{ c.value.type }}</div>
<div :class="$style.uiInspectorId">{{ c.value.id }}</div>
<button :class="$style.uiInspectorPropsToggle" @click="() => uiInspectorOpenedComponents.set(c, !uiInspectorOpenedComponents.get(c))">
<i v-if="uiInspectorOpenedComponents.get(c)" class="ti ti-chevron-up icon"></i>
<i v-else class="ti ti-chevron-down icon"></i>
</button>
<div v-if="uiInspectorOpenedComponents.get(c)">
<MkTextarea :modelValue="stringifyUiProps(c.value)" code readonly></MkTextarea>
</div>
</div>
<div :class="$style.uiInspectorDescription">{{ i18n.ts.uiInspectorDescription }}</div>
</div>
</MkContainer>
<div class=""> <div class="">
{{ i18n.ts.scratchpadDescription }} {{ i18n.ts.scratchpadDescription }}
</div> </div>
@ -43,6 +61,7 @@ import { onDeactivated, onUnmounted, Ref, ref, watch, computed } from 'vue';
import { Interpreter, Parser, utils } from '@syuilo/aiscript'; import { Interpreter, Parser, utils } from '@syuilo/aiscript';
import MkContainer from '@/components/MkContainer.vue'; import MkContainer from '@/components/MkContainer.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkCodeEditor from '@/components/MkCodeEditor.vue'; import MkCodeEditor from '@/components/MkCodeEditor.vue';
import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js'; import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
@ -61,6 +80,7 @@ const logs = ref<any[]>([]);
const root = ref<AsUiRoot>(); const root = ref<AsUiRoot>();
const components = ref<Ref<AsUiComponent>[]>([]); const components = ref<Ref<AsUiComponent>[]>([]);
const uiKey = ref(0); const uiKey = ref(0);
const uiInspectorOpenedComponents = ref(new Map<string, boolean>);
const saved = miLocalStorage.getItem('scratchpad'); const saved = miLocalStorage.getItem('scratchpad');
if (saved) { if (saved) {
@ -71,6 +91,14 @@ watch(code, () => {
miLocalStorage.setItem('scratchpad', code.value); miLocalStorage.setItem('scratchpad', code.value);
}); });
function stringifyUiProps(uiProps) {
return JSON.stringify(
{ ...uiProps, type: undefined, id: undefined },
(k, v) => typeof v === 'function' ? '<function>' : v,
2
);
}
async function run() { async function run() {
if (aiscript) aiscript.abort(); if (aiscript) aiscript.abort();
root.value = undefined; root.value = undefined;
@ -192,4 +220,35 @@ definePageMetadata(() => ({
} }
} }
} }
.uiInspector {
display: grid;
gap: 8px;
padding: 16px;
}
.uiInspectorType {
display: inline-block;
border: hidden;
border-radius: 10px;
background-color: var(--panelHighlight);
padding: 2px 8px;
font-size: 12px;
}
.uiInspectorId {
display: inline-block;
padding-left: 8px;
}
.uiInspectorDescription {
display: block;
font-size: 12px;
padding-top: 16px;
}
.uiInspectorPropsToggle {
background: none;
border: none;
}
</style> </style>