Compare commits

...

22 Commits

Author SHA1 Message Date
かっこかり 82fd3ae323
Merge a55fae6d6f into 3954837cfa 2025-10-04 16:17:57 +09:00
renovate[bot] 3954837cfa
fix(deps): update [root] update dependencies [skip ci] (#16576)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 15:58:30 +09:00
renovate[bot] 7ea4cad12e
chore(deps): update [misskey-js] update dependencies [skip ci] (#16543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 15:44:08 +09:00
github-actions[bot] d864e9a269 Bump version to 2025.10.0-beta.0 2025-10-04 06:40:01 +00:00
syuilo 4e0434c275
Update CHANGELOG with new features and enhancements 2025-10-04 15:38:05 +09:00
renovate[bot] e2f939080a
fix(deps): update [frontend] update dependencies [ci skip] (#16548)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 15:26:26 +09:00
renovate[bot] 6956f44d1f
chore(deps): update [github actions] update dependencies (#16545)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 15:18:21 +09:00
renovate[bot] a393d5a87e
fix(deps): update [backend] update dependencies (#16547)
* fix(deps): update [backend] update dependencies

* chore: update typeorm.patch

* fix: handle socket creation failure in HttpRequestServiceAgent

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: anatawa12 <anatawa12@icloud.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
2025-10-04 15:04:28 +09:00
syuilo 6c634de482
Bump version to 2025.10.0 in CHANGELOG
Updated version number and note for pnpm requirement.
2025-10-04 09:50:58 +09:00
syuilo fc02e0d34d chore(frontend): make enableFolderPageView false by default
see #16553
2025-10-04 08:54:49 +09:00
syuilo cb1a90ddad chore(frontend): improve usability 2025-10-04 08:53:19 +09:00
github-actions[bot] f0fb3a56a8 Bump version to 2025.10.0-alpha.0 2025-10-01 04:57:00 +00:00
かっこかり b8ae7edcec
fix(gh): add minimumReleaseAge settings to renovate [ci skip] 2025-09-28 18:28:37 +09:00
kakkokari-gtyih a55fae6d6f Merge branch 'develop' into enh-postform-uploader 2025-09-24 15:39:46 +09:00
kakkokari-gtyih 2a1e7a4270 Merge branch 'enh-postform-uploader' of https://github.com/kakkokari-gtyih/misskey into enh-postform-uploader 2025-06-29 19:58:04 +09:00
kakkokari-gtyih 26063ca7b5 Merge branch 'develop' into enh-postform-uploader 2025-06-29 19:58:02 +09:00
かっこかり 73cae46132
Merge branch 'develop' into enh-postform-uploader 2025-06-25 22:17:45 +09:00
kakkokari-gtyih 6d58e2c3e5 fix: better v-if handling 2025-06-07 16:46:53 +09:00
kakkokari-gtyih 46915d2ae3 fix: better post-posting handling 2025-06-07 16:43:57 +09:00
kakkokari-gtyih 6e3494a389 fix: better cursor handling 2025-06-07 16:39:29 +09:00
kakkokari-gtyih 5963e8c484 🎨 2025-06-07 16:31:00 +09:00
kakkokari-gtyih 73c837a1e1 enhance(frontend): refine postform uploader 2025-06-07 16:21:38 +09:00
26 changed files with 2829 additions and 2507 deletions

View File

@ -81,7 +81,7 @@ jobs:
cache: 'pnpm' cache: 'pnpm'
- run: pnpm i --frozen-lockfile - run: pnpm i --frozen-lockfile
- name: Restore eslint cache - name: Restore eslint cache
uses: actions/cache@v4.2.4 uses: actions/cache@v4.3.0
with: with:
path: ${{ env.eslint-cache-path }} path: ${{ env.eslint-cache-path }}
key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }} key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}

View File

@ -16,7 +16,7 @@ jobs:
# api-artifact # api-artifact
steps: steps:
- name: Download artifact - name: Download artifact
uses: actions/github-script@v7.0.1 uses: actions/github-script@v7.1.0
with: with:
script: | script: |
const fs = require('fs'); const fs = require('fs');

View File

@ -90,7 +90,7 @@ jobs:
env: env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Notify that Chromatic detects changes - name: Notify that Chromatic detects changes
uses: actions/github-script@v7.0.1 uses: actions/github-script@v7.1.0
if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false' if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false'
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,4 +1,4 @@
## 2025.9.1 ## 2025.10.0
### NOTE ### NOTE
- pnpm 10.16.0 が必要です - pnpm 10.16.0 が必要です
@ -7,6 +7,8 @@
- Feat: 予約投稿ができるようになりました - Feat: 予約投稿ができるようになりました
- デフォルトで作成可能数は1になっています。適宜ロールのポリシーで設定を行ってください。 - デフォルトで作成可能数は1になっています。適宜ロールのポリシーで設定を行ってください。
- Enhance: 広告ごとにセンシティブフラグを設定できるようになりました - Enhance: 広告ごとにセンシティブフラグを設定できるようになりました
- Enhance: 依存関係の更新
- Enhance: 翻訳の更新
### Client ### Client
- Feat: アカウントのQRコードを表示・読み取りできるようになりました - Feat: アカウントのQRコードを表示・読み取りできるようになりました

View File

@ -1,12 +1,12 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2025.9.1-alpha.2", "version": "2025.10.0-beta.0",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/misskey-dev/misskey.git" "url": "https://github.com/misskey-dev/misskey.git"
}, },
"packageManager": "pnpm@10.16.0", "packageManager": "pnpm@10.17.1",
"workspaces": [ "workspaces": [
"packages/frontend-shared", "packages/frontend-shared",
"packages/frontend", "packages/frontend",
@ -54,30 +54,30 @@
}, },
"dependencies": { "dependencies": {
"cssnano": "7.1.1", "cssnano": "7.1.1",
"esbuild": "0.25.9", "esbuild": "0.25.10",
"execa": "9.6.0", "execa": "9.6.0",
"fast-glob": "3.3.3", "fast-glob": "3.3.3",
"glob": "11.0.3", "glob": "11.0.3",
"ignore-walk": "7.0.0", "ignore-walk": "7.0.0",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"postcss": "8.5.6", "postcss": "8.5.6",
"tar": "7.4.3", "tar": "7.5.1",
"terser": "5.44.0", "terser": "5.44.0",
"typescript": "5.9.2" "typescript": "5.9.2"
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/eslint-plugin": "2.1.0", "@misskey-dev/eslint-plugin": "2.1.0",
"@types/js-yaml": "4.0.9", "@types/js-yaml": "4.0.9",
"@types/node": "22.18.1", "@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.42.0", "@typescript-eslint/parser": "8.44.1",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "14.5.4", "cypress": "14.5.4",
"eslint": "9.35.0", "eslint": "9.36.0",
"globals": "16.3.0", "globals": "16.4.0",
"ncp": "2.0.0", "ncp": "2.0.0",
"pnpm": "10.16.0", "pnpm": "10.17.1",
"start-server-and-test": "2.1.0" "start-server-and-test": "2.1.2"
}, },
"optionalDependencies": { "optionalDependencies": {
"@tensorflow/tfjs-core": "4.22.0" "@tensorflow/tfjs-core": "4.22.0"

View File

@ -39,17 +39,17 @@
}, },
"optionalDependencies": { "optionalDependencies": {
"@swc/core-android-arm64": "1.3.11", "@swc/core-android-arm64": "1.3.11",
"@swc/core-darwin-arm64": "1.13.5", "@swc/core-darwin-arm64": "1.13.19",
"@swc/core-darwin-x64": "1.13.5", "@swc/core-darwin-x64": "1.13.19",
"@swc/core-freebsd-x64": "1.3.11", "@swc/core-freebsd-x64": "1.3.11",
"@swc/core-linux-arm-gnueabihf": "1.13.5", "@swc/core-linux-arm-gnueabihf": "1.13.19",
"@swc/core-linux-arm64-gnu": "1.13.5", "@swc/core-linux-arm64-gnu": "1.13.19",
"@swc/core-linux-arm64-musl": "1.13.5", "@swc/core-linux-arm64-musl": "1.13.19",
"@swc/core-linux-x64-gnu": "1.13.5", "@swc/core-linux-x64-gnu": "1.13.19",
"@swc/core-linux-x64-musl": "1.13.5", "@swc/core-linux-x64-musl": "1.13.19",
"@swc/core-win32-arm64-msvc": "1.13.5", "@swc/core-win32-arm64-msvc": "1.13.19",
"@swc/core-win32-ia32-msvc": "1.13.5", "@swc/core-win32-ia32-msvc": "1.13.19",
"@swc/core-win32-x64-msvc": "1.13.5", "@swc/core-win32-x64-msvc": "1.13.19",
"@tensorflow/tfjs": "4.22.0", "@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0", "@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.0.9", "bufferutil": "4.0.9",
@ -69,8 +69,8 @@
"utf-8-validate": "6.0.5" "utf-8-validate": "6.0.5"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "3.883.0", "@aws-sdk/client-s3": "3.896.0",
"@aws-sdk/lib-storage": "3.883.0", "@aws-sdk/lib-storage": "3.895.0",
"@discordapp/twemoji": "16.0.1", "@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.2", "@fastify/accepts": "5.0.2",
"@fastify/cookie": "11.0.2", "@fastify/cookie": "11.0.2",
@ -82,7 +82,7 @@
"@fastify/view": "10.0.2", "@fastify/view": "10.0.2",
"@misskey-dev/sharp-read-bmp": "1.2.0", "@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.2.3", "@misskey-dev/summaly": "5.2.3",
"@napi-rs/canvas": "0.1.79", "@napi-rs/canvas": "0.1.80",
"@nestjs/common": "11.1.6", "@nestjs/common": "11.1.6",
"@nestjs/core": "11.1.6", "@nestjs/core": "11.1.6",
"@nestjs/testing": "11.1.6", "@nestjs/testing": "11.1.6",
@ -103,29 +103,29 @@
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blurhash": "2.0.5", "blurhash": "2.0.5",
"body-parser": "1.20.3", "body-parser": "1.20.3",
"bullmq": "5.58.5", "bullmq": "5.58.8",
"cacheable-lookup": "7.0.0", "cacheable-lookup": "7.0.0",
"cbor": "9.0.2", "cbor": "9.0.2",
"chalk": "5.6.0", "chalk": "5.6.2",
"chalk-template": "1.1.0", "chalk-template": "1.1.2",
"chokidar": "4.0.3", "chokidar": "4.0.3",
"cli-highlight": "2.1.11", "cli-highlight": "2.1.11",
"color-convert": "2.0.1", "color-convert": "2.0.1",
"content-disposition": "0.5.4", "content-disposition": "0.5.4",
"date-fns": "2.30.0", "date-fns": "2.30.0",
"deep-email-validator": "0.1.21", "deep-email-validator": "0.1.21",
"fastify": "5.6.0", "fastify": "5.6.1",
"fastify-raw-body": "5.0.0", "fastify-raw-body": "5.0.0",
"feed": "4.2.2", "feed": "4.2.2",
"file-type": "19.6.0", "file-type": "19.6.0",
"fluent-ffmpeg": "2.1.3", "fluent-ffmpeg": "2.1.3",
"form-data": "4.0.4", "form-data": "4.0.4",
"got": "14.4.8", "got": "14.4.9",
"happy-dom": "16.8.1", "happy-dom": "16.8.1",
"hpagent": "1.2.0", "hpagent": "1.2.0",
"htmlescape": "1.1.1", "htmlescape": "1.1.1",
"http-link-header": "1.1.3", "http-link-header": "1.1.3",
"ioredis": "5.7.0", "ioredis": "5.8.0",
"ip-cidr": "4.0.2", "ip-cidr": "4.0.2",
"ipaddr.js": "2.2.0", "ipaddr.js": "2.2.0",
"is-svg": "5.1.0", "is-svg": "5.1.0",
@ -135,14 +135,14 @@
"jsonld": "8.3.3", "jsonld": "8.3.3",
"jsrsasign": "11.1.0", "jsrsasign": "11.1.0",
"juice": "11.0.1", "juice": "11.0.1",
"meilisearch": "0.52.0", "meilisearch": "0.53.0",
"mfm-js": "0.25.0", "mfm-js": "0.25.0",
"microformats-parser": "2.0.4", "microformats-parser": "2.0.4",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"misskey-reversi": "workspace:*", "misskey-reversi": "workspace:*",
"ms": "3.0.0-canary.202508261828", "ms": "3.0.0-canary.202508261828",
"nanoid": "5.1.5", "nanoid": "5.1.6",
"nested-property": "4.0.0", "nested-property": "4.0.0",
"node-fetch": "3.3.2", "node-fetch": "3.3.2",
"nodemailer": "6.10.1", "nodemailer": "6.10.1",
@ -175,12 +175,12 @@
"slacc": "0.0.10", "slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0", "stringz": "2.1.0",
"systeminformation": "5.27.8", "systeminformation": "5.27.10",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tmp": "0.2.5", "tmp": "0.2.5",
"tsc-alias": "1.8.16", "tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"typeorm": "0.3.26", "typeorm": "0.3.27",
"typescript": "5.9.2", "typescript": "5.9.2",
"ulid": "2.4.0", "ulid": "2.4.0",
"vary": "1.1.2", "vary": "1.1.2",
@ -210,7 +210,7 @@
"@types/jsrsasign": "10.5.15", "@types/jsrsasign": "10.5.15",
"@types/mime-types": "2.1.4", "@types/mime-types": "2.1.4",
"@types/ms": "0.7.34", "@types/ms": "0.7.34",
"@types/node": "22.18.1", "@types/node": "22.18.6",
"@types/nodemailer": "6.4.19", "@types/nodemailer": "6.4.19",
"@types/oauth": "0.9.6", "@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5", "@types/oauth2orize": "1.11.5",
@ -231,8 +231,8 @@
"@types/vary": "1.1.3", "@types/vary": "1.1.3",
"@types/web-push": "3.6.4", "@types/web-push": "3.6.4",
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.42.0", "@typescript-eslint/parser": "8.44.1",
"aws-sdk-client-mock": "4.1.0", "aws-sdk-client-mock": "4.1.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",

View File

@ -37,17 +37,23 @@ class HttpRequestServiceAgent extends http.Agent {
@bindThis @bindThis
public createConnection(options: http.ClientRequestArgs, callback?: (err: Error | null, stream: stream.Duplex) => void): stream.Duplex { public createConnection(options: http.ClientRequestArgs, callback?: (err: Error | null, stream: stream.Duplex) => void): stream.Duplex {
const socket = super.createConnection(options, callback) const socket = super.createConnection(options, callback);
.on('connect', () => {
if (socket instanceof net.Socket && process.env.NODE_ENV === 'production') { if (socket == null) {
const address = socket.remoteAddress; throw new Error('Failed to create socket');
if (address && ipaddr.isValid(address)) { }
if (this.isPrivateIp(address)) {
socket.destroy(new Error(`Blocked address: ${address}`)); socket.on('connect', () => {
} if (socket instanceof net.Socket && process.env.NODE_ENV === 'production') {
const address = socket.remoteAddress;
if (address && ipaddr.isValid(address)) {
if (this.isPrivateIp(address)) {
socket.destroy(new Error(`Blocked address: ${address}`));
} }
} }
}); }
});
return socket; return socket;
} }
@ -76,17 +82,23 @@ class HttpsRequestServiceAgent extends https.Agent {
@bindThis @bindThis
public createConnection(options: http.ClientRequestArgs, callback?: (err: Error | null, stream: stream.Duplex) => void): stream.Duplex { public createConnection(options: http.ClientRequestArgs, callback?: (err: Error | null, stream: stream.Duplex) => void): stream.Duplex {
const socket = super.createConnection(options, callback) const socket = super.createConnection(options, callback);
.on('connect', () => {
if (socket instanceof net.Socket && process.env.NODE_ENV === 'production') { if (socket == null) {
const address = socket.remoteAddress; throw new Error('Failed to create socket');
if (address && ipaddr.isValid(address)) { }
if (this.isPrivateIp(address)) {
socket.destroy(new Error(`Blocked address: ${address}`)); socket.on('connect', () => {
} if (socket instanceof net.Socket && process.env.NODE_ENV === 'production') {
const address = socket.remoteAddress;
if (address && ipaddr.isValid(address)) {
if (this.isPrivateIp(address)) {
socket.destroy(new Error(`Blocked address: ${address}`));
} }
} }
}); }
});
return socket; return socket;
} }

View File

@ -11,15 +11,15 @@
}, },
"devDependencies": { "devDependencies": {
"@types/estree": "1.0.8", "@types/estree": "1.0.8",
"@types/node": "22.17.0", "@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.38.0", "@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.38.0", "@typescript-eslint/parser": "8.44.1",
"rollup": "4.46.2", "rollup": "4.52.2",
"typescript": "5.9.2" "typescript": "5.9.2"
}, },
"dependencies": { "dependencies": {
"estree-walker": "3.0.3", "estree-walker": "3.0.3",
"magic-string": "0.30.17", "magic-string": "0.30.19",
"vite": "7.0.7" "vite": "7.1.7"
} }
} }

