+
{{ i18n.tsx.thereAreNChanges({ n: form.modifiedCount.value }) }}
@@ -289,6 +292,9 @@ const patronsWithIcon = [{
}, {
name: 'NigN',
icon: 'https://assets.misskey-hub.net/patrons/1ccaef8e73ec4a50b59ff7cd688ceb84.jpg',
+}, {
+ name: 'しゃどかの',
+ icon: 'https://assets.misskey-hub.net/patrons/5bec3c6b402942619e03f7a2ae76d69e.jpg',
}];
const patrons = [
@@ -403,6 +409,7 @@ const patrons = [
'東雲 琥珀',
'ほとラズ',
'スズカケン',
+ '蒼井よみこ',
];
const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index bd919146f8..7ed280358a 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
Turnstile
testCaptcha
{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})
-
+
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index bf4911f648..d2c2621a35 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -88,7 +88,7 @@ let choices = [
]
// シードが「PlayID+ユーザーID+今日の日付」である乱数生成器を用意
-let random = Math:gen_rng(\`{THIS_ID}{USER_ID}{Date:year()}{Date:month()}{Date:day()}\`, { algorithm: 'rc4_legacy' })
+let random = Math:gen_rng(\`{THIS_ID}{USER_ID}{Date:year()}{Date:month()}{Date:day()}\`)
// ランダムに選択肢を選ぶ
let chosen = choices[random(0, (choices.len - 1))]
diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue
index 30ab2ce11e..730cce183a 100644
--- a/packages/frontend/src/pages/settings/other.vue
+++ b/packages/frontend/src/pages/settings/other.vue
@@ -99,6 +99,9 @@ SPDX-License-Identifier: AGPL-3.0-only
Enable folder page view
+
+ Enable haptic feedback
+
@@ -173,6 +176,7 @@ const skipNoteRender = prefer.model('skipNoteRender');
const devMode = prefer.model('devMode');
const stackingRouterView = prefer.model('experimental.stackingRouterView');
const enableFolderPageView = prefer.model('experimental.enableFolderPageView');
+const enableHapticFeedback = prefer.model('experimental.enableHapticFeedback');
watch(skipNoteRender, () => {
suggestReload();
diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue
index 3977359c54..ab012841dc 100644
--- a/packages/frontend/src/pages/settings/privacy.vue
+++ b/packages/frontend/src/pages/settings/privacy.vue
@@ -125,16 +125,20 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
-
-
-
-
-
-
+
+
+
+
+ {{ i18n.ts._time.month }}
+
+
{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}
-
-
-
-
-
-
-
-
+
+
+
+
+ {{ i18n.ts._time.month }}
+
+
{
}
});
+const makeNotesFollowersOnlyBefore_presets = [
+ { label: i18n.ts.oneHour, value: -3600 },
+ { label: i18n.ts.oneDay, value: -86400 },
+ { label: i18n.ts.threeDays, value: -259200 },
+ { label: i18n.ts.oneWeek, value: -604800 },
+ { label: i18n.ts.oneMonth, value: -2592000 },
+ { label: i18n.ts.threeMonths, value: -7776000 },
+ { label: i18n.ts.oneYear, value: -31104000 },
+];
+
+const makeNotesFollowersOnlyBefore_isCustomMode = ref(
+ makeNotesFollowersOnlyBefore.value != null &&
+ makeNotesFollowersOnlyBefore.value < 0 &&
+ !makeNotesFollowersOnlyBefore_presets.some((preset) => preset.value === makeNotesFollowersOnlyBefore.value)
+);
+
+const makeNotesFollowersOnlyBefore_selection = computed({
+ get: () => makeNotesFollowersOnlyBefore_isCustomMode.value ? 'custom' : makeNotesFollowersOnlyBefore.value,
+ set(value) {
+ makeNotesFollowersOnlyBefore_isCustomMode.value = value === 'custom';
+ if (value !== 'custom') makeNotesFollowersOnlyBefore.value = value;
+ }
+});
+
+const makeNotesFollowersOnlyBefore_customMonths = computed({
+ get: () => makeNotesFollowersOnlyBefore.value ? Math.abs(makeNotesFollowersOnlyBefore.value) / (30 * 24 * 60 * 60) : null,
+ set(value) {
+ if (value != null && value > 0) makeNotesFollowersOnlyBefore.value = -Math.abs(Math.floor(Number(value))) * 30 * 24 * 60 * 60;
+ }
+});
+
const makeNotesHiddenBefore_type = computed(() => {
if (makeNotesHiddenBefore.value == null) {
return null;
@@ -251,6 +290,37 @@ const makeNotesHiddenBefore_type = computed(() => {
}
});
+const makeNotesHiddenBefore_presets = [
+ { label: i18n.ts.oneHour, value: -3600 },
+ { label: i18n.ts.oneDay, value: -86400 },
+ { label: i18n.ts.threeDays, value: -259200 },
+ { label: i18n.ts.oneWeek, value: -604800 },
+ { label: i18n.ts.oneMonth, value: -2592000 },
+ { label: i18n.ts.threeMonths, value: -7776000 },
+ { label: i18n.ts.oneYear, value: -31104000 },
+];
+
+const makeNotesHiddenBefore_isCustomMode = ref(
+ makeNotesHiddenBefore.value != null &&
+ makeNotesHiddenBefore.value < 0 &&
+ !makeNotesHiddenBefore_presets.some((preset) => preset.value === makeNotesHiddenBefore.value)
+);
+
+const makeNotesHiddenBefore_selection = computed({
+ get: () => makeNotesHiddenBefore_isCustomMode.value ? 'custom' : makeNotesHiddenBefore.value,
+ set(value) {
+ makeNotesHiddenBefore_isCustomMode.value = value === 'custom';
+ if (value !== 'custom') makeNotesHiddenBefore.value = value;
+ }
+});
+
+const makeNotesHiddenBefore_customMonths = computed({
+ get: () => makeNotesHiddenBefore.value ? Math.abs(makeNotesHiddenBefore.value) / (30 * 24 * 60 * 60) : null,
+ set(value) {
+ if (value != null && value > 0) makeNotesHiddenBefore.value = -Math.abs(Math.floor(Number(value))) * 30 * 24 * 60 * 60;
+ }
+});
+
watch([makeNotesFollowersOnlyBefore, makeNotesHiddenBefore], () => {
save();
});
diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts
index f6370c8c78..7b045687d6 100644
--- a/packages/frontend/src/preferences/def.ts
+++ b/packages/frontend/src/preferences/def.ts
@@ -498,4 +498,7 @@ export const PREF_DEF = definePreferences({
'experimental.enableFolderPageView': {
default: false,
},
+ 'experimental.enableHapticFeedback': {
+ default: false,
+ },
});
diff --git a/packages/frontend/src/utility/autocomplete.ts b/packages/frontend/src/utility/autocomplete.ts
index 1246c32554..82109af1a0 100644
--- a/packages/frontend/src/utility/autocomplete.ts
+++ b/packages/frontend/src/utility/autocomplete.ts
@@ -78,7 +78,10 @@ export class Autocomplete {
const caretPos = Number(this.textarea.selectionStart);
const text = this.text.substring(0, caretPos).split('\n').pop()!;
- const mentionIndex = text.lastIndexOf('@');
+ // メンションに含められる文字のみで構成された、最も末尾にある文字列を抽出
+ const mentionCandidate = text.split(/[^a-zA-Z0-9_@.\-]+/).pop()!;
+
+ const mentionIndex = mentionCandidate.lastIndexOf('@');
const hashtagIndex = text.lastIndexOf('#');
const emojiIndex = text.lastIndexOf(':');
const mfmTagIndex = text.lastIndexOf('$');
@@ -97,7 +100,7 @@ export class Autocomplete {
const afterLastMfmParam = text.split(/\$\[[a-zA-Z]+/).pop();
- const isMention = mentionIndex !== -1;
+ const maybeMention = mentionIndex !== -1;
const isHashtag = hashtagIndex !== -1;
const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam.includes(' ');
const isMfmTag = mfmTagIndex !== -1 && !isMfmParam;
@@ -107,20 +110,27 @@ export class Autocomplete {
let opened = false;
- if (isMention && this.onlyType.includes('user')) {
+ if (maybeMention && this.onlyType.includes('user')) {
// ユーザのサジェスト中に@を入力すると、その位置から新たにユーザ名を取りなおそうとしてしまう
// この動きはリモートユーザのサジェストを阻害するので、@を検知したらその位置よりも前の@を探し、
// ホスト名を含むリモートのユーザ名を全て拾えるようにする
- const mentionIndexAlt = text.lastIndexOf('@', mentionIndex - 1);
- const username = mentionIndexAlt === -1
- ? text.substring(mentionIndex + 1)
- : text.substring(mentionIndexAlt + 1);
- if (username !== '' && username.match(/^[a-zA-Z0-9_@.]+$/)) {
- this.open('user', username);
- opened = true;
- } else if (username === '') {
- this.open('user', null);
- opened = true;
+ const mentionIndexAlt = mentionCandidate.lastIndexOf('@', mentionIndex - 1);
+
+ // @が連続している場合、1つ目を無視する
+ const mentionIndexLeft = (mentionIndexAlt !== -1 && mentionIndexAlt !== mentionIndex - 1) ? mentionIndexAlt : mentionIndex;
+
+ // メンションを構成する条件を満たしているか確認する
+ const isMention = mentionIndexLeft === 0 || '_@.-'.includes(mentionCandidate[mentionIndexLeft - 1]);
+
+ if (isMention) {
+ const username = mentionCandidate.substring(mentionIndexLeft + 1);
+ if (username !== '' && username.match(/^[a-zA-Z0-9_@.\-]+$/)) {
+ this.open('user', username);
+ opened = true;
+ } else if (username === '') {
+ this.open('user', null);
+ opened = true;
+ }
}
}
diff --git a/packages/frontend/src/utility/haptic.ts b/packages/frontend/src/utility/haptic.ts
new file mode 100644
index 0000000000..6f4706d202
--- /dev/null
+++ b/packages/frontend/src/utility/haptic.ts
@@ -0,0 +1,13 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { haptic as _haptic } from 'ios-haptics';
+import { prefer } from '@/preferences.js';
+
+export function haptic() {
+ if (prefer.s['experimental.enableHapticFeedback']) {
+ _haptic();
+ }
+}
diff --git a/packages/frontend/test/aiscript/ui.test.ts b/packages/frontend/test/aiscript/ui.test.ts
index c9df070bec..dc4041b135 100644
--- a/packages/frontend/test/aiscript/ui.test.ts
+++ b/packages/frontend/test/aiscript/ui.test.ts
@@ -320,7 +320,7 @@ describe('AiScript UI API', () => {
const { root, get, outputs } = await exe(`
let text_input = Ui:C:textInput({
onInput: print
- "default": 'a'
+ default: 'a'
label: 'b'
caption: 'c'
}, 'id')
@@ -361,7 +361,7 @@ describe('AiScript UI API', () => {
const { root, get, outputs } = await exe(`
let textarea = Ui:C:textarea({
onInput: print
- "default": 'a'
+ default: 'a'
label: 'b'
caption: 'c'
}, 'id')
@@ -402,7 +402,7 @@ describe('AiScript UI API', () => {
const { root, get, outputs } = await exe(`
let number_input = Ui:C:numberInput({
onInput: print
- "default": 1
+ default: 1
label: 'a'
caption: 'b'
}, 'id')
@@ -564,7 +564,7 @@ describe('AiScript UI API', () => {
const { root, get, outputs } = await exe(`
let switch = Ui:C:switch({
onChange: print
- "default": false
+ default: false
label: 'a'
caption: 'b'
}, 'id')
@@ -609,7 +609,7 @@ describe('AiScript UI API', () => {
{ text: 'B', value: 'b' }
]
onChange: print
- "default": 'a'
+ default: 'a'
label: 'c'
caption: 'd'
}, 'id')
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 160a34e062..b74afa9115 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
- "version": "2025.8.0-alpha.9",
+ "version": "2025.8.0-beta.0",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d9bd004206..c54d7aa264 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -731,8 +731,8 @@ importers:
specifier: 10.0.0
version: 10.0.0(vue@3.5.18(typescript@5.9.2))
'@syuilo/aiscript':
- specifier: 1.0.0
- version: 1.0.0
+ specifier: 1.1.0
+ version: 1.1.0
'@syuilo/aiscript-0-19-0':
specifier: npm:@syuilo/aiscript@^0.19.0
version: '@syuilo/aiscript@0.19.0'
@@ -811,6 +811,9 @@ importers:
insert-text-at-cursor:
specifier: 0.3.0
version: 0.3.0
+ ios-haptics:
+ specifier: 0.1.0
+ version: 0.1.0
is-file-animated:
specifier: 1.0.2
version: 1.0.2
@@ -4193,8 +4196,8 @@ packages:
'@syuilo/aiscript@0.19.0':
resolution: {integrity: sha512-ZWG4s1m6RrFjE7NeIMaxFz769YO1jW5ReTrOROrEO4IHheOrjxxJ/Ffe2TUNqX9/XxDloMwfWplKhfSzx8LGMA==}
- '@syuilo/aiscript@1.0.0':
- resolution: {integrity: sha512-m+Dxx0g2pDI198OCj/OJgiJnE4ajlbOFAMyh84FmbY1S8ss/MHytxY82dCnMZj5WVt7VE7a1rtW7biuRRfuyaA==}
+ '@syuilo/aiscript@1.1.0':
+ resolution: {integrity: sha512-3S6+tWC6f8WD8nnCgXSkqzPlEL9iKH9cfjERrk3OEuVQy+qP3wSLRszjy9FEHeYtvg90erUCGhTuYUy4XCNnmg==}
'@szmarczak/http-timer@5.0.1':
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
@@ -7179,6 +7182,9 @@ packages:
resolution: {integrity: sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==}
engines: {node: '>=12.22.0'}
+ ios-haptics@0.1.0:
+ resolution: {integrity: sha512-Fk0RApBYJeZNZ9pW3Wx3WcunhdLlpEnVNy/BOn85tx39eZDOHLGhXEb7medoIURGBUjXatOZf5Ozy0+OG466YA==}
+
ip-address@9.0.5:
resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
engines: {node: '>= 12'}
@@ -14450,7 +14456,7 @@ snapshots:
stringz: 2.1.0
uuid: 9.0.1
- '@syuilo/aiscript@1.0.0':
+ '@syuilo/aiscript@1.1.0':
dependencies:
seedrandom: 3.0.5
stringz: 2.1.0
@@ -18221,6 +18227,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ ios-haptics@0.1.0: {}
+
ip-address@9.0.5:
dependencies:
jsbn: 1.1.0