Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
263d476ffe | ||
|
|
3ab64ac688 | ||
|
|
ca76ba1871 | ||
|
|
9f768b8022 | ||
|
|
4ff826eb3d | ||
|
|
60bcb9c6a9 | ||
|
|
0c85bfd56f | ||
|
|
9d37f696b4 | ||
|
|
2734ff6af7 | ||
|
|
456504cf82 | ||
|
|
81cea6aed5 | ||
|
|
2d1b7c957a | ||
|
|
5b6aa1496a | ||
|
|
259dd34b26 |
@@ -1,3 +1,15 @@
|
||||
## Unreleased
|
||||
|
||||
### General
|
||||
-
|
||||
|
||||
### Client
|
||||
- Fix: アプリ内からキャッシュをクリアするとテーマ再適用するまでレンダリングが正しく行われない問題を修正
|
||||
|
||||
### Server
|
||||
-
|
||||
|
||||
|
||||
## 2025.10.1
|
||||
|
||||
### General
|
||||
@@ -13,6 +25,7 @@
|
||||
- Fix: 一部のブラウザでバナー画像が上下中央に表示されない問題を修正
|
||||
- Fix: ナビゲーションバーの設定で削除した項目をその場で再追加できない問題を修正
|
||||
- Fix: ロールポリシーによりダイレクトメッセージが無効化されている際のデッキのダイレクトメッセージカラムの挙動を改善
|
||||
- Fix: 画像のマスクでタッチ操作が不安定な問題を修正
|
||||
- Fix: ウォーターマークの各種挙動修正
|
||||
- ウォーターマークを回転させると歪む問題を修正
|
||||
- ウォーターマークを敷き詰めると上下左右反転した画像/文字が表示される問題を修正
|
||||
|
||||
@@ -3201,6 +3201,7 @@ _watermarkEditor:
|
||||
title: "워터마크 편집"
|
||||
cover: "전체에 붙이기"
|
||||
repeat: "전면에 깔기"
|
||||
preserveBoundingRect: "회전 시 빠져나오지 않도록 조정"
|
||||
opacity: "불투명도"
|
||||
scale: "크기"
|
||||
text: "텍스트"
|
||||
|
||||
@@ -3201,6 +3201,7 @@ _watermarkEditor:
|
||||
title: "编辑水印"
|
||||
cover: "覆盖全体"
|
||||
repeat: "平铺"
|
||||
preserveBoundingRect: "调整为旋转时不超出范围"
|
||||
opacity: "不透明度"
|
||||
scale: "大小"
|
||||
text: "文本"
|
||||
|
||||
@@ -3200,6 +3200,7 @@ _watermarkEditor:
|
||||
title: "編輯浮水印"
|
||||
cover: "覆蓋整體"
|
||||
repeat: "佈局"
|
||||
preserveBoundingRect: "調整使其在旋轉時不會突出"
|
||||
opacity: "透明度"
|
||||
scale: "大小"
|
||||
text: "文字"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2025.10.1-beta.2",
|
||||
"version": "2025.10.1",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div :class="$style.root">
|
||||
<div :class="$style.container">
|
||||
<div :class="$style.preview">
|
||||
<canvas ref="canvasEl" :class="$style.previewCanvas" @pointerdown="onImagePointerdown"></canvas>
|
||||
<canvas ref="canvasEl" :class="$style.previewCanvas" @pointerdown.prevent.stop="onImagePointerdown"></canvas>
|
||||
<div :class="$style.previewContainer">
|
||||
<div class="_acrylic" :class="$style.previewTitle">{{ i18n.ts.preview }}</div>
|
||||
<div class="_acrylic" :class="$style.editControls">
|
||||
@@ -242,31 +242,27 @@ function onImagePointerdown(ev: PointerEvent) {
|
||||
|
||||
const AW = canvasEl.value.clientWidth;
|
||||
const AH = canvasEl.value.clientHeight;
|
||||
const BW = imageBitmap.width;
|
||||
const BH = imageBitmap.height;
|
||||
const BW = canvasEl.value.width; // Canvas intrinsic width (render width)
|
||||
const BH = canvasEl.value.height; // Canvas intrinsic height (render height)
|
||||
|
||||
let xOffset = 0;
|
||||
let yOffset = 0;
|
||||
// Calculate the scale factor for object-fit: contain
|
||||
const scale = Math.min(AW / BW, AH / BH);
|
||||
|
||||
// Calculate the actual rendered size of the canvas content
|
||||
const renderW = BW * scale;
|
||||
const renderH = BH * scale;
|
||||
|
||||
// Calculate the offset to center the image
|
||||
const xOffset = (AW - renderW) / 2;
|
||||
const yOffset = (AH - renderH) / 2;
|
||||
|
||||
if (AW / AH < BW / BH) { // 横長
|
||||
yOffset = AH - BH * (AW / BW);
|
||||
} else { // 縦長
|
||||
xOffset = AW - BW * (AH / BH);
|
||||
}
|
||||
const rect = canvasEl.value.getBoundingClientRect();
|
||||
const pointerOffsetX = ev.clientX - rect.left;
|
||||
const pointerOffsetY = ev.clientY - rect.top;
|
||||
|
||||
xOffset /= 2;
|
||||
yOffset /= 2;
|
||||
|
||||
let startX = ev.offsetX - xOffset;
|
||||
let startY = ev.offsetY - yOffset;
|
||||
|
||||
if (AW / AH < BW / BH) { // 横長
|
||||
startX = startX / (Math.max(AW, AH) / Math.max(BH / BW, 1));
|
||||
startY = startY / (Math.max(AW, AH) / Math.max(BW / BH, 1));
|
||||
} else { // 縦長
|
||||
startX = startX / (Math.min(AW, AH) / Math.max(BH / BW, 1));
|
||||
startY = startY / (Math.min(AW, AH) / Math.max(BW / BH, 1));
|
||||
}
|
||||
// Convert from client coordinates to canvas coordinates
|
||||
let startX = (pointerOffsetX - xOffset) / scale;
|
||||
let startY = (pointerOffsetY - yOffset) / scale;
|
||||
|
||||
const id = genId();
|
||||
if (penMode.value === 'fill') {
|
||||
@@ -311,19 +307,15 @@ function onImagePointerdown(ev: PointerEvent) {
|
||||
});
|
||||
}
|
||||
|
||||
_move(ev.offsetX, ev.offsetY);
|
||||
_move(ev.clientX, ev.clientY);
|
||||
|
||||
function _move(pointerX: number, pointerY: number) {
|
||||
let x = pointerX - xOffset;
|
||||
let y = pointerY - yOffset;
|
||||
|
||||
if (AW / AH < BW / BH) { // 横長
|
||||
x = x / (Math.max(AW, AH) / Math.max(BH / BW, 1));
|
||||
y = y / (Math.max(AW, AH) / Math.max(BW / BH, 1));
|
||||
} else { // 縦長
|
||||
x = x / (Math.min(AW, AH) / Math.max(BH / BW, 1));
|
||||
y = y / (Math.min(AW, AH) / Math.max(BW / BH, 1));
|
||||
}
|
||||
function _move(pointerClientX: number, pointerClientY: number) {
|
||||
const pointerX = pointerClientX - rect.left;
|
||||
const pointerY = pointerClientY - rect.top;
|
||||
|
||||
// Convert from client coordinates to canvas coordinates
|
||||
let x = (pointerX - xOffset) / scale;
|
||||
let y = (pointerY - yOffset) / scale;
|
||||
|
||||
const scaleX = Math.abs(x - startX);
|
||||
const scaleY = Math.abs(y - startY);
|
||||
@@ -340,7 +332,7 @@ function onImagePointerdown(ev: PointerEvent) {
|
||||
}
|
||||
|
||||
function move(ev: PointerEvent) {
|
||||
_move(ev.offsetX, ev.offsetY);
|
||||
_move(ev.clientX, ev.clientY);
|
||||
}
|
||||
|
||||
function up() {
|
||||
@@ -448,6 +440,7 @@ function onImagePointerdown(ev: PointerEvent) {
|
||||
margin: 20px;
|
||||
box-sizing: border-box;
|
||||
object-fit: contain;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.controls {
|
||||
|
||||
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<component :is="link ? MkA : 'span'" v-user-preview="preview ? user.id : undefined" v-bind="bound" class="_noSelect" :class="[$style.root, { [$style.animation]: animation, [$style.cat]: user.isCat, [$style.square]: squareAvatars, [$style.mochimochi]: isMochimochi }]" :style="{ color }" :title="acct(user)" @click="onClick">
|
||||
<component :is="link ? MkA : 'span'" v-user-preview="preview ? user.id : undefined" v-bind="bound" class="_noSelect" :class="[$style.root, { [$style.animation]: animation, [$style.cat]: user.isCat, [$style.square]: squareAvatars }]" :style="{ color }" :title="acct(user)" @click="onClick">
|
||||
<MkImgWithBlurhash v-if="prefer.s.enableHighQualityImagePlaceholders" :class="$style.inner" :src="url" :hash="user.avatarBlurhash" :cover="true" :onlyAvgColor="true"/>
|
||||
<img v-else :class="$style.inner" :src="url" alt="" decoding="async" style="pointer-events: none;"/>
|
||||
<MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/>
|
||||
@@ -55,7 +55,6 @@ import { prefer } from '@/preferences.js';
|
||||
|
||||
const animation = ref(prefer.s.animation);
|
||||
const squareAvatars = ref(prefer.s.squareAvatars);
|
||||
const isMochimochi = ref(false);
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
user: Misskey.entities.User;
|
||||
@@ -92,13 +91,6 @@ const url = computed(() => {
|
||||
function onClick(ev: MouseEvent): void {
|
||||
if (props.link) return;
|
||||
emit('click', ev);
|
||||
// Trigger mochimochi animation
|
||||
if (animation.value) {
|
||||
isMochimochi.value = true;
|
||||
setTimeout(() => {
|
||||
isMochimochi.value = false;
|
||||
}, 600);
|
||||
}
|
||||
}
|
||||
|
||||
function getDecorationUrl(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
|
||||
@@ -352,29 +344,4 @@ watch(() => props.user.avatarBlurhash, () => {
|
||||
filter: brightness(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mochimochi {
|
||||
0% {
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
20% {
|
||||
transform: scale(0.9, 1.1);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1.1, 0.9);
|
||||
}
|
||||
60% {
|
||||
transform: scale(0.95, 1.05);
|
||||
}
|
||||
80% {
|
||||
transform: scale(1.02, 0.98);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.mochimochi {
|
||||
animation: mochimochi 0.6s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -295,6 +295,9 @@ const patronsWithIcon = [{
|
||||
}, {
|
||||
name: 'しゃどかの',
|
||||
icon: 'https://assets.misskey-hub.net/patrons/5bec3c6b402942619e03f7a2ae76d69e.jpg',
|
||||
}, {
|
||||
name: '大賀愛一郎',
|
||||
icon: 'https://assets.misskey-hub.net/patrons/c701a797d1df4125970f25d3052250ac.jpg',
|
||||
}];
|
||||
|
||||
const patrons = [
|
||||
|
||||
@@ -242,3 +242,9 @@ export async function installTheme(code: string): Promise<void> {
|
||||
if (!theme) return;
|
||||
await addTheme(theme);
|
||||
}
|
||||
|
||||
export function clearAppliedThemeCache() {
|
||||
miLocalStorage.removeItem('theme');
|
||||
miLocalStorage.removeItem('themeId');
|
||||
miLocalStorage.removeItem('themeCachedVersion');
|
||||
}
|
||||
|
||||
@@ -9,14 +9,15 @@ import * as os from '@/os.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
||||
import { fetchInstance } from '@/instance.js';
|
||||
import { clearAppliedThemeCache } from '@/theme.js';
|
||||
|
||||
export async function clearCache() {
|
||||
os.waiting();
|
||||
miLocalStorage.removeItem('instance');
|
||||
miLocalStorage.removeItem('instanceCachedAt');
|
||||
miLocalStorage.removeItem('theme');
|
||||
miLocalStorage.removeItem('emojis');
|
||||
miLocalStorage.removeItem('lastEmojisFetchedAt');
|
||||
clearAppliedThemeCache();
|
||||
await misskeyApiGet('clear-browser-cache', {}).catch(() => {
|
||||
// ignore
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "misskey-js",
|
||||
"version": "2025.10.1-beta.2",
|
||||
"version": "2025.10.1",
|
||||
"description": "Misskey SDK for JavaScript",
|
||||
"license": "MIT",
|
||||
"main": "./built/index.js",
|
||||
|
||||
Reference in New Issue
Block a user