View File

@ -16,7 +16,7 @@
"@rollup/pluginutils": "5.3.0", "@rollup/pluginutils": "5.3.0",
"@twemoji/parser": "16.0.0", "@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "6.0.1", "@vitejs/plugin-vue": "6.0.1",
"@vue/compiler-sfc": "3.5.21", "@vue/compiler-sfc": "3.5.22",
"astring": "1.9.0", "astring": "1.9.0",
"buraha": "0.0.1", "buraha": "0.0.1",
"estree-walker": "3.0.3", "estree-walker": "3.0.3",
@ -26,47 +26,47 @@
"mfm-js": "0.25.0", "mfm-js": "0.25.0",
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"punycode.js": "2.3.1", "punycode.js": "2.3.1",
"rollup": "4.50.1", "rollup": "4.52.2",
"sass": "1.92.1", "sass": "1.93.2",
"shiki": "3.12.2", "shiki": "3.13.0",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tsc-alias": "1.8.16", "tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"typescript": "5.9.2", "typescript": "5.9.2",
"uuid": "11.1.0", "uuid": "11.1.0",
"vite": "7.1.5", "vite": "7.1.7",
"vue": "3.5.21" "vue": "3.5.22"
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/summaly": "5.2.3", "@misskey-dev/summaly": "5.2.3",
"@tabler/icons-webfont": "3.34.1", "@tabler/icons-webfont": "3.35.0",
"@testing-library/vue": "8.1.0", "@testing-library/vue": "8.1.0",
"@types/estree": "1.0.8", "@types/estree": "1.0.8",
"@types/micromatch": "4.0.9", "@types/micromatch": "4.0.9",
"@types/node": "22.18.1", "@types/node": "22.18.6",
"@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/tinycolor2": "1.4.6", "@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.42.0", "@typescript-eslint/parser": "8.44.1",
"@vitest/coverage-v8": "3.2.4", "@vitest/coverage-v8": "3.2.4",
"@vue/runtime-core": "3.5.21", "@vue/runtime-core": "3.5.22",
"acorn": "8.15.0", "acorn": "8.15.0",
"cross-env": "10.0.0", "cross-env": "10.0.0",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.4.0", "eslint-plugin-vue": "10.5.0",
"fast-glob": "3.3.3", "fast-glob": "3.3.3",
"happy-dom": "18.0.1", "happy-dom": "18.0.1",
"intersection-observer": "0.12.2", "intersection-observer": "0.12.2",
"micromatch": "4.0.8", "micromatch": "4.0.8",
"msw": "2.11.1", "msw": "2.11.3",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"prettier": "3.6.2", "prettier": "3.6.2",
"start-server-and-test": "2.1.0", "start-server-and-test": "2.1.2",
"tsx": "4.20.5", "tsx": "4.20.6",
"vite-plugin-turbosnap": "1.0.3", "vite-plugin-turbosnap": "1.0.3",
"vue-component-type-helpers": "3.0.6", "vue-component-type-helpers": "3.0.8",
"vue-eslint-parser": "10.2.0", "vue-eslint-parser": "10.2.0",
"vue-tsc": "3.0.6" "vue-tsc": "3.0.8"
} }
} }

