Compare commits

..
Author SHA1 Message Date
copilot-swe-agent[bot]andsyuilo 263d476ffe Fix iOS touch position offset in image masking
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-10-25 06:22:35 +00:00
copilot-swe-agent[bot] 3ab64ac688 Initial plan 2025-10-25 06:17:02 +00:00
syuilo ca76ba1871 Update CHANGELOG.md 2025-10-25 08:47:51 +09:00
syuilo 9f768b8022 fix(frontend): キャッシュをクリアするとテーマ再適用するまでレンダリングが正しく行われない問題を修正 2025-10-25 08:22:47 +09:00
github-actions[bot] 4ff826eb3d [skip ci] Update CHANGELOG.md (prepend template) 2025-10-24 06:31:38 +00:00
github-actions[bot] 60bcb9c6a9 Release: 2025.10.1 2025-10-24 06:31:32 +00:00
github-actions[bot] 0c85bfd56f Bump version to 2025.10.1-beta.4 2025-10-24 02:28:37 +00:00
syuilo 9d37f696b4 New Crowdin updates (#16692)
* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Chinese Traditional)
2025-10-24 11:15:30 +09:00
syuilo 2734ff6af7 Update CHANGELOG.md 2025-10-24 11:15:11 +09:00
Copilotandsyuilo 456504cf82 Fix touch position offset in image masking on iOS devices (#16702)
* Initial plan

* Fix iOS touch position offset in image masking feature

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

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-10-24 11:14:12 +09:00
syuilo 81cea6aed5 Update MkImageEffectorDialog.vue
related? #16703
2025-10-24 11:01:20 +09:00
syuilo 2d1b7c957a Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2025-10-24 10:41:55 +09:00
syuilo 5b6aa1496a Update about-misskey.vue 2025-10-24 10:40:11 +09:00
github-actions[bot] 259dd34b26 Bump version to 2025.10.1-beta.3 2025-10-23 10:25:35 +00:00
syuilo cf81406fae Fix audio track loss during video compression on iOS
修正: iOSで動画の圧縮を行うと音声トラックが失われる問題を修正
2025-10-23 19:00:28 +09:00
Copilotandsyuilo 42f230f223 Fix video audio loss during compression by preserving audio stream (#16696)
* Initial plan

* Fix video audio loss during compression by specifying AAC codec

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

* Fix audio by avoiding transcoding - copy audio stream directly

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

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
2025-10-23 18:59:12 +09:00
かっこかり 2e07e50bb4 refactor(frontend): 既存のGLSLを単独のファイルに移行 (#16677)
* refactor(frontend): 既存のGLSLを単独のファイルに移行

* fix: glslファイルを参照元ファイルと同じ場所に移動
2025-10-23 11:05:21 +09:00
かっこかり d203e1a446 refactor(frontend): フロントエンドの型エラー解消 (#16694) 2025-10-23 11:04:27 +09:00
github-actions[bot] 4988719a2e Bump version to 2025.10.1-beta.2 2025-10-22 02:13:11 +00:00
syuilo f0380f2d1c enhance(frontend): improve theme apply handling 2025-10-22 11:11:30 +09:00
27 changed files with 286 additions and 235 deletions
+14
View File
@@ -1,3 +1,15 @@
## Unreleased
### General
-
### Client
- Fix: アプリ内からキャッシュをクリアするとテーマ再適用するまでレンダリングが正しく行われない問題を修正
### Server
-
## 2025.10.1
### General
@@ -13,12 +25,14 @@
- Fix: 一部のブラウザでバナー画像が上下中央に表示されない問題を修正
- Fix: ナビゲーションバーの設定で削除した項目をその場で再追加できない問題を修正
- Fix: ロールポリシーによりダイレクトメッセージが無効化されている際のデッキのダイレクトメッセージカラムの挙動を改善
- Fix: 画像のマスクでタッチ操作が不安定な問題を修正
- Fix: ウォーターマークの各種挙動修正
- ウォーターマークを回転させると歪む問題を修正
- ウォーターマークを敷き詰めると上下左右反転した画像/文字が表示される問題を修正
- ウォーターマークを回転させた際に画面からはみ出た部分を考慮できるように
- Fix: 投票が終了した後に投票結果が正しく表示されない問題を修正
- Fix: ダークモードの同期が機能しない場合がある問題を修正
- Fix: iOSで動画の圧縮を行うと音声トラックが失われる問題を修正
### Server
- Enhance: 管理者/モデレーターはファイルのアップロード制限をバイパスするように
+1
View File
@@ -3201,6 +3201,7 @@ _watermarkEditor:
title: "워터마크 편집"
cover: "전체에 붙이기"
repeat: "전면에 깔기"
preserveBoundingRect: "회전 시 빠져나오지 않도록 조정"
opacity: "불투명도"
scale: "크기"
text: "텍스트"
+1
View File
@@ -3201,6 +3201,7 @@ _watermarkEditor:
title: "编辑水印"
cover: "覆盖全体"
repeat: "平铺"
preserveBoundingRect: "调整为旋转时不超出范围"
opacity: "不透明度"
scale: "大小"
text: "文本"
+1
View File
@@ -3200,6 +3200,7 @@ _watermarkEditor:
title: "編輯浮水印"
cover: "覆蓋整體"
repeat: "佈局"
preserveBoundingRect: "調整使其在旋轉時不會突出"
opacity: "透明度"
scale: "大小"
text: "文字"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "2025.10.1-beta.1",
"version": "2025.10.1",
"codename": "nasubi",
"repository": {
"type": "git",
+8 -3
View File
@@ -39,13 +39,18 @@ for (let i = 0; i < emojilist.length; i++) {
export const emojiCharByCategory = _charGroupByCategory;
export function getUnicodeEmoji(char: string): UnicodeEmojiDef | string {
export function getUnicodeEmojiOrNull(char: string): UnicodeEmojiDef | null {
// Colorize it because emojilist.json assumes that
return unicodeEmojisMap.get(colorizeEmoji(char))
// カラースタイル絵文字がjsonに無い場合はテキストスタイル絵文字にフォールバックする
?? unicodeEmojisMap.get(char)
// それでも見つからない場合はそのまま返す(絵文字情報がjsonに無い場合、このフォールバックが無いとレンダリングに失敗する)
?? char;
// それでも見つからない場合はnullを返す
?? null;
}
export function getUnicodeEmoji(char: string): UnicodeEmojiDef | string {
// 絵文字が見つからない場合はそのまま返す(絵文字情報がjsonに無い場合、このフォールバックが無いとレンダリングに失敗する)
return getUnicodeEmojiOrNull(char) ?? char;
}
export function isSupportedEmoji(char: string): boolean {
-3
View File
@@ -69,9 +69,6 @@ export async function common(createVue: () => Promise<App<Element>>) {
if (lastVersion !== version) {
miLocalStorage.setItem('lastVersion', version);
// テーマリビルドするため
miLocalStorage.removeItem('theme');
try { // 変なバージョン文字列来るとcompareVersionsでエラーになるため
if (lastVersion != null && compareVersions(version, lastVersion) === 1) {
isClientUpdated = true;
@@ -0,0 +1,111 @@
#version 300 es
precision mediump float;
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x*34.0)+1.0)*x);
}
float snoise(vec2 v) {
const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
vec2 i = floor(v + dot(v, C.yy));
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod289(i);
vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
m = m*m;
m = m*m;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
in vec2 in_uv;
uniform float u_time;
uniform vec2 u_resolution;
uniform float u_spread;
uniform float u_speed;
uniform float u_warp;
uniform float u_focus;
uniform float u_itensity;
out vec4 out_color;
float circle(in vec2 _pos, in vec2 _origin, in float _radius) {
float SPREAD = 0.7 * u_spread;
float SPEED = 0.00055 * u_speed;
float WARP = 1.5 * u_warp;
float FOCUS = 1.15 * u_focus;
vec2 dist = _pos - _origin;
float distortion = snoise(vec2(
_pos.x * 1.587 * WARP + u_time * SPEED * 0.5,
_pos.y * 1.192 * WARP + u_time * SPEED * 0.3
)) * 0.5 + 0.5;
float feather = 0.01 + SPREAD * pow(distortion, FOCUS);
return 1.0 - smoothstep(
_radius - (_radius * feather),
_radius + (_radius * feather),
dot( dist, dist ) * 4.0
);
}
void main() {
vec3 green = vec3(1.0) - vec3(153.0 / 255.0, 211.0 / 255.0, 221.0 / 255.0);
vec3 purple = vec3(1.0) - vec3(195.0 / 255.0, 165.0 / 255.0, 242.0 / 255.0);
vec3 orange = vec3(1.0) - vec3(255.0 / 255.0, 156.0 / 255.0, 136.0 / 255.0);
float ratio = u_resolution.x / u_resolution.y;
vec2 uv = vec2(in_uv.x, in_uv.y / ratio) * 0.5 + 0.5;
vec3 color = vec3(0.0);
float greenMix = snoise(in_uv * 1.31 + u_time * 0.8 * 0.00017) * 0.5 + 0.5;
float purpleMix = snoise(in_uv * 1.26 + u_time * 0.8 * -0.0001) * 0.5 + 0.5;
float orangeMix = snoise(in_uv * 1.34 + u_time * 0.8 * 0.00015) * 0.5 + 0.5;
float alphaOne = 0.35 + 0.65 * pow(snoise(vec2(u_time * 0.00012, uv.x)) * 0.5 + 0.5, 1.2);
float alphaTwo = 0.35 + 0.65 * pow(snoise(vec2((u_time + 1561.0) * 0.00014, uv.x )) * 0.5 + 0.5, 1.2);
float alphaThree = 0.35 + 0.65 * pow(snoise(vec2((u_time + 3917.0) * 0.00013, uv.x )) * 0.5 + 0.5, 1.2);
color += vec3(circle(uv, vec2(0.22 + sin(u_time * 0.000201) * 0.06, 0.80 + cos(u_time * 0.000151) * 0.06), 0.15)) * alphaOne * (purple * purpleMix + orange * orangeMix);
color += vec3(circle(uv, vec2(0.90 + cos(u_time * 0.000166) * 0.06, 0.42 + sin(u_time * 0.000138) * 0.06), 0.18)) * alphaTwo * (green * greenMix + purple * purpleMix);
color += vec3(circle(uv, vec2(0.19 + sin(u_time * 0.000112) * 0.06, 0.25 + sin(u_time * 0.000192) * 0.06), 0.09)) * alphaThree * (orange * orangeMix);
color *= u_itensity + 1.0 * pow(snoise(vec2(in_uv.y + u_time * 0.00013, in_uv.x + u_time * -0.00009)) * 0.5 + 0.5, 2.0);
vec3 inverted = vec3(1.0) - color;
out_color = vec4(color, max(max(color.x, color.y), color.z));
}
@@ -0,0 +1,15 @@
#version 300 es
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
in vec2 position;
uniform vec2 u_scale;
out vec2 in_uv;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
in_uv = position / u_scale;
}
+3 -120
View File
@@ -10,6 +10,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, onUnmounted, useTemplateRef } from 'vue';
import isChromatic from 'chromatic/isChromatic';
import vertexShaderSource from './MkAnimBg.vertex.glsl';
import fragmentShaderSource from './MkAnimBg.fragment.glsl';
import { initShaderProgram } from '@/utility/webgl.js';
const canvasEl = useTemplateRef('canvasEl');
@@ -42,126 +44,7 @@ onMounted(() => {
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const shaderProgram = initShaderProgram(gl, `#version 300 es
in vec2 position;
uniform vec2 u_scale;
out vec2 in_uv;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
in_uv = position / u_scale;
}
`, `#version 300 es
precision mediump float;
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x*34.0)+1.0)*x);
}
float snoise(vec2 v) {
const vec4 C = vec4(0.211324865405187,
0.366025403784439,
-0.577350269189626,
0.024390243902439);
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod289(i);
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
in vec2 in_uv;
uniform float u_time;
uniform vec2 u_resolution;
uniform float u_spread;
uniform float u_speed;
uniform float u_warp;
uniform float u_focus;
uniform float u_itensity;
out vec4 out_color;
float circle( in vec2 _pos, in vec2 _origin, in float _radius ) {
float SPREAD = 0.7 * u_spread;
float SPEED = 0.00055 * u_speed;
float WARP = 1.5 * u_warp;
float FOCUS = 1.15 * u_focus;
vec2 dist = _pos - _origin;
float distortion = snoise( vec2(
_pos.x * 1.587 * WARP + u_time * SPEED * 0.5,
_pos.y * 1.192 * WARP + u_time * SPEED * 0.3
) ) * 0.5 + 0.5;
float feather = 0.01 + SPREAD * pow( distortion, FOCUS );
return 1.0 - smoothstep(
_radius - ( _radius * feather ),
_radius + ( _radius * feather ),
dot( dist, dist ) * 4.0
);
}
void main() {
vec3 green = vec3( 1.0 ) - vec3( 153.0 / 255.0, 211.0 / 255.0, 221.0 / 255.0 );
vec3 purple = vec3( 1.0 ) - vec3( 195.0 / 255.0, 165.0 / 255.0, 242.0 / 255.0 );
vec3 orange = vec3( 1.0 ) - vec3( 255.0 / 255.0, 156.0 / 255.0, 136.0 / 255.0 );
float ratio = u_resolution.x / u_resolution.y;
vec2 uv = vec2( in_uv.x, in_uv.y / ratio ) * 0.5 + 0.5;
vec3 color = vec3( 0.0 );
float greenMix = snoise( in_uv * 1.31 + u_time * 0.8 * 0.00017 ) * 0.5 + 0.5;
float purpleMix = snoise( in_uv * 1.26 + u_time * 0.8 * -0.0001 ) * 0.5 + 0.5;
float orangeMix = snoise( in_uv * 1.34 + u_time * 0.8 * 0.00015 ) * 0.5 + 0.5;
float alphaOne = 0.35 + 0.65 * pow( snoise( vec2( u_time * 0.00012, uv.x ) ) * 0.5 + 0.5, 1.2 );
float alphaTwo = 0.35 + 0.65 * pow( snoise( vec2( ( u_time + 1561.0 ) * 0.00014, uv.x ) ) * 0.5 + 0.5, 1.2 );
float alphaThree = 0.35 + 0.65 * pow( snoise( vec2( ( u_time + 3917.0 ) * 0.00013, uv.x ) ) * 0.5 + 0.5, 1.2 );
color += vec3( circle( uv, vec2( 0.22 + sin( u_time * 0.000201 ) * 0.06, 0.80 + cos( u_time * 0.000151 ) * 0.06 ), 0.15 ) ) * alphaOne * ( purple * purpleMix + orange * orangeMix );
color += vec3( circle( uv, vec2( 0.90 + cos( u_time * 0.000166 ) * 0.06, 0.42 + sin( u_time * 0.000138 ) * 0.06 ), 0.18 ) ) * alphaTwo * ( green * greenMix + purple * purpleMix );
color += vec3( circle( uv, vec2( 0.19 + sin( u_time * 0.000112 ) * 0.06, 0.25 + sin( u_time * 0.000192 ) * 0.06 ), 0.09 ) ) * alphaThree * ( orange * orangeMix );
color *= u_itensity + 1.0 * pow( snoise( vec2( in_uv.y + u_time * 0.00013, in_uv.x + u_time * -0.00009 ) ) * 0.5 + 0.5, 2.0 );
vec3 inverted = vec3( 1.0 ) - color;
out_color = vec4(color, max(max(color.x, color.y), color.z));
}
`);
const shaderProgram = initShaderProgram(gl, vertexShaderSource, fragmentShaderSource);
if (shaderProgram == null) return;
gl.useProgram(shaderProgram);
@@ -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 {
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, inject, onMounted, useTemplateRef, watch } from 'vue';
import * as Misskey from 'misskey-js';
import { getUnicodeEmoji } from '@@/js/emojilist.js';
import { getUnicodeEmojiOrNull } from '@@/js/emojilist.js';
import MkCustomEmojiDetailedDialog from './MkCustomEmojiDetailedDialog.vue';
import type { MenuItem } from '@/types/menu';
import XDetails from '@/components/MkReactionsViewer.details.vue';
@@ -60,11 +60,11 @@ const buttonEl = useTemplateRef('buttonEl');
const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, ''));
const canToggle = computed(() => {
const emoji = customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction);
const emoji = customEmojisMap.get(emojiName.value) ?? getUnicodeEmojiOrNull(props.reaction);
// TODO
//return !props.reaction.match(/@\w/) && $i && emoji && checkReactionPermissions($i, props.note, emoji);
return !props.reaction.match(/@\w/) && $i && emoji;
return props.reaction.match(/@\w/) == null && $i != null && emoji != null;
});
const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':'));
const isLocalCustomEmoji = props.reaction[0] === ':' && props.reaction.includes('@.');
@@ -64,6 +64,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts">
import type { Awaitable } from '@/types/misc.js';
export type SuperMenuDef = {
title?: string;
items: ({
@@ -80,7 +82,7 @@ export type SuperMenuDef = {
text: string;
danger?: boolean;
active?: boolean;
action: (ev: MouseEvent) => void | Promise<void>;
action: (ev: MouseEvent) => Awaitable<void>;
} | {
type?: 'link';
to: string;
@@ -634,7 +634,9 @@ export function useUploader(options: {
bitrate: item.compressionLevel === 1 ? mediabunny.QUALITY_VERY_HIGH : item.compressionLevel === 2 ? mediabunny.QUALITY_MEDIUM : mediabunny.QUALITY_VERY_LOW,
},
audio: {
bitrate: item.compressionLevel === 1 ? mediabunny.QUALITY_VERY_HIGH : item.compressionLevel === 2 ? mediabunny.QUALITY_MEDIUM : mediabunny.QUALITY_VERY_LOW,
// Explicitly keep audio (don't discard) and copy it if possible
// without re-encoding to avoid WebCodecs limitations on iOS Safari
discard: false,
},
});
+2 -1
View File
@@ -5,6 +5,7 @@
import { throttle } from 'throttle-debounce';
import type { Directive } from 'vue';
import type { Awaitable } from '@/types/misc.js';
interface HTMLElementWithObserver extends HTMLElement {
_observer_?: IntersectionObserver;
@@ -31,4 +32,4 @@ export const appearDirective = {
unmounted(src) {
if (src._observer_) src._observer_.disconnect();
},
} as Directive<HTMLElementWithObserver, () => void>;
} as Directive<HTMLElementWithObserver, (() => Awaitable<void>) | null | undefined>;
+3 -2
View File
@@ -15,7 +15,7 @@ const start = isTouchUsing ? 'touchstart' : 'mouseenter';
const end = isTouchUsing ? 'touchend' : 'mouseleave';
type TooltipDirectiveState = {
text: string;
text: string | null | undefined;
_close: null | (() => void);
showTimer: number | null;
hideTimer: number | null;
@@ -53,6 +53,7 @@ export const tooltipDirective = {
if (binding.arg === 'dialog') {
el.addEventListener('click', (ev) => {
if (binding.value == null) return;
ev.preventDefault();
ev.stopPropagation();
alert({
@@ -128,4 +129,4 @@ export const tooltipDirective = {
if (self.checkTimer) window.clearTimeout(self.checkTimer);
self.close();
},
} as Directive<TooltipDirectiveElement, string, TooltipDirectiveModifiers, TooltipDirectiveArg>;
} as Directive<TooltipDirectiveElement, string | null | undefined, TooltipDirectiveModifiers, TooltipDirectiveArg>;
@@ -131,4 +131,4 @@ export const userPreviewDirective = {
if (self == null) return;
self.preview.detach();
},
} as Directive<UserPreviewDirectiveElement, string | Misskey.entities.UserDetailed>;
} as Directive<UserPreviewDirectiveElement, string | Misskey.entities.UserDetailed | null | undefined>;
+1
View File
@@ -25,6 +25,7 @@ export type Keys = (
'bootloaderLocales' |
'theme' |
'themeId' |
'themeCachedVersion' |
'customCss' |
'chatMessageDrafts' |
'scratchpad' |
@@ -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 = [
+9 -1
View File
@@ -10,6 +10,7 @@ import tinycolor from 'tinycolor2';
import lightTheme from '@@/themes/_light.json5';
import darkTheme from '@@/themes/_dark.json5';
import JSON5 from 'json5';
import { version } from '@@/js/config.js';
import type { Ref } from 'vue';
import type { BundledTheme } from 'shiki/themes';
import { deepClone } from '@/utility/clone.js';
@@ -123,6 +124,7 @@ function applyThemeInternal(theme: Theme, persist: boolean) {
if (persist) {
miLocalStorage.setItem('theme', JSON.stringify(props));
miLocalStorage.setItem('themeId', theme.id);
miLocalStorage.setItem('themeCachedVersion', version);
miLocalStorage.setItem('colorScheme', colorScheme);
}
@@ -139,7 +141,7 @@ export function applyTheme(theme: Theme, persist = true) {
timeout = null;
}
if (theme.id === currentThemeId) return;
if (theme.id === currentThemeId && miLocalStorage.getItem('themeCachedVersion') === version) return;
currentThemeId = theme.id;
if (window.document.startViewTransition != null) {
@@ -240,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');
}
-8
View File
@@ -1,8 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
export type WithNonNullable<T, K extends keyof T> = T & { [P in K]-?: NonNullable<T[P]> };
+6
View File
@@ -0,0 +1,6 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export type Awaitable <T> = T | Promise<T>;
+2 -1
View File
@@ -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
});
@@ -0,0 +1,23 @@
#version 300 es
precision mediump float;
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
in vec4 v_color;
in float v_rotation;
uniform sampler2D u_texture;
out vec4 out_color;
void main() {
vec2 rotated = vec2(
cos(v_rotation) * (gl_PointCoord.x - 0.5) + sin(v_rotation) * (gl_PointCoord.y - 0.5) + 0.5,
cos(v_rotation) * (gl_PointCoord.y - 0.5) - sin(v_rotation) * (gl_PointCoord.x - 0.5) + 0.5
);
vec4 snowflake = texture(u_texture, rotated);
out_color = vec4(snowflake.rgb * v_color.xyz, snowflake.a * v_color.a);
}
@@ -3,59 +3,12 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import vertexSource from './snowfall-effect.vertex.glsl';
import fragmentSource from './snowfall-effect.fragment.glsl';
export class SnowfallEffect {
private VERTEX_SOURCE = `#version 300 es
in vec4 a_position;
in vec4 a_color;
in vec3 a_rotation;
in vec3 a_speed;
in float a_size;
out vec4 v_color;
out float v_rotation;
uniform float u_time;
uniform mat4 u_projection;
uniform vec3 u_worldSize;
uniform float u_gravity;
uniform float u_wind;
uniform float u_spin_factor;
uniform float u_turbulence;
void main() {
v_color = a_color;
v_rotation = a_rotation.x + (u_time * u_spin_factor) * a_rotation.y;
vec3 pos = a_position.xyz;
pos.x = mod(pos.x + u_time + u_wind * a_speed.x, u_worldSize.x * 2.0) - u_worldSize.x;
pos.y = mod(pos.y - u_time * a_speed.y * u_gravity, u_worldSize.y * 2.0) - u_worldSize.y;
pos.x += sin(u_time * a_speed.z * u_turbulence) * a_rotation.z;
pos.z += cos(u_time * a_speed.z * u_turbulence) * a_rotation.z;
gl_Position = u_projection * vec4(pos.xyz, a_position.w);
gl_PointSize = (a_size / gl_Position.w) * 100.0;
}
`;
private FRAGMENT_SOURCE = `#version 300 es
precision mediump float;
in vec4 v_color;
in float v_rotation;
uniform sampler2D u_texture;
out vec4 out_color;
void main() {
vec2 rotated = vec2(
cos(v_rotation) * (gl_PointCoord.x - 0.5) + sin(v_rotation) * (gl_PointCoord.y - 0.5) + 0.5,
cos(v_rotation) * (gl_PointCoord.y - 0.5) - sin(v_rotation) * (gl_PointCoord.x - 0.5) + 0.5
);
vec4 snowflake = texture(u_texture, rotated);
out_color = vec4(snowflake.rgb * v_color.xyz, snowflake.a * v_color.a);
}
`;
private VERTEX_SOURCE = vertexSource;
private FRAGMENT_SOURCE = fragmentSource;
private gl: WebGLRenderingContext;
private program: WebGLProgram;
@@ -0,0 +1,37 @@
#version 300 es
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
in vec4 a_position;
in vec4 a_color;
in vec3 a_rotation;
in vec3 a_speed;
in float a_size;
out vec4 v_color;
out float v_rotation;
uniform float u_time;
uniform mat4 u_projection;
uniform vec3 u_worldSize;
uniform float u_gravity;
uniform float u_wind;
uniform float u_spin_factor;
uniform float u_turbulence;
void main() {
v_color = a_color;
v_rotation = a_rotation.x + (u_time * u_spin_factor) * a_rotation.y;
vec3 pos = a_position.xyz;
pos.x = mod(pos.x + u_time + u_wind * a_speed.x, u_worldSize.x * 2.0) - u_worldSize.x;
pos.y = mod(pos.y - u_time * a_speed.y * u_gravity, u_worldSize.y * 2.0) - u_worldSize.y;
pos.x += sin(u_time * a_speed.z * u_turbulence) * a_rotation.z;
pos.z += cos(u_time * a_speed.z * u_turbulence) * a_rotation.z;
gl_Position = u_projection * vec4(pos.xyz, a_position.w);
gl_PointSize = (a_size / gl_Position.w) * 100.0;
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
"version": "2025.10.1-beta.1",
"version": "2025.10.1",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",