View File

@ -21,11 +21,11 @@
"lint": "pnpm typecheck && pnpm eslint" "lint": "pnpm typecheck && pnpm eslint"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "22.18.1", "@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.42.0", "@typescript-eslint/parser": "8.44.1",
"esbuild": "0.25.9", "esbuild": "0.25.10",
"eslint-plugin-vue": "10.4.0", "eslint-plugin-vue": "10.5.0",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"typescript": "5.9.2", "typescript": "5.9.2",
"vue-eslint-parser": "10.2.0" "vue-eslint-parser": "10.2.0"
@ -35,6 +35,6 @@
], ],
"dependencies": { "dependencies": {
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"vue": "3.5.21" "vue": "3.5.22"
} }
} }

View File

@ -24,12 +24,12 @@
"@rollup/plugin-json": "6.1.0", "@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.2", "@rollup/plugin-replace": "6.0.2",
"@rollup/pluginutils": "5.3.0", "@rollup/pluginutils": "5.3.0",
"@sentry/vue": "10.10.0", "@sentry/vue": "10.15.0",
"@syuilo/aiscript": "1.1.0", "@syuilo/aiscript": "1.1.2",
"@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0", "@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0",
"@twemoji/parser": "16.0.0", "@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "6.0.1", "@vitejs/plugin-vue": "6.0.1",
"@vue/compiler-sfc": "3.5.21", "@vue/compiler-sfc": "3.5.22",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15", "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
"analytics": "0.8.19", "analytics": "0.8.19",
"astring": "1.9.0", "astring": "1.9.0",
@ -41,7 +41,7 @@
"chartjs-chart-matrix": "3.0.0", "chartjs-chart-matrix": "3.0.0",
"chartjs-plugin-gradient": "0.6.1", "chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.2.0", "chartjs-plugin-zoom": "2.2.0",
"chromatic": "13.1.4", "chromatic": "13.2.1",
"compare-versions": "6.1.1", "compare-versions": "6.1.1",
"cropperjs": "2.0.1", "cropperjs": "2.0.1",
"date-fns": "4.1.0", "date-fns": "4.1.0",
@ -52,12 +52,12 @@
"icons-subsetter": "workspace:*", "icons-subsetter": "workspace:*",
"idb-keyval": "6.2.2", "idb-keyval": "6.2.2",
"insert-text-at-cursor": "0.3.0", "insert-text-at-cursor": "0.3.0",
"ios-haptics": "0.1.0", "ios-haptics": "0.1.4",
"is-file-animated": "1.0.2", "is-file-animated": "1.0.2",
"json5": "2.2.3", "json5": "2.2.3",
"magic-string": "0.30.18", "magic-string": "0.30.19",
"matter-js": "0.20.0", "matter-js": "0.20.0",
"mediabunny": "1.15.1", "mediabunny": "1.21.0",
"mfm-js": "0.25.0", "mfm-js": "0.25.0",
"misskey-bubble-game": "workspace:*", "misskey-bubble-game": "workspace:*",
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
@ -66,10 +66,10 @@
"punycode.js": "2.3.1", "punycode.js": "2.3.1",
"qr-code-styling": "1.9.2", "qr-code-styling": "1.9.2",
"qr-scanner": "1.4.2", "qr-scanner": "1.4.2",
"rollup": "4.50.1", "rollup": "4.52.2",
"sanitize-html": "2.17.0", "sanitize-html": "2.17.0",
"sass": "1.92.1", "sass": "1.93.2",
"shiki": "3.12.2", "shiki": "3.13.0",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
"three": "0.180.0", "three": "0.180.0",
@ -79,8 +79,8 @@
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"typescript": "5.9.2", "typescript": "5.9.2",
"v-code-diff": "1.13.1", "v-code-diff": "1.13.1",
"vite": "7.1.5", "vite": "7.1.7",
"vue": "3.5.21", "vue": "3.5.22",
"vuedraggable": "next", "vuedraggable": "next",
"wanakana": "5.3.1" "wanakana": "5.3.1"
}, },
@ -88,7 +88,7 @@
"@misskey-dev/summaly": "5.2.3", "@misskey-dev/summaly": "5.2.3",
"@storybook/addon-essentials": "8.6.14", "@storybook/addon-essentials": "8.6.14",
"@storybook/addon-interactions": "8.6.14", "@storybook/addon-interactions": "8.6.14",
"@storybook/addon-links": "9.1.5", "@storybook/addon-links": "9.1.8",
"@storybook/addon-mdx-gfm": "8.6.14", "@storybook/addon-mdx-gfm": "8.6.14",
"@storybook/addon-storysource": "8.6.14", "@storybook/addon-storysource": "8.6.14",
"@storybook/blocks": "8.6.14", "@storybook/blocks": "8.6.14",
@ -96,57 +96,57 @@
"@storybook/core-events": "8.6.14", "@storybook/core-events": "8.6.14",
"@storybook/manager-api": "8.6.14", "@storybook/manager-api": "8.6.14",
"@storybook/preview-api": "8.6.14", "@storybook/preview-api": "8.6.14",
"@storybook/react": "9.1.5", "@storybook/react": "9.1.8",
"@storybook/react-vite": "9.1.5", "@storybook/react-vite": "9.1.8",
"@storybook/test": "8.6.14", "@storybook/test": "8.6.14",
"@storybook/theming": "8.6.14", "@storybook/theming": "8.6.14",
"@storybook/types": "8.6.14", "@storybook/types": "8.6.14",
"@storybook/vue3": "9.1.5", "@storybook/vue3": "9.1.8",
"@storybook/vue3-vite": "9.1.5", "@storybook/vue3-vite": "9.1.8",
"@tabler/icons-webfont": "3.34.1", "@tabler/icons-webfont": "3.35.0",
"@testing-library/vue": "8.1.0", "@testing-library/vue": "8.1.0",
"@types/canvas-confetti": "1.9.0", "@types/canvas-confetti": "1.9.0",
"@types/estree": "1.0.8", "@types/estree": "1.0.8",
"@types/matter-js": "0.20.0", "@types/matter-js": "0.20.2",
"@types/micromatch": "4.0.9", "@types/micromatch": "4.0.9",
"@types/node": "22.18.1", "@types/node": "22.18.6",
"@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/sanitize-html": "2.16.0", "@types/sanitize-html": "2.16.0",
"@types/seedrandom": "3.0.8", "@types/seedrandom": "3.0.8",
"@types/throttle-debounce": "5.0.2", "@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6", "@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.42.0", "@typescript-eslint/parser": "8.44.1",
"@vitest/coverage-v8": "3.2.4", "@vitest/coverage-v8": "3.2.4",
"@vue/compiler-core": "3.5.21", "@vue/compiler-core": "3.5.22",
"@vue/runtime-core": "3.5.21", "@vue/runtime-core": "3.5.22",
"acorn": "8.15.0", "acorn": "8.15.0",
"cross-env": "10.0.0", "cross-env": "10.0.0",
"cypress": "14.5.4", "cypress": "14.5.4",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.4.0", "eslint-plugin-vue": "10.5.0",
"fast-glob": "3.3.3", "fast-glob": "3.3.3",
"happy-dom": "18.0.1", "happy-dom": "18.0.1",
"intersection-observer": "0.12.2", "intersection-observer": "0.12.2",
"micromatch": "4.0.8", "micromatch": "4.0.8",
"minimatch": "10.0.3", "minimatch": "10.0.3",
"msw": "2.11.1", "msw": "2.11.3",
"msw-storybook-addon": "2.0.5", "msw-storybook-addon": "2.0.5",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"prettier": "3.6.2", "prettier": "3.6.2",
"react": "19.1.1", "react": "19.1.1",
"react-dom": "19.1.1", "react-dom": "19.1.1",
"seedrandom": "3.0.5", "seedrandom": "3.0.5",
"start-server-and-test": "2.1.0", "start-server-and-test": "2.1.2",
"storybook": "9.1.5", "storybook": "9.1.8",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"tsx": "4.20.5", "tsx": "4.20.6",
"vite-plugin-turbosnap": "1.0.3", "vite-plugin-turbosnap": "1.0.3",
"vitest": "3.2.4", "vitest": "3.2.4",
"vitest-fetch-mock": "0.4.5", "vitest-fetch-mock": "0.4.5",
"vue-component-type-helpers": "3.0.6", "vue-component-type-helpers": "3.0.8",
"vue-eslint-parser": "10.2.0", "vue-eslint-parser": "10.2.0",
"vue-tsc": "3.0.6" "vue-tsc": "3.0.8"
} }
} }

View File

@ -29,14 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="$style.thumbnail" :class="$style.thumbnail"
:style="{ objectFit: fit }" :style="{ objectFit: fit }"
/> />
<i v-else-if="is === 'image'" class="ti ti-photo" :class="$style.icon"></i> <i v-else :class="[$style.icon, fileIcon]"></i>
<i v-else-if="is === 'video'" class="ti ti-video" :class="$style.icon"></i>
<i v-else-if="is === 'audio' || is === 'midi'" class="ti ti-file-music" :class="$style.icon"></i>
<i v-else-if="is === 'csv'" class="ti ti-file-text" :class="$style.icon"></i>
<i v-else-if="is === 'pdf'" class="ti ti-file-text" :class="$style.icon"></i>
<i v-else-if="is === 'textfile'" class="ti ti-file-text" :class="$style.icon"></i>
<i v-else-if="is === 'archive'" class="ti ti-file-zip" :class="$style.icon"></i>
<i v-else class="ti ti-file" :class="$style.icon"></i>
<i v-if="isThumbnailAvailable && is === 'video'" class="ti ti-video" :class="$style.iconSub"></i> <i v-if="isThumbnailAvailable && is === 'video'" class="ti ti-video" :class="$style.iconSub"></i>
</div> </div>
@ -46,6 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed } from 'vue'; import { computed } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; import MkImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
import { getFileType, getFileTypeIcon } from '@/utility/file-type.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
const props = defineProps<{ const props = defineProps<{
@ -56,27 +50,8 @@ const props = defineProps<{
large?: boolean; large?: boolean;
}>(); }>();
const is = computed(() => { const is = computed(() => getFileType(props.file.type));
if (props.file.type.startsWith('image/')) return 'image'; const fileIcon = computed(() => getFileTypeIcon(is.value));
if (props.file.type.startsWith('video/')) return 'video';
if (props.file.type === 'audio/midi') return 'midi';
if (props.file.type.startsWith('audio/')) return 'audio';
if (props.file.type.endsWith('/csv')) return 'csv';
if (props.file.type.endsWith('/pdf')) return 'pdf';
if (props.file.type.startsWith('text/')) return 'textfile';
if ([
'application/zip',
'application/x-cpio',
'application/x-bzip',
'application/x-bzip2',
'application/java-archive',
'application/x-rar-compressed',
'application/x-tar',
'application/gzip',
'application/x-7z-compressed',
].some(archiveType => archiveType === props.file.type)) return 'archive';
return 'unknown';
});
const isThumbnailAvailable = computed(() => { const isThumbnailAvailable = computed(() => {
return props.file.thumbnailUrl return props.file.thumbnailUrl

View File

@ -79,22 +79,22 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div> <div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div>
</div> </div>
<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags"> <input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags">
<XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/> <XPostFormAttaches
<div v-if="uploader.items.value.length > 0" style="padding: 12px;"> v-model="attaches"
<MkTip k="postFormUploader"> :draggable="!posting && !posted"
{{ i18n.ts._postForm.uploaderTip }} @detach="detachAttaches"
</MkTip> @uploaderItemAborted="handleUploaderItemAbort"
<MkUploaderItems :items="uploader.items.value" @showMenu="(item, ev) => showPerUploadItemMenu(item, ev)" @showMenuViaContextmenu="(item, ev) => showPerUploadItemMenuViaContextmenu(item, ev)"/> @changeDriveFileSensitivity="updateFileSensitive"
</div> @changeDriveFileName="updateFileName"
@showUploaderMenu="handleShowUploaderMenu"
/>
<MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/> <MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/>
<MkNotePreview v-if="showPreview" :class="$style.preview" :text="text" :files="files" :poll="poll ?? undefined" :useCw="useCw" :cw="cw" :user="postAccount ?? $i"/> <MkNotePreview v-if="showPreview" :class="$style.preview" :text="text" :files="files" :poll="poll ?? undefined" :useCw="useCw" :cw="cw" :user="postAccount ?? $i"/>
<div v-if="showingOptions" style="padding: 8px 16px;"> <div v-if="showingOptions" style="padding: 8px 16px;">
</div> </div>
<footer :class="$style.footer"> <footer :class="$style.footer">
<div :class="$style.footerLeft"> <div :class="$style.footerLeft">
<button v-tooltip="i18n.ts.attachFile + ' (' + i18n.ts.upload + ')'" class="_button" :class="$style.footerButton" @click="chooseFileFromPc"><i class="ti ti-photo-plus"></i></button> <button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button> <button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button>
<button v-tooltip="i18n.ts.attachFile + ' (' + i18n.ts.fromDrive + ')'" class="_button" :class="$style.footerButton" @click="chooseFileFromDrive"><i class="ti ti-cloud-download"></i></button>
<button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button>
<button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button> <button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button>
<button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button> <button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button>
<button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button> <button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button>
@ -118,7 +118,7 @@ import * as Misskey from 'misskey-js';
import insertTextAtCursor from 'insert-text-at-cursor'; import insertTextAtCursor from 'insert-text-at-cursor';
import { toASCII } from 'punycode.js'; import { toASCII } from 'punycode.js';
import { host, url } from '@@/js/config.js'; import { host, url } from '@@/js/config.js';
import MkUploaderItems from './MkUploaderItems.vue'; import type { Attach } from './MkPostFormAttaches.vue';
import type { ShallowRef } from 'vue'; import type { ShallowRef } from 'vue';
import type { PostFormProps } from '@/types/post-form.js'; import type { PostFormProps } from '@/types/post-form.js';
import type { MenuItem } from '@/types/menu.js'; import type { MenuItem } from '@/types/menu.js';
@ -135,7 +135,7 @@ import { formatTimeString } from '@/utility/format-time-string.js';
import { Autocomplete } from '@/utility/autocomplete.js'; import { Autocomplete } from '@/utility/autocomplete.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js'; import { misskeyApi } from '@/utility/misskey-api.js';
import { chooseDriveFile } from '@/utility/drive.js'; import { chooseDriveFile, chooseFileFromUrl } from '@/utility/drive.js';
import { store } from '@/store.js'; import { store } from '@/store.js';
import MkInfo from '@/components/MkInfo.vue'; import MkInfo from '@/components/MkInfo.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
@ -230,11 +230,6 @@ onUnmounted(() => {
uploader.dispose(); uploader.dispose();
}); });
uploader.events.on('itemUploaded', ctx => {
files.value.push(ctx.item.uploaded!);
uploader.removeItem(ctx.item);
});
const draftKey = computed((): string => { const draftKey = computed((): string => {
let key = targetChannel.value ? `channel:${targetChannel.value.id}` : ''; let key = targetChannel.value ? `channel:${targetChannel.value.id}` : '';
@ -297,26 +292,34 @@ const cwTextLength = computed((): number => {
const maxCwTextLength = 100; const maxCwTextLength = 100;
/**
* Computes whether the post data meets the required conditions for submission or enabling the post button.
*
* @param cond - Specifies the context in which the condition is being checked ('button' for UI button state, 'req' for request validation).
*/
function computePostDataCondition(cond: 'button' | 'req') {
return !uploader.uploading.value && (
1 <= textLength.value ||
1 <= files.value.length ||
(cond === 'button' ? (1 <= uploader.items.value.length) : false) ||
poll.value != null ||
renoteTargetNote.value != null ||
quoteId.value != null
) &&
(textLength.value <= maxTextLength.value) &&
(
useCw.value ?
(
cw.value != null && cw.value.trim() !== '' &&
cwTextLength.value <= maxCwTextLength
) : true
) &&
(files.value.length <= 16) &&
(!poll.value || poll.value.choices.length >= 2);
}
const canPost = computed((): boolean => { const canPost = computed((): boolean => {
return !props.mock && !posting.value && !posted.value && !uploader.uploading.value && (uploader.items.value.length === 0 || uploader.readyForUpload.value) && return !props.mock && !posting.value && !posted.value && !uploader.uploading.value && (uploader.items.value.length === 0 || uploader.readyForUpload.value) && computePostDataCondition('button');
(
1 <= textLength.value ||
1 <= files.value.length ||
1 <= uploader.items.value.length ||
poll.value != null ||
renoteTargetNote.value != null ||
quoteId.value != null
) &&
(textLength.value <= maxTextLength.value) &&
(
useCw.value ?
(
cw.value != null && cw.value.trim() !== '' &&
cwTextLength.value <= maxCwTextLength
) : true
) &&
(files.value.length <= 16) &&
(!poll.value || poll.value.choices.length >= 2);
}); });
// cannot save pure renote as draft // cannot save pure renote as draft
@ -481,6 +484,29 @@ function focus() {
} }
} }
function chooseFileFrom(ev: MouseEvent) {
const anchorElement = ev.currentTarget ?? ev.target;
os.popupMenu([{
text: i18n.ts.attachFile,
type: 'label',
}, {
text: i18n.ts.upload,
icon: 'ti ti-upload',
action: () => chooseFileFromPc(ev),
}, {
text: i18n.ts.fromDrive,
icon: 'ti ti-cloud',
action: () => chooseFileFromDrive(ev),
}, {
text: i18n.ts.fromUrl,
icon: 'ti ti-link',
action: () => chooseFileFromUrl().then(file => {
attachOrder.set(file.id, files.value.length);
files.value.push(file);
}),
}], anchorElement);
}
function chooseFileFromPc(ev: MouseEvent) { function chooseFileFromPc(ev: MouseEvent) {
if (props.mock) return; if (props.mock) return;
@ -498,21 +524,85 @@ function chooseFileFromDrive(ev: MouseEvent) {
}); });
} }
function detachFile(id) { uploader.events.on('itemUploaded', ({ item }) => {
files.value = files.value.filter(x => x.id !== id); if (!item.uploaded) return;
const attachesOrder = attaches.value.findIndex(f => f.id === item.id);
const attachOrderOrder = attachOrder.get(item.id);
if (attachesOrder >= 0 || attachOrderOrder != null) {
const index = attachesOrder >= 0 ? attachOrderOrder ?? attachesOrder : attachOrderOrder ?? attaches.value.length;
attachOrder.delete(item.id);
attachOrder.set(item.uploaded.id, index);
}
files.value.push(item.uploaded);
uploader.removeItem(item);
});
function detachAttaches(id: string) {
const attach = attaches.value.find(a => a.id === id);
if (!attach) return;
attachOrder.delete(attach.id);
if (attach.type === 'driveFile') {
files.value = files.value.filter(f => f.id !== attach.id);
} else if (attach.type === 'uploaderItem') {
uploader.removeItem(attach.file);
}
} }
function updateFileSensitive(file, sensitive) { function handleUploaderItemAbort(id: string) {
if (props.mock) return;
const item = uploader.items.value.find(i => i.id === id);
if (!item) return;
if (posting.value && attaches.value.length > 1) {
// 稿
detachAttaches(id);
}
}
function updateFileSensitive(file: Misskey.entities.DriveFile, sensitive: boolean) {
if (props.mock) { if (props.mock) {
emit('fileChangeSensitive', file.id, sensitive); emit('fileChangeSensitive', file.id, sensitive);
} }
files.value[files.value.findIndex(x => x.id === file.id)].isSensitive = sensitive; files.value[files.value.findIndex(x => x.id === file.id)].isSensitive = sensitive;
} }
function updateFileName(file, name) { function updateFileName(file: Misskey.entities.DriveFile, name: string) {
files.value[files.value.findIndex(x => x.id === file.id)].name = name; files.value[files.value.findIndex(x => x.id === file.id)].name = name;
} }
const attachOrder = new Map<string, number>();
const attaches = computed<Attach[]>({
get: () => {
const _attaches = [
...files.value.map(f => ({ id: f.id, type: 'driveFile' as const, file: f })),
...uploader.items.value.filter(i => i.uploaded == null).map(i => ({ id: i.id, type: 'uploaderItem' as const, file: i })),
];
_attaches.forEach((a, i) => {
if (!attachOrder.has(a.id)) {
attachOrder.set(a.id, i);
}
});
return _attaches.sort((a, b) => {
const aOrder = attachOrder.get(a.id) ?? 0;
const bOrder = attachOrder.get(b.id) ?? 0;
return aOrder - bOrder;
});
},
set: (newAttaches: Attach[]) => {
attachOrder.clear();
newAttaches.forEach((a, i) => {
attachOrder.set(a.id, i);
});
files.value = newAttaches.filter(a => a.type === 'driveFile').map(a => a.file);
uploader.items.value = newAttaches.filter(a => a.type === 'uploaderItem').map(a => a.file);
},
});
function handleShowUploaderMenu(item: UploaderItem, ev: MouseEvent | KeyboardEvent) {
if (props.mock) return;
os.popupMenu(uploader.getMenu(item), ev.currentTarget ?? ev.target);
}
function setVisibility() { function setVisibility() {
if (targetChannel.value) { if (targetChannel.value) {
visibility.value = 'public'; visibility.value = 'public';
@ -943,6 +1033,9 @@ async function post(ev?: MouseEvent) {
} }
} }
// 稿
posting.value = true;
if (uploader.items.value.some(x => x.uploaded == null)) { if (uploader.items.value.some(x => x.uploaded == null)) {
await uploadFiles(); await uploadFiles();
@ -954,7 +1047,12 @@ async function post(ev?: MouseEvent) {
let postData = { let postData = {
text: text.value === '' ? null : text.value, text: text.value === '' ? null : text.value,
fileIds: files.value.length > 0 ? files.value.map(f => f.id) : undefined, fileIds: files.value.length > 0 ? files.value.map(f => f.id).sort((a, b) => {
// itemUploadedfiles
const aOrder = attachOrder.get(a) ?? 0;
const bOrder = attachOrder.get(b) ?? 0;
return aOrder - bOrder;
}) : undefined,
replyId: replyTargetNote.value ? replyTargetNote.value.id : undefined, replyId: replyTargetNote.value ? replyTargetNote.value.id : undefined,
renoteId: renoteTargetNote.value ? renoteTargetNote.value.id : quoteId.value ? quoteId.value : undefined, renoteId: renoteTargetNote.value ? renoteTargetNote.value.id : quoteId.value ? quoteId.value : undefined,
channelId: targetChannel.value ? targetChannel.value.id : undefined, channelId: targetChannel.value ? targetChannel.value.id : undefined,
@ -993,6 +1091,13 @@ async function post(ev?: MouseEvent) {
} }
} }
//
// canPost
if (!computePostDataCondition('req')) {
posting.value = false;
return;
}
let token: string | undefined = undefined; let token: string | undefined = undefined;
if (postAccount.value) { if (postAccount.value) {
@ -1009,7 +1114,6 @@ async function post(ev?: MouseEvent) {
} }
} }
posting.value = true;
misskeyApi('notes/create', postData, token).then((res) => { misskeyApi('notes/create', postData, token).then((res) => {
if (props.freezeAfterPosted) { if (props.freezeAfterPosted) {
posted.value = true; posted.value = true;
@ -1021,6 +1125,7 @@ async function post(ev?: MouseEvent) {
nextTick(() => { nextTick(() => {
deleteDraft(); deleteDraft();
attachOrder.clear();
emit('posted'); emit('posted');
if (postData.text && postData.text !== '') { if (postData.text && postData.text !== '') {
const hashtags_ = mfm.parse(postData.text).map(x => x.type === 'hashtag' && x.props.hashtag).filter(x => x) as string[]; const hashtags_ = mfm.parse(postData.text).map(x => x.type === 'hashtag' && x.props.hashtag).filter(x => x) as string[];

View File

@ -5,18 +5,46 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div v-show="props.modelValue.length != 0" :class="$style.root"> <div v-show="props.modelValue.length != 0" :class="$style.root">
<Sortable :modelValue="props.modelValue" :class="$style.files" itemKey="id" :animation="150" :delay="100" :delayOnTouchOnly="true" @update:modelValue="v => emit('update:modelValue', v)"> <Sortable
:modelValue="props.modelValue"
:class="$style.files"
itemKey="id"
:animation="150"
:delay="100"
:delayOnTouchOnly="true"
:disabled="props.draggable === false"
@update:modelValue="v => emit('update:modelValue', v)"
>
<template #item="{ element }"> <template #item="{ element }">
<div <div
:class="$style.file" :class="[$style.file, { [$style.dragEnabled]: props.draggable !== false }]"
role="button" role="button"
tabindex="0" tabindex="0"
@click="showFileMenu(element, $event)" @click="handleClick(element, $event)"
@keydown.space.enter="showFileMenu(element, $event)" @keydown.space.enter="showFileMenu(element, $event)"
@contextmenu.prevent="showFileMenu(element, $event)" @contextmenu.prevent="showFileMenu(element, $event)"
> >
<MkDriveFileThumbnail :data-id="element.id" :class="$style.thumbnail" :file="element" fit="cover"/> <MkDriveFileThumbnail v-if="element.type === 'driveFile'" :data-id="element.id" :class="$style.thumbnail" :file="element.file" fit="cover"/>
<div v-if="element.isSensitive" :class="$style.sensitive"> <template v-else-if="element.type === 'uploaderItem'">
<img v-if="element.file.thumbnail" :src="element.file.thumbnail" :class="[$style.thumbnail, $style.uploaderThumbnail]" />
<div v-else v-panel :class="[$style.thumbnail, $style.uploaderThumbnailIcon]">
<i :class="[$style.icon, getFileTypeIcon(getFileType(element.file.file.type))]"></i>
</div>
<div :class="[$style.uploadProgressWrapper, { uploading: element.file.uploading }]">
<svg :class="$style.uploadProgressSvg" viewBox="0 0 64 64">
<circle
:class="$style.uploadProgressFg"
cx="32" cy="32" r="16"
:stroke-dasharray="progressDashArray(element.file)"
/>
</svg>
<div :class="$style.uploadAbortButton">
<!-- 実際のボタン機能はhandleClick -->
<i class="ti ti-x"></i>
</div>
</div>
</template>
<div v-if="(element.type === 'driveFile' && element.file.isSensitive) || (element.type === 'uploaderItem' && element.file.isSensitive)" :class="$style.sensitive">
<i class="ti ti-eye-exclamation" style="margin: auto;"></i> <i class="ti ti-eye-exclamation" style="margin: auto;"></i>
</div> </div>
</div> </div>
@ -32,11 +60,26 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</template> </template>
<script lang="ts">
import type { UploaderItem } from '@/composables/use-uploader.js';
export type Attach = {
id: string;
type: 'driveFile';
file: Misskey.entities.DriveFile;
} | {
id: string;
type: 'uploaderItem';
file: UploaderItem;
};
</script>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, inject } from 'vue'; import { defineAsyncComponent, inject } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import type { MenuItem } from '@/types/menu'; import type { MenuItem } from '@/types/menu';
import { copyToClipboard } from '@/utility/copy-to-clipboard'; import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { getFileType, getFileTypeIcon } from '@/utility/file-type.js';
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js'; import { misskeyApi } from '@/utility/misskey-api.js';
@ -48,29 +91,30 @@ import { globalEvents } from '@/events.js';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
const props = defineProps<{ const props = defineProps<{
modelValue: Misskey.entities.DriveFile[]; draggable?: boolean;
detachMediaFn?: (id: string) => void; modelValue: Attach[];
}>(); }>();
const mock = inject(DI.mock, false); const mock = inject(DI.mock, false);
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'update:modelValue', value: Misskey.entities.DriveFile[]): void; (ev: 'update:modelValue', value: Attach[]): void;
(ev: 'detach', id: string): void; (ev: 'detach', id: string): void;
(ev: 'changeSensitive', file: Misskey.entities.DriveFile, isSensitive: boolean): void; (ev: 'uploaderItemAborted', id: string): void;
(ev: 'changeName', file: Misskey.entities.DriveFile, newName: string): void; (ev: 'changeDriveFileSensitivity', file: Misskey.entities.DriveFile, isSensitive: boolean): void;
(ev: 'changeDriveFileName', file: Misskey.entities.DriveFile, newName: string): void;
(ev: 'showUploaderMenu', uploaderItem: UploaderItem, event: MouseEvent | KeyboardEvent): void;
}>(); }>();
let menuShowing = false; function progressDashArray(item: UploaderItem): string {
const progress = item.progress ? item.progress.value / item.progress.max : 0;
return `${progress * 100} ${100 - progress * 100}`;
}
function detachMedia(id: string) { function detachMedia(id: string) {
if (mock) return; if (mock) return;
if (props.detachMediaFn) { emit('detach', id);
props.detachMediaFn(id);
} else {
emit('detach', id);
}
} }
async function detachAndDeleteMedia(file: Misskey.entities.DriveFile) { async function detachAndDeleteMedia(file: Misskey.entities.DriveFile) {
@ -91,9 +135,9 @@ async function detachAndDeleteMedia(file: Misskey.entities.DriveFile) {
globalEvents.emit('driveFilesDeleted', [file]); globalEvents.emit('driveFilesDeleted', [file]);
} }
function toggleSensitive(file) { function toggleDriveFileSensitivity(file: Misskey.entities.DriveFile) {
if (mock) { if (mock) {
emit('changeSensitive', file, !file.isSensitive); emit('changeDriveFileSensitivity', file, !file.isSensitive);
return; return;
} }
@ -101,11 +145,11 @@ function toggleSensitive(file) {
fileId: file.id, fileId: file.id,
isSensitive: !file.isSensitive, isSensitive: !file.isSensitive,
}).then(() => { }).then(() => {
emit('changeSensitive', file, !file.isSensitive); emit('changeDriveFileSensitivity', file, !file.isSensitive);
}); });
} }
async function rename(file) { async function renameDriveFile(file: Misskey.entities.DriveFile) {
if (mock) return; if (mock) return;
const { canceled, result } = await os.inputText({ const { canceled, result } = await os.inputText({
@ -118,12 +162,12 @@ async function rename(file) {
fileId: file.id, fileId: file.id,
name: result, name: result,
}).then(() => { }).then(() => {
emit('changeName', file, result); emit('changeDriveFileName', file, result);
file.name = result; file.name = result;
}); });
} }
async function describe(file: Misskey.entities.DriveFile) { async function describeDriveFile(file: Misskey.entities.DriveFile) {
if (mock) return; if (mock) return;
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), { const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), {
@ -143,66 +187,82 @@ async function describe(file: Misskey.entities.DriveFile) {
}); });
} }
function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | KeyboardEvent): void { function handleClick(attach: Attach, ev: MouseEvent | KeyboardEvent): void {
if (menuShowing) return; if (ev instanceof MouseEvent && ev.button !== 0) return; //
const isImage = file.type.startsWith('image/'); if (attach.type === 'driveFile' || (attach.type === 'uploaderItem' && !attach.file.uploading)) {
showFileMenu(attach, ev);
} else {
if (attach.file.abort) {
attach.file.abort();
}
attach.file.aborted = true;
attach.file.uploadFailed = true;
emit('uploaderItemAborted', attach.file.id);
}
}
const menuItems: MenuItem[] = []; function showFileMenu(attach: Attach, ev: MouseEvent | KeyboardEvent): void {
if (attach.type === 'driveFile') {
const file = attach.file;
const isImage = file.type.startsWith('image/');
menuItems.push({ const menuItems: MenuItem[] = [];
text: i18n.ts.renameFile,
icon: 'ti ti-forms',
action: () => { rename(file); },
}, {
text: file.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
icon: file.isSensitive ? 'ti ti-eye-exclamation' : 'ti ti-eye',
action: () => { toggleSensitive(file); },
}, {
text: i18n.ts.describeFile,
icon: 'ti ti-text-caption',
action: () => { describe(file); },
});
if (isImage) {
menuItems.push({ menuItems.push({
text: i18n.ts.preview, text: i18n.ts.renameFile,
icon: 'ti ti-photo-search', icon: 'ti ti-forms',
action: async () => { action: () => { renameDriveFile(file); },
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkImgPreviewDialog.vue').then(x => x.default), { }, {
file: file, text: file.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
}, { icon: file.isSensitive ? 'ti ti-eye-exclamation' : 'ti ti-eye',
closed: () => dispose(), action: () => { toggleDriveFileSensitivity(file); },
}); }, {
}, text: i18n.ts.describeFile,
icon: 'ti ti-text-caption',
action: () => { describeDriveFile(file); },
}); });
}
menuItems.push({ if (isImage) {
type: 'divider', menuItems.push({
}, { text: i18n.ts.preview,
text: i18n.ts.attachCancel, icon: 'ti ti-photo-search',
icon: 'ti ti-circle-x', action: async () => {
action: () => { detachMedia(file.id); }, const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkImgPreviewDialog.vue').then(x => x.default), {
}, { file: file,
text: i18n.ts.deleteFile, }, {
icon: 'ti ti-trash', closed: () => dispose(),
danger: true, });
action: () => { detachAndDeleteMedia(file); }, },
}); });
}
if (prefer.s.devMode) { menuItems.push({
menuItems.push({ type: 'divider' }, { type: 'divider',
icon: 'ti ti-hash', }, {
text: i18n.ts.copyFileId, text: i18n.ts.attachCancel,
action: () => { icon: 'ti ti-circle-x',
copyToClipboard(file.id); action: () => { detachMedia(file.id); },
}, }, {
text: i18n.ts.deleteFile,
icon: 'ti ti-trash',
danger: true,
action: () => { detachAndDeleteMedia(file); },
}); });
}
os.popupMenu(menuItems, ev.currentTarget ?? ev.target).then(() => menuShowing = false); if (prefer.s.devMode) {
menuShowing = true; menuItems.push({ type: 'divider' }, {
icon: 'ti ti-hash',
text: i18n.ts.copyFileId,
action: () => {
copyToClipboard(file.id);
},
});
}
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
} else if (attach.type === 'uploaderItem') {
emit('showUploaderMenu', attach.file, ev);
}
} }
</script> </script>
@ -222,13 +282,16 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar
width: 64px; width: 64px;
height: 64px; height: 64px;
margin-right: 4px; margin-right: 4px;
border-radius: 4px; border-radius: 8px;
overflow: hidden; overflow: hidden;
cursor: move;
&:focus-visible { &:focus-visible {
outline-offset: 4px; outline-offset: 4px;
} }
&.dragEnabled {
cursor: move;
}
} }
.thumbnail { .thumbnail {
@ -238,6 +301,24 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar
color: var(--MI_THEME-fg); color: var(--MI_THEME-fg);
} }
.uploaderThumbnail {
object-fit: cover;
object-position: center;
}
.uploaderThumbnailIcon {
display: flex;
align-items: center;
justify-content: center;
}
.icon {
pointer-events: none;
margin: auto;
font-size: 32px;
color: #777;
}
.sensitive { .sensitive {
display: flex; display: flex;
position: absolute; position: absolute;
@ -263,4 +344,89 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar
color: var(--MI_THEME-error); color: var(--MI_THEME-error);
} }
} }
.uploadProgressWrapper {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.75);
mask-image: linear-gradient(#000, #000), url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAwIDEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNTAiIC8+PC9zdmc+");
mask-position: center;
mask-repeat: no-repeat;
mask-size: 100% 100%, 90px 90px;
mask-composite: exclude;
transition: mask-size 0.2s ease;
}
}
.uploadProgressSvg {
position: absolute;
top: 50%;
left: 50%;
width: 32px;
height: 32px;
transform: translate(-50%, -50%);
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease;
}
.uploadProgressFg {
fill: none;
stroke-width: 32;
stroke: rgba(0, 0, 0, 0.75);
stroke-dashoffset: 25;
transition: stroke-dasharray 0.2s ease;
}
.uploadAbortButton {
position: absolute;
top: 50%;
left: 50%;
width: 32px;
height: 32px;
border-radius: 50%;
transform: translate(-50%, -50%);
font-size: 16px;
line-height: 32px;
text-align: center;
background-color: rgba(0, 0, 0, 0.75);
color: #fff;
opacity: 0;
transition: opacity 0.2s ease;
cursor: pointer;
}
.uploadProgressWrapper:global(.uploading) {
backdrop-filter: brightness(1.5);
&::before {
mask-size: 100% 100%, 36px 36px;
}
.uploadProgressSvg {
opacity: 1;
}
}
.file:hover .uploadProgressWrapper:global(.uploading) {
.uploadProgressSvg {
opacity: 0;
}
.uploadAbortButton {
opacity: 1;
}
}
</style> </style>

View File

@ -948,6 +948,7 @@ watch([
chatShowSenderName, chatShowSenderName,
useStickyIcons, useStickyIcons,
enableHighQualityImagePlaceholders, enableHighQualityImagePlaceholders,
disableShowingAnimatedImages,
keepScreenOn, keepScreenOn,
contextMenu, contextMenu,
fontSize, fontSize,
@ -958,6 +959,8 @@ watch([
enablePullToRefresh, enablePullToRefresh,
reduceAnimation, reduceAnimation,
showAvailableReactionsFirstInNote, showAvailableReactionsFirstInNote,
animatedMfm,
advancedMfm,
], () => { ], () => {
suggestReload(); suggestReload();
}); });

View File

@ -511,7 +511,7 @@ export const PREF_DEF = definePreferences({
default: false, default: false,
}, },
'experimental.enableFolderPageView': { 'experimental.enableFolderPageView': {
default: true, default: false,
}, },
'experimental.enableHapticFeedback': { 'experimental.enableHapticFeedback': {
default: false, default: false,

View File

@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export type DetectableFileType =
| 'image'
| 'video'
| 'midi'
| 'audio'
| 'csv'
| 'pdf'
| 'textfile'
| 'archive'
| 'unknown';
export function getFileType(type: string): DetectableFileType {
if (type.startsWith('image/')) return 'image';
if (type.startsWith('video/')) return 'video';
if (type === 'audio/midi') return 'midi';
if (type.startsWith('audio/')) return 'audio';
if (type.endsWith('/csv')) return 'csv';
if (type.endsWith('/pdf')) return 'pdf';
if (type.startsWith('text/')) return 'textfile';
if ([
'application/zip',
'application/x-cpio',
'application/x-bzip',
'application/x-bzip2',
'application/java-archive',
'application/x-rar-compressed',
'application/x-tar',
'application/gzip',
'application/x-7z-compressed',
].some(archiveType => archiveType === type)) return 'archive';
return 'unknown';
}
export function getFileTypeIcon(type: DetectableFileType) {
switch (type) {
case 'image': return 'ti ti-photo';
case 'video': return 'ti ti-video';
case 'audio':
case 'midi':
return 'ti ti-file-music';
case 'csv':
case 'pdf':
case 'textfile':
return 'ti ti-file-text';
case 'archive': return 'ti ti-file-zip';
default: return 'ti ti-file';
}
}

View File

@ -11,16 +11,16 @@
"lint": "pnpm typecheck && pnpm eslint" "lint": "pnpm typecheck && pnpm eslint"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "22.18.1", "@types/node": "22.18.6",
"@types/wawoff2": "1.0.2", "@types/wawoff2": "1.0.2",
"@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.42.0" "@typescript-eslint/parser": "8.44.1"
}, },
"dependencies": { "dependencies": {
"@tabler/icons-webfont": "3.34.1", "@tabler/icons-webfont": "3.35.0",
"harfbuzzjs": "0.4.11", "harfbuzzjs": "0.4.12",
"tiny-glob": "0.2.9", "tiny-glob": "0.2.9",
"tsx": "4.20.5", "tsx": "4.20.6",
"typescript": "5.9.2", "typescript": "5.9.2",
"wawoff2": "2.0.1" "wawoff2": "2.0.1"
}, },

View File

@ -22,15 +22,15 @@
"lint": "pnpm typecheck && pnpm eslint" "lint": "pnpm typecheck && pnpm eslint"
}, },
"devDependencies": { "devDependencies": {
"@types/matter-js": "0.20.0", "@types/matter-js": "0.20.2",
"@types/seedrandom": "3.0.8", "@types/seedrandom": "3.0.8",
"@types/node": "22.18.1", "@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.42.0", "@typescript-eslint/parser": "8.44.1",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"execa": "9.6.0", "execa": "9.6.0",
"typescript": "5.9.2", "typescript": "5.9.2",
"esbuild": "0.25.9", "esbuild": "0.25.10",
"glob": "11.0.3" "glob": "11.0.3"
}, },
"files": [ "files": [

View File

@ -8,13 +8,13 @@
}, },
"devDependencies": { "devDependencies": {
"@readme/openapi-parser": "5.0.1", "@readme/openapi-parser": "5.0.1",
"@types/node": "22.18.1", "@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.42.0", "@typescript-eslint/parser": "8.44.1",
"openapi-types": "12.1.3", "openapi-types": "12.1.3",
"openapi-typescript": "7.9.1", "openapi-typescript": "7.9.1",
"ts-case-convert": "2.1.0", "ts-case-convert": "2.1.0",
"tsx": "4.20.5", "tsx": "4.20.6",
"typescript": "5.9.2" "typescript": "5.9.2"
}, },
"files": [ "files": [

View File

@ -1,7 +1,7 @@
{ {
"type": "module", "type": "module",
"name": "misskey-js", "name": "misskey-js",
"version": "2025.9.1-alpha.2", "version": "2025.10.0-beta.0",
"description": "Misskey SDK for JavaScript", "description": "Misskey SDK for JavaScript",
"license": "MIT", "license": "MIT",
"main": "./built/index.js", "main": "./built/index.js",
@ -35,12 +35,12 @@
"directory": "packages/misskey-js" "directory": "packages/misskey-js"
}, },
"devDependencies": { "devDependencies": {
"@microsoft/api-extractor": "7.52.11", "@microsoft/api-extractor": "7.52.13",
"@types/node": "22.18.1", "@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.42.0", "@typescript-eslint/parser": "8.44.1",
"@vitest/coverage-v8": "3.2.4", "@vitest/coverage-v8": "3.2.4",
"esbuild": "0.25.9", "esbuild": "0.25.10",
"execa": "9.6.0", "execa": "9.6.0",
"glob": "11.0.3", "glob": "11.0.3",
"ncp": "2.0.0", "ncp": "2.0.0",

View File

@ -22,13 +22,13 @@
"lint": "pnpm typecheck && pnpm eslint" "lint": "pnpm typecheck && pnpm eslint"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "22.18.1", "@types/node": "22.18.6",
"@typescript-eslint/eslint-plugin": "8.42.0", "@typescript-eslint/eslint-plugin": "8.44.1",
"@typescript-eslint/parser": "8.42.0", "@typescript-eslint/parser": "8.44.1",
"execa": "9.6.0", "execa": "9.6.0",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"typescript": "5.9.2", "typescript": "5.9.2",
"esbuild": "0.25.9", "esbuild": "0.25.10",
"glob": "11.0.3" "glob": "11.0.3"
}, },
"files": [ "files": [

View File

@ -9,12 +9,12 @@
"lint": "pnpm typecheck && pnpm eslint" "lint": "pnpm typecheck && pnpm eslint"
}, },
"dependencies": { "dependencies": {
"esbuild": "0.25.9", "esbuild": "0.25.10",
"idb-keyval": "6.2.2", "idb-keyval": "6.2.2",
"misskey-js": "workspace:*" "misskey-js": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/parser": "8.42.0", "@typescript-eslint/parser": "8.44.1",
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",
"nodemon": "3.1.10", "nodemon": "3.1.10",

View File

@ -1,8 +1,8 @@
diff --git a/driver/postgres/PostgresDriver.js b/driver/postgres/PostgresDriver.js diff --git a/driver/postgres/PostgresDriver.js b/driver/postgres/PostgresDriver.js
index 278f29c1f3deec4939bb4ed90e6edae167f704e0..9a84c3098dda915d6c33e24d925a8fa09af9095e 100644 index e13b903c73b71113bb529552e59fb4ce0ca8af0c..50de6a60120ece7ebf49009eac588a5313343f39 100644
--- a/driver/postgres/PostgresDriver.js --- a/driver/postgres/PostgresDriver.js
+++ b/driver/postgres/PostgresDriver.js +++ b/driver/postgres/PostgresDriver.js
@@ -785,10 +785,10 @@ class PostgresDriver { @@ -819,10 +819,10 @@ class PostgresDriver {
const tableColumnDefault = typeof tableColumn.default === "string" const tableColumnDefault = typeof tableColumn.default === "string"
? JSON.parse(tableColumn.default.substring(1, tableColumn.default.length - 1)) ? JSON.parse(tableColumn.default.substring(1, tableColumn.default.length - 1))
: tableColumn.default; : tableColumn.default;

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@
dependencyDashboardAutoclose: true, dependencyDashboardAutoclose: true,
osvVulnerabilityAlerts: true, osvVulnerabilityAlerts: true,
dependencyDashboardOSVVulnerabilitySummary: 'unresolved', dependencyDashboardOSVVulnerabilitySummary: 'unresolved',
minimumReleaseAge: '7 days',
ignoreDeps: [ ignoreDeps: [
// https://github.com/misskey-dev/misskey/pull/15489#issuecomment-2660717458 // https://github.com/misskey-dev/misskey/pull/15489#issuecomment-2660717458
'@typescript/lib-webworker', '@typescript/lib-webworker',