diff options
| author | Hazelnoot <acomputerdog@gmail.com> | 2025-04-29 15:37:43 -0400 |
|---|---|---|
| committer | Hazelnoot <acomputerdog@gmail.com> | 2025-04-29 15:54:11 -0400 |
| commit | 9c301fa5aac7e339a2b41feab8d0d247a60f50aa (patch) | |
| tree | 26e1423620a2811a5e9372bcee6858851d9fad3e /packages | |
| parent | align `docker_example.yml` with `example.yml` (diff) | |
| parent | chore: follow up on fixing Chromatic CI diff strategy (#15912) (diff) | |
| download | sharkey-9c301fa5aac7e339a2b41feab8d0d247a60f50aa.tar.gz sharkey-9c301fa5aac7e339a2b41feab8d0d247a60f50aa.tar.bz2 sharkey-9c301fa5aac7e339a2b41feab8d0d247a60f50aa.zip | |
Merge branch 'misskey-develop' into merge/2025-03-24
# Conflicts:
# .github/workflows/api-misskey-js.yml
# .github/workflows/changelog-check.yml
# .github/workflows/check-misskey-js-autogen.yml
# .github/workflows/get-api-diff.yml
# .github/workflows/lint.yml
# .github/workflows/locale.yml
# .github/workflows/on-release-created.yml
# .github/workflows/storybook.yml
# .github/workflows/test-backend.yml
# .github/workflows/test-federation.yml
# .github/workflows/test-frontend.yml
# .github/workflows/test-misskey-js.yml
# .github/workflows/test-production.yml
# .github/workflows/validate-api-json.yml
# package.json
# packages/backend/package.json
# packages/backend/src/server/api/ApiCallService.ts
# packages/backend/src/server/api/endpoints/drive/files/create.ts
# packages/frontend-shared/js/url.ts
# packages/frontend/package.json
# packages/frontend/src/components/MkFileCaptionEditWindow.vue
# packages/frontend/src/components/MkInfo.vue
# packages/frontend/src/components/MkLink.vue
# packages/frontend/src/components/MkNote.vue
# packages/frontend/src/components/MkNotes.vue
# packages/frontend/src/components/MkPageWindow.vue
# packages/frontend/src/components/MkReactionsViewer.vue
# packages/frontend/src/components/MkTimeline.vue
# packages/frontend/src/components/MkUrlPreview.vue
# packages/frontend/src/components/MkUserPopup.vue
# packages/frontend/src/components/global/MkPageHeader.vue
# packages/frontend/src/components/global/MkUrl.vue
# packages/frontend/src/components/global/PageWithHeader.vue
# packages/frontend/src/pages/about-misskey.vue
# packages/frontend/src/pages/announcements.vue
# packages/frontend/src/pages/antenna-timeline.vue
# packages/frontend/src/pages/channel.vue
# packages/frontend/src/pages/instance-info.vue
# packages/frontend/src/pages/note.vue
# packages/frontend/src/pages/page.vue
# packages/frontend/src/pages/role.vue
# packages/frontend/src/pages/tag.vue
# packages/frontend/src/pages/timeline.vue
# packages/frontend/src/pages/user-list-timeline.vue
# packages/frontend/src/pages/user/followers.vue
# packages/frontend/src/pages/user/following.vue
# packages/frontend/src/pages/user/home.vue
# packages/frontend/src/pages/user/index.vue
# packages/frontend/src/ui/deck.vue
# packages/misskey-js/generator/package.json
# pnpm-lock.yaml
# scripts/changelog-checker/package-lock.json
# scripts/changelog-checker/package.json
Diffstat (limited to 'packages')
234 files changed, 1624 insertions, 1435 deletions
diff --git a/packages/backend/package.json b/packages/backend/package.json index cd8ab7121f..0d62c8a536 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -37,17 +37,17 @@ }, "optionalDependencies": { "@swc/core-android-arm64": "1.3.11", - "@swc/core-darwin-arm64": "1.11.18", - "@swc/core-darwin-x64": "1.11.18", + "@swc/core-darwin-arm64": "1.11.22", + "@swc/core-darwin-x64": "1.11.22", "@swc/core-freebsd-x64": "1.3.11", - "@swc/core-linux-arm-gnueabihf": "1.11.18", - "@swc/core-linux-arm64-gnu": "1.11.18", - "@swc/core-linux-arm64-musl": "1.11.18", - "@swc/core-linux-x64-gnu": "1.11.18", - "@swc/core-linux-x64-musl": "1.11.18", - "@swc/core-win32-arm64-msvc": "1.11.18", - "@swc/core-win32-ia32-msvc": "1.11.18", - "@swc/core-win32-x64-msvc": "1.11.18", + "@swc/core-linux-arm-gnueabihf": "1.11.22", + "@swc/core-linux-arm64-gnu": "1.11.22", + "@swc/core-linux-arm64-musl": "1.11.22", + "@swc/core-linux-x64-gnu": "1.11.22", + "@swc/core-linux-x64-musl": "1.11.22", + "@swc/core-win32-arm64-msvc": "1.11.22", + "@swc/core-win32-ia32-msvc": "1.11.22", + "@swc/core-win32-x64-msvc": "1.11.22", "bufferutil": "4.0.9", "slacc-android-arm-eabi": "0.0.10", "slacc-android-arm64": "0.0.10", @@ -65,8 +65,8 @@ "utf-8-validate": "6.0.5" }, "dependencies": { - "@aws-sdk/client-s3": "3.782.0", - "@aws-sdk/lib-storage": "3.782.0", + "@aws-sdk/client-s3": "3.797.0", + "@aws-sdk/lib-storage": "3.797.0", "@discordapp/twemoji": "15.1.0", "@fastify/accepts": "5.0.2", "@fastify/cookie": "11.0.2", @@ -78,17 +78,17 @@ "@fastify/view": "10.0.2", "@misskey-dev/sharp-read-bmp": "1.3.0", "@misskey-dev/summaly": "5.2.1", - "@nestjs/common": "11.0.16", - "@nestjs/core": "11.0.15", - "@nestjs/testing": "11.0.15", + "@nestjs/common": "11.1.0", + "@nestjs/core": "11.1.0", + "@nestjs/testing": "11.1.0", "@peertube/http-signature": "1.7.0", "@sentry/node": "8.55.0", "@sentry/profiling-node": "8.55.0", "@simplewebauthn/server": "12.0.0", "@sinonjs/fake-timers": "11.3.1", "@smithy/node-http-handler": "2.5.0", - "@swc/cli": "0.6.0", - "@swc/core": "1.11.18", + "@swc/cli": "0.7.3", + "@swc/core": "1.11.22", "@transfem-org/sfm-js": "0.24.6", "@twemoji/parser": "15.1.1", "@types/redis-info": "3.0.3", @@ -101,7 +101,7 @@ "bcryptjs": "2.4.3", "blurhash": "2.0.5", "body-parser": "1.20.3", - "bullmq": "5.48.1", + "bullmq": "5.51.1", "cacheable-lookup": "7.0.0", "canvas": "^3.1.0", "cbor": "9.0.2", @@ -127,18 +127,18 @@ "hpagent": "1.2.0", "htmlescape": "1.1.1", "http-link-header": "1.1.3", - "ioredis": "5.6.0", + "ioredis": "5.6.1", "ip-cidr": "4.0.2", "ipaddr.js": "2.2.0", "is-svg": "5.1.0", "js-yaml": "4.1.0", - "jsdom": "26.0.0", + "jsdom": "26.1.0", "json5": "2.2.3", "jsonld": "8.3.3", "jsrsasign": "11.1.0", "juice": "11.0.1", "megalodon": "workspace:*", - "meilisearch": "0.49.0", + "meilisearch": "0.50.0", "microformats-parser": "2.0.2", "mime-types": "2.1.35", "misskey-js": "workspace:*", @@ -148,14 +148,14 @@ "nanoid": "5.1.5", "nested-property": "4.0.0", "node-fetch": "3.3.2", - "nodemailer": "6.10.0", + "nodemailer": "6.10.1", "oauth": "0.10.2", "oauth2orize": "1.12.0", "oauth2orize-pkce": "0.1.2", "os-utils": "0.0.14", "otpauth": "9.4.0", - "parse5": "7.2.1", - "pg": "8.14.1", + "parse5": "7.3.0", + "pg": "8.15.6", "pkce-challenge": "4.1.0", "probe-image-size": "7.2.3", "promise-limit": "2.7.0", @@ -172,7 +172,7 @@ "rename": "1.0.4", "rss-parser": "3.13.0", "rxjs": "7.8.2", - "sanitize-html": "2.15.0", + "sanitize-html": "2.16.0", "secure-json-parse": "3.0.2", "sharp": "0.34.1", "slacc": "0.0.10", @@ -194,10 +194,10 @@ }, "devDependencies": { "@jest/globals": "29.7.0", - "@nestjs/platform-express": "10.4.15", - "@sentry/vue": "9.12.0", + "@nestjs/platform-express": "10.4.17", + "@sentry/vue": "9.14.0", "@simplewebauthn/types": "12.0.0", - "@swc/jest": "0.2.37", + "@swc/jest": "0.2.38", "@types/accepts": "1.3.7", "@types/archiver": "6.0.3", "@types/bcryptjs": "2.4.6", @@ -214,12 +214,12 @@ "@types/jsrsasign": "10.5.15", "@types/mime-types": "2.1.4", "@types/ms": "0.7.34", - "@types/node": "22.14.0", + "@types/node": "22.15.2", "@types/nodemailer": "6.4.17", "@types/oauth": "0.9.6", "@types/oauth2orize": "1.11.5", "@types/oauth2orize-pkce": "0.1.2", - "@types/pg": "8.11.11", + "@types/pg": "8.11.14", "@types/proxy-addr": "^2.0.3", "@types/pug": "2.0.10", "@types/qrcode": "1.5.5", @@ -230,14 +230,15 @@ "@types/semver": "7.7.0", "@types/simple-oauth2": "5.0.7", "@types/sinonjs__fake-timers": "8.1.5", + "@types/supertest": "6.0.3", "@types/tinycolor2": "1.4.6", "@types/tmp": "0.2.6", "@types/uuid": "^9.0.4", "@types/vary": "1.1.3", "@types/web-push": "3.6.4", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.29.1", - "@typescript-eslint/parser": "8.29.1", + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", "aws-sdk-client-mock": "4.1.0", "cross-env": "7.0.3", "eslint-plugin-import": "2.31.0", @@ -245,8 +246,9 @@ "fkill": "9.0.0", "jest": "29.7.0", "jest-mock": "29.7.0", - "nodemon": "3.1.9", + "nodemon": "3.1.10", "pid-port": "1.0.2", - "simple-oauth2": "5.1.0" + "simple-oauth2": "5.1.0", + "supertest": "7.1.0" } } diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 4be1b0e41b..bb76b680a4 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -515,9 +515,16 @@ export class DriveService { const policies = await this.roleService.getUserPolicies(user.id); const driveCapacity = 1024 * 1024 * policies.driveCapacityMb; + const maxFileSize = 1024 * 1024 * policies.maxFileSizeMb; this.registerLogger.debug('drive capacity override applied'); this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`); + if (maxFileSize < info.size) { + if (isLocalUser) { + throw new IdentifiableError('f9e4e5f3-4df4-40b5-b400-f236945f7073', 'Max file size exceeded.'); + } + } + // If usage limit exceeded if (driveCapacity < usage + info.size) { if (isLocalUser) { diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index 84ca06ec1e..af2723e99d 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -8,10 +8,12 @@ import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import type { MiUser } from '@/models/User.js'; import type { MiNote } from '@/models/Note.js'; +import type { MiMeta } from '@/models/Meta.js'; import { Packed } from '@/misc/json-schema.js'; import type { NotesRepository } from '@/models/_.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { FanoutTimelineName, FanoutTimelineService } from '@/core/FanoutTimelineService.js'; +import { UtilityService } from '@/core/UtilityService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { isQuote, isRenote } from '@/misc/is-renote.js'; import { CacheService } from '@/core/CacheService.js'; @@ -30,6 +32,7 @@ type TimelineOptions = { alwaysIncludeMyNotes?: boolean; ignoreAuthorFromBlock?: boolean; ignoreAuthorFromMute?: boolean; + ignoreAuthorFromInstanceBlock?: boolean; excludeNoFiles?: boolean; excludeReplies?: boolean; excludeBots?: boolean; @@ -43,9 +46,13 @@ export class FanoutTimelineEndpointService { @Inject(DI.notesRepository) private notesRepository: NotesRepository, + @Inject(DI.meta) + private meta: MiMeta, + private noteEntityService: NoteEntityService, private cacheService: CacheService, private fanoutTimelineService: FanoutTimelineService, + private utilityService: UtilityService, ) { } @@ -125,6 +132,19 @@ export class FanoutTimelineEndpointService { }; } + { + const parentFilter = filter; + filter = (note) => { + if (!ps.ignoreAuthorFromInstanceBlock) { + if (this.utilityService.isBlockedHost(this.meta.blockedHosts, note.userHost)) return false; + } + if (note.userId !== note.renoteUserId && this.utilityService.isBlockedHost(this.meta.blockedHosts, note.renoteUserHost)) return false; + if (note.userId !== note.replyUserId && this.utilityService.isBlockedHost(this.meta.blockedHosts, note.replyUserHost)) return false; + + return parentFilter(note); + }; + } + const redisTimeline: MiNote[] = []; let readFromRedis = 0; let lastSuccessfulRate = 1; // rateをキャッシュする? diff --git a/packages/backend/src/core/ImageProcessingService.ts b/packages/backend/src/core/ImageProcessingService.ts index 6f978b34c8..6f60475442 100644 --- a/packages/backend/src/core/ImageProcessingService.ts +++ b/packages/backend/src/core/ImageProcessingService.ts @@ -34,6 +34,7 @@ export const webpDefault: sharp.WebpOptions = { smartSubsample: true, mixed: true, effort: 2, + loop: 0, }; export const avifDefault: sharp.AvifOptions = { diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index c611fe5f1e..fb4335b55d 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets, ObjectLiteral } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { MiUser } from '@/models/User.js'; -import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/_.js'; +import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository, MiMeta } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; import type { SelectQueryBuilder } from 'typeorm'; @@ -36,6 +36,9 @@ export class QueryService { @Inject(DI.renoteMutingsRepository) private renoteMutingsRepository: RenoteMutingsRepository, + @Inject(DI.meta) + private meta: MiMeta, + private idService: IdService, ) { } @@ -251,4 +254,37 @@ export class QueryService { q.setParameters(mutingQuery.getParameters()); } + + @bindThis + public generateBlockedHostQueryForNote(q: SelectQueryBuilder<any>, excludeAuthor?: boolean): void { + let nonBlockedHostQuery: (part: string) => string; + if (this.meta.blockedHosts.length === 0) { + nonBlockedHostQuery = () => '1=1'; + } else { + nonBlockedHostQuery = (match: string) => `${match} NOT ILIKE ALL(ARRAY[:...blocked])`; + q.setParameters({ blocked: this.meta.blockedHosts.flatMap(x => [x, `%.${x}`]) }); + } + + if (excludeAuthor) { + const instanceSuspension = (user: string) => new Brackets(qb => qb + .where(`note.${user}Id IS NULL`) // no corresponding user + .orWhere(`note.userId = note.${user}Id`) + .orWhere(`note.${user}Host IS NULL`) // local + .orWhere(nonBlockedHostQuery(`note.${user}Host`))); + + q + .andWhere(instanceSuspension('replyUser')) + .andWhere(instanceSuspension('renoteUser')); + } else { + const instanceSuspension = (user: string) => new Brackets(qb => qb + .where(`note.${user}Id IS NULL`) // no corresponding user + .orWhere(`note.${user}Host IS NULL`) // local + .orWhere(nonBlockedHostQuery(`note.${user}Host`))); + + q + .andWhere(instanceSuspension('user')) + .andWhere(instanceSuspension('replyUser')) + .andWhere(instanceSuspension('renoteUser')); + } + } } diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 8b98680f4c..229781c079 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -48,6 +48,7 @@ export type RolePolicies = { canUseTranslator: boolean; canHideAds: boolean; driveCapacityMb: number; + maxFileSizeMb: number; alwaysMarkNsfw: boolean; canUpdateBioMedia: boolean; pinLimit: number; @@ -86,6 +87,7 @@ export const DEFAULT_POLICIES: RolePolicies = { canUseTranslator: true, canHideAds: false, driveCapacityMb: 100, + maxFileSizeMb: 10, alwaysMarkNsfw: false, canUpdateBioMedia: true, pinLimit: 5, @@ -399,6 +401,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)), canHideAds: calc('canHideAds', vs => vs.some(v => v === true)), driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)), + maxFileSizeMb: calc('maxFileSizeMb', vs => Math.max(...vs)), alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)), canUpdateBioMedia: calc('canUpdateBioMedia', vs => vs.some(v => v === true)), pinLimit: calc('pinLimit', vs => Math.max(...vs)), diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts index e17677bcb5..37238dc4b0 100644 --- a/packages/backend/src/core/SearchService.ts +++ b/packages/backend/src/core/SearchService.ts @@ -300,6 +300,7 @@ export class SearchService { } this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); if (me) this.queryService.generateMutedUserQueryForNotes(query, me); if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); @@ -366,9 +367,14 @@ export class SearchService { this.cacheService.userBlockedCache.fetch(me.id), ]) : [new Set<string>(), new Set<string>()]; - const notes = (await this.notesRepository.findBy({ - id: In(res.hits.map(x => x.id)), - })).filter(note => { + + const query = this.notesRepository.createQueryBuilder('note'); + + query.where('note.id IN (:...noteIds)', { noteIds: res.hits.map(x => x.id) }); + + this.queryService.generateBlockedHostQueryForNote(query); + + const notes = (await query.getMany()).filter(note => { if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false; if (me && isUserRelated(note, userIdsWhoMeMuting)) return false; return true; diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index 9e95684f67..307c114c96 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -228,6 +228,10 @@ export const packedRolePoliciesSchema = { type: 'integer', optional: false, nullable: false, }, + maxFileSizeMb: { + type: 'integer', + optional: false, nullable: false, + }, alwaysMarkNsfw: { type: 'boolean', optional: false, nullable: false, diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index dce47e2290..5857b3059e 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -75,7 +75,7 @@ export class ServerService implements OnApplicationShutdown { } @bindThis - public async launch(): Promise<void> { + public async launch() { const fastify = Fastify({ trustProxy: true, logger: false, @@ -135,8 +135,8 @@ export class ServerService implements OnApplicationShutdown { reply.header('content-type', 'text/plain; charset=utf-8'); reply.header('link', `<${encodeURI(location)}>; rel="canonical"`); done(null, [ - "Refusing to relay remote ActivityPub object lookup.", - "", + 'Refusing to relay remote ActivityPub object lookup.', + '', `Please remove 'application/activity+json' and 'application/ld+json' from the Accept header or fetch using the authoritative URL at ${location}.`, ].join('\n')); }); @@ -304,6 +304,7 @@ export class ServerService implements OnApplicationShutdown { } await fastify.ready(); + return fastify; } @bindThis diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index b22a8c1837..1b8d33f9c9 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -6,8 +6,11 @@ import { randomUUID } from 'node:crypto'; import * as fs from 'node:fs'; import * as stream from 'node:stream/promises'; +import { Transform } from 'node:stream'; +import { type MultipartFile } from '@fastify/multipart'; import { Inject, Injectable } from '@nestjs/common'; import * as Sentry from '@sentry/node'; +import { AttachmentFile } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import type { MiLocalUser, MiUser } from '@/models/User.js'; @@ -16,7 +19,7 @@ import type Logger from '@/logger.js'; import type { MiMeta, UserIpsRepository } from '@/models/_.js'; import { createTemp } from '@/misc/create-temp.js'; import { bindThis } from '@/decorators.js'; -import { RoleService } from '@/core/RoleService.js'; +import { type RolePolicies, RoleService } from '@/core/RoleService.js'; import type { Config } from '@/config.js'; import { sendRateLimitHeaders } from '@/misc/rate-limit-utils.js'; import { SkRateLimiterService } from '@/server/SkRateLimiterService.js'; @@ -191,18 +194,6 @@ export class ApiCallService implements OnApplicationShutdown { return; } - const [path, cleanup] = await createTemp(); - await stream.pipeline(multipartData.file, fs.createWriteStream(path)); - - // ファイルサイズが制限を超えていた場合 - // なお truncated はストリームを読み切ってからでないと機能しないため、stream.pipeline より後にある必要がある - if (multipartData.file.truncated) { - cleanup(); - reply.code(413); - reply.send(); - return; - } - const fields = {} as Record<string, unknown>; for (const [k, v] of Object.entries(multipartData.fields)) { fields[k] = typeof v === 'object' && 'value' in v ? v.value : undefined; @@ -217,10 +208,7 @@ export class ApiCallService implements OnApplicationShutdown { return; } this.authenticateService.authenticate(token).then(([user, app]) => { - this.call(endpoint, user, app, fields, { - name: multipartData.filename, - path: path, - }, request, reply).then((res) => { + this.call(endpoint, user, app, fields, multipartData, request, reply).then((res) => { this.send(reply, res); }).catch((err: ApiError) => { this.#sendApiError(reply, err); @@ -290,10 +278,7 @@ export class ApiCallService implements OnApplicationShutdown { user: MiLocalUser | null | undefined, token: MiAccessToken | null | undefined, data: any, - file: { - name: string; - path: string; - } | null, + multipartFile: MultipartFile | null, request: FastifyRequest<{ Body: Record<string, unknown> | undefined, Querystring: Record<string, unknown> }>, reply: FastifyReply, ) { @@ -369,6 +354,37 @@ export class ApiCallService implements OnApplicationShutdown { } } + // Cast non JSON input + if ((ep.meta.requireFile || request.method === 'GET') && ep.params.properties) { + for (const k of Object.keys(ep.params.properties)) { + const param = ep.params.properties![k]; + if (['boolean', 'number', 'integer'].includes(param.type ?? '') && typeof data[k] === 'string') { + try { + data[k] = JSON.parse(data[k]); + } catch (e) { + throw new ApiError({ + message: 'Invalid param.', + code: 'INVALID_PARAM', + id: '0b5f1631-7c1a-41a6-b399-cce335f34d85', + }, { + param: k, + reason: `cannot cast to ${param.type}`, + }); + } + } + } + } + + if (token && ((ep.meta.kind && !token.permission.some(p => p === ep.meta.kind)) + || (!ep.meta.kind && (ep.meta.requireCredential || ep.meta.requireModerator || ep.meta.requireAdmin)))) { + throw new ApiError({ + message: 'Your app does not have the necessary permissions to use this endpoint.', + code: 'PERMISSION_DENIED', + kind: 'permission', + id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838', + }); + } + if ((ep.meta.requireModerator || ep.meta.requireAdmin) && (this.meta.rootUserId !== user!.id)) { const myRoles = await this.roleService.getUserRoles(user!.id); if (ep.meta.requireModerator && !myRoles.some(r => r.isModerator || r.isAdministrator)) { @@ -402,47 +418,89 @@ export class ApiCallService implements OnApplicationShutdown { } } - if (token && ((ep.meta.kind && !token.permission.some(p => p === ep.meta.kind)) - || (!ep.meta.kind && (ep.meta.requireCredential || ep.meta.requireModerator || ep.meta.requireAdmin)))) { - throw new ApiError({ - message: 'Your app does not have the necessary permissions to use this endpoint.', - code: 'PERMISSION_DENIED', - kind: 'permission', - id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838', - }); - } - - // Cast non JSON input - if ((ep.meta.requireFile || request.method === 'GET') && ep.params.properties) { - for (const k of Object.keys(ep.params.properties)) { - const param = ep.params.properties![k]; - if (['boolean', 'number', 'integer'].includes(param.type ?? '') && typeof data[k] === 'string') { - try { - data[k] = JSON.parse(data[k]); - } catch (e) { - throw new ApiError({ - message: 'Invalid param.', - code: 'INVALID_PARAM', - id: '0b5f1631-7c1a-41a6-b399-cce335f34d85', - }, { - param: k, - reason: `cannot cast to ${param.type}`, - }); - } - } - } + let attachmentFile: AttachmentFile | null = null; + let cleanup = () => {}; + if (ep.meta.requireFile && request.method === 'POST' && multipartFile) { + const policies = await this.roleService.getUserPolicies(user!.id); + const result = await this.handleAttachmentFile( + Math.min((policies.maxFileSizeMb * 1024 * 1024), this.config.maxFileSize), + multipartFile, + ); + attachmentFile = result.attachmentFile; + cleanup = result.cleanup; } // API invoking if (this.config.sentryForBackend) { return await Sentry.startSpan({ name: 'API: ' + ep.name, - }, () => ep.exec(data, user, token, file, request.ip, request.headers) - .catch((err: Error) => this.#onExecError(ep, data, err, user?.id))); + }, () => { + return ep.exec(data, user, token, attachmentFile, request.ip, request.headers) + .catch((err: Error) => this.#onExecError(ep, data, err, user?.id)) + .finally(() => cleanup()); + }); } else { - return await ep.exec(data, user, token, file, request.ip, request.headers) - .catch((err: Error) => this.#onExecError(ep, data, err, user?.id)); + return await ep.exec(data, user, token, attachmentFile, request.ip, request.headers) + .catch((err: Error) => this.#onExecError(ep, data, err, user?.id)) + .finally(() => cleanup()); + } + } + + @bindThis + private async handleAttachmentFile( + fileSizeLimit: number, + multipartFile: MultipartFile, + ) { + function createTooLongError() { + return new ApiError({ + httpStatusCode: 413, + kind: 'client', + message: 'File size is too large.', + code: 'FILE_SIZE_TOO_LARGE', + id: 'ff827ce8-9b4b-4808-8511-422222a3362f', + }); + } + + function createLimitStream(limit: number) { + let total = 0; + + return new Transform({ + transform(chunk, _, callback) { + total += chunk.length; + if (total > limit) { + callback(createTooLongError()); + } else { + callback(null, chunk); + } + }, + }); } + + const [path, cleanup] = await createTemp(); + try { + await stream.pipeline( + multipartFile.file, + createLimitStream(fileSizeLimit), + fs.createWriteStream(path), + ); + + // ファイルサイズが制限を超えていた場合 + // なお truncated はストリームを読み切ってからでないと機能しないため、stream.pipeline より後にある必要がある + if (multipartFile.file.truncated) { + throw createTooLongError(); + } + } catch (err) { + cleanup(); + throw err; + } + + return { + attachmentFile: { + name: multipartFile.filename, + path, + }, + cleanup, + }; } @bindThis diff --git a/packages/backend/src/server/api/endpoint-base.ts b/packages/backend/src/server/api/endpoint-base.ts index e061aa3a8e..b063487305 100644 --- a/packages/backend/src/server/api/endpoint-base.ts +++ b/packages/backend/src/server/api/endpoint-base.ts @@ -21,23 +21,23 @@ ajv.addFormat('misskey:id', /^[a-zA-Z0-9]+$/); export type Response = Record<string, any> | void; -type File = { +export type AttachmentFile = { name: string | null; path: string; }; // TODO: paramsの型をT['params']のスキーマ定義から推論する type Executor<T extends IEndpointMeta, Ps extends Schema> = - (params: SchemaType<Ps>, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, cleanup?: () => any, ip?: string | null, headers?: Record<string, string> | null) => - Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>; + (params: SchemaType<Ps>, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: AttachmentFile, cleanup?: () => any, ip?: string | null, headers?: Record<string, string> | null) => + Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>; export abstract class Endpoint<T extends IEndpointMeta, Ps extends Schema> { - public exec: (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => Promise<any>; + public exec: (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: AttachmentFile, ip?: string | null, headers?: Record<string, string> | null) => Promise<any>; constructor(meta: T, paramDef: Ps, cb: Executor<T, Ps>) { const validate = ajv.compile(paramDef); - this.exec = (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => { + this.exec = (params: any, user: T['requireCredential'] extends true ? MiLocalUser : MiLocalUser | null, token: MiAccessToken | null, file?: AttachmentFile, ip?: string | null, headers?: Record<string, string> | null) => { let cleanup: undefined | (() => void) = undefined; if (meta.requireFile) { diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 57c62b7c89..b90ba6aa0d 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -117,6 +117,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // NOTE: センシティブ除外の設定はこのエンドポイントでは無視する。 // https://github.com/misskey-dev/misskey/pull/15346#discussion_r1929950255 + this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateVisibilityQuery(query, me); this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 5fc0ae00b2..6336f43e9f 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -137,6 +137,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); + this.queryService.generateBlockedHostQueryForNote(query); if (me) { this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/chat/messages/delete.ts b/packages/backend/src/server/api/endpoints/chat/messages/delete.ts index 63b75fb6a7..52a054303b 100644 --- a/packages/backend/src/server/api/endpoints/chat/messages/delete.ts +++ b/packages/backend/src/server/api/endpoints/chat/messages/delete.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchMessage: { message: 'No such message.', diff --git a/packages/backend/src/server/api/endpoints/chat/messages/react.ts b/packages/backend/src/server/api/endpoints/chat/messages/react.ts index 5f61e7e992..2197e7bf80 100644 --- a/packages/backend/src/server/api/endpoints/chat/messages/react.ts +++ b/packages/backend/src/server/api/endpoints/chat/messages/react.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchMessage: { message: 'No such message.', diff --git a/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts b/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts index 6784bb6ecf..adfcd232f9 100644 --- a/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts +++ b/packages/backend/src/server/api/endpoints/chat/messages/unreact.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchMessage: { message: 'No such message.', diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts b/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts index 82a8e1f30d..1ea81448c1 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/delete.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchRoom: { message: 'No such room.', diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts index b8a228089b..88ea234527 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/ignore.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchRoom: { message: 'No such room.', diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/join.ts b/packages/backend/src/server/api/endpoints/chat/rooms/join.ts index d561f9e03f..550b4da1a6 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/join.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/join.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchRoom: { message: 'No such room.', diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts b/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts index a3ad0c2d6f..f99b408d67 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/leave.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchRoom: { message: 'No such room.', diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts b/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts index 11cbe7b8b9..ee60f92505 100644 --- a/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts +++ b/packages/backend/src/server/api/endpoints/chat/rooms/mute.ts @@ -16,9 +16,6 @@ export const meta = { kind: 'write:chat', - res: { - }, - errors: { noSuchRoom: { message: 'No such room.', diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 69ff45a1c6..59513e530d 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -91,6 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser') .andWhere('clipNote.clipId = :clipId', { clipId: clip.id }); + this.queryService.generateBlockedHostQueryForNote(query); if (me) { this.queryService.generateVisibilityQuery(query, me); this.queryService.generateMutedUserQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/drive/files/create.ts b/packages/backend/src/server/api/endpoints/drive/files/create.ts index f67ff6ddc4..7043f4883a 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts @@ -62,6 +62,12 @@ export const meta = { code: 'COMMENT_TOO_LONG', id: '333652d9-0826-40f5-a2c3-e2bedcbb9fe5', }, + + maxFileSizeExceeded: { + message: 'Cannot upload the file because it exceeds the maximum file size.', + code: 'MAX_FILE_SIZE_EXCEEDED', + id: 'b9d8c348-33f0-4673-b9a9-5d4da058977a', + }, }, } as const; @@ -128,6 +134,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (err instanceof IdentifiableError) { if (err.id === '282f77bf-5816-4f72-9264-aa14d8261a21') throw new ApiError(meta.errors.inappropriate); if (err.id === 'c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6') throw new ApiError(meta.errors.noFreeSpace); + if (err.id === 'f9e4e5f3-4df4-40b5-b400-f236945f7073') throw new ApiError(meta.errors.maxFileSizeExceeded); } throw new ApiError(); } finally { diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index c97a0c0bc7..8f19d534d4 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -79,6 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); if (me) { this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 4853489827..734ff31700 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -11,6 +11,7 @@ import { DI } from '@/di-symbols.js'; import { FeaturedService } from '@/core/FeaturedService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { CacheService } from '@/core/CacheService.js'; +import { QueryService } from '@/core/QueryService.js'; export const meta = { tags: ['notes'], @@ -58,6 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private cacheService: CacheService, private noteEntityService: NoteEntityService, private featuredService: FeaturedService, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { let noteIds: string[]; @@ -100,6 +102,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); + this.queryService.generateBlockedHostQueryForNote(query); + const notes = (await query.getMany()).filter(note => { if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false; if (me && isUserRelated(note, userIdsWhoMeMuting)) return false; diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index b0e3327411..6461a2e33f 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -254,6 +254,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); this.queryService.generateMutedUserRenotesQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 864592ed9b..f55853f3f3 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -167,6 +167,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); if (me) this.queryService.generateMutedUserQueryForNotes(query, me); if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index bfcd120f49..269b57366c 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -78,6 +78,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateMutedNoteThreadQuery(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 01f09433a9..0f08cc9cf2 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -91,6 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); if (me) this.queryService.generateMutedUserQueryForNotes(query, me); if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index f04c9b0ec4..0882e19182 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -62,6 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); if (me) this.queryService.generateMutedUserQueryForNotes(query, me); if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index af9bc3b426..91874a8195 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -97,6 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (!this.serverSettings.enableBotTrending) query.andWhere('user.isBot = FALSE'); this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); if (me) this.queryService.generateMutedUserQueryForNotes(query, me); if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 9f19117426..a2dfa7fdac 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -209,6 +209,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- })); this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); this.queryService.generateMutedUserRenotesQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 4c37edfdec..60f18a09b0 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -190,6 +190,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- })); this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); this.queryService.generateMutedUserRenotesQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index dd40e49d3d..d1c2e4b686 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -108,6 +108,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/users/featured-notes.ts b/packages/backend/src/server/api/endpoints/users/featured-notes.ts index e6acae08b1..3fb091cc0e 100644 --- a/packages/backend/src/server/api/endpoints/users/featured-notes.ts +++ b/packages/backend/src/server/api/endpoints/users/featured-notes.ts @@ -11,6 +11,7 @@ import { DI } from '@/di-symbols.js'; import { FeaturedService } from '@/core/FeaturedService.js'; import { CacheService } from '@/core/CacheService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; +import { QueryService } from '@/core/QueryService.js'; export const meta = { tags: ['notes'], @@ -55,6 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private noteEntityService: NoteEntityService, private featuredService: FeaturedService, private cacheService: CacheService, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const userIdsWhoBlockingMe = me ? await this.cacheService.userBlockedCache.fetch(me.id) : new Set<string>(); @@ -91,6 +93,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); + this.queryService.generateBlockedHostQueryForNote(query); + const notes = (await query.getMany()).filter(note => { if (me && isUserRelated(note, userIdsWhoBlockingMe, false)) return false; if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false; diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index a4eee544f0..965baa859a 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -145,6 +145,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- redisTimelines, useDbFallback: true, ignoreAuthorFromMute: true, + ignoreAuthorFromInstanceBlock: true, excludeReplies: ps.withChannelNotes && !ps.withReplies, // userTimelineWithChannel may include replies excludeNoFiles: ps.withChannelNotes && ps.withFiles, // userTimelineWithChannel may include notes without files excludePureRenotes: !ps.withRenotes, @@ -216,6 +217,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query, true); if (me) { this.queryService.generateMutedUserQueryForNotes(query, me, { id: ps.userId }); this.queryService.generateBlockedUserQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 49c1190197..56f59bd285 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -108,6 +108,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('reaction.note', 'note'); this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateBlockedHostQueryForNote(query); const reactions = (await query .limit(ps.limit) diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css index 8094a0f6de..1c63d77e06 100644 --- a/packages/backend/src/server/web/style.css +++ b/packages/backend/src/server/web/style.css @@ -31,6 +31,7 @@ html { margin: auto; width: 64px; height: 64px; + border-radius: 10px; pointer-events: none; } diff --git a/packages/backend/src/server/web/style.embed.css b/packages/backend/src/server/web/style.embed.css index 5e8786cc4e..0911d562bf 100644 --- a/packages/backend/src/server/web/style.embed.css +++ b/packages/backend/src/server/web/style.embed.css @@ -53,6 +53,7 @@ html.embed.noborder #splash { margin: auto; width: 64px; height: 64px; + border-radius: 10px; pointer-events: none; } diff --git a/packages/backend/test-federation/compose.tpl.yml b/packages/backend/test-federation/compose.tpl.yml index 25770063d3..a7e907c3ee 100644 --- a/packages/backend/test-federation/compose.tpl.yml +++ b/packages/backend/test-federation/compose.tpl.yml @@ -75,10 +75,6 @@ services: target: /misskey/pnpm-workspace.yaml read_only: true - type: bind - source: ../../../scripts/dependency-patches - target: /misskey/scripts/dependency-patches - read_only: true - - type: bind source: ./certificates/rootCA.crt target: /usr/local/share/ca-certificates/rootCA.crt read_only: true diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml index dfa51b940a..4df4ced365 100644 --- a/packages/backend/test-federation/compose.yml +++ b/packages/backend/test-federation/compose.yml @@ -71,10 +71,6 @@ services: target: /misskey/pnpm-workspace.yaml read_only: true - type: bind - source: ../../../scripts/dependency-patches - target: /misskey/scripts/dependency-patches - read_only: true - - type: bind source: ./certificates/rootCA.crt target: /usr/local/share/ca-certificates/rootCA.crt read_only: true @@ -118,10 +114,6 @@ services: source: ../../../pnpm-workspace.yaml target: /misskey/pnpm-workspace.yaml read_only: true - - type: bind - source: ../../../scripts/dependency-patches - target: /misskey/scripts/dependency-patches - read_only: true working_dir: /misskey command: > bash -c " diff --git a/packages/backend/test/e2e/api.ts b/packages/backend/test/e2e/api.ts index 49c6a0636b..f9e65aaa84 100644 --- a/packages/backend/test/e2e/api.ts +++ b/packages/backend/test/e2e/api.ts @@ -159,8 +159,8 @@ describe('API', () => { user: { token: application3 }, }, { status: 403, - code: 'ROLE_PERMISSION_DENIED', - id: 'c3d38592-54c0-429d-be96-5636b0431a61', + code: 'PERMISSION_DENIED', + id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838', }); await failedApiCall({ diff --git a/packages/backend/test/unit/server/api/drive/files/create.ts b/packages/backend/test/unit/server/api/drive/files/create.ts new file mode 100644 index 0000000000..b98892fa03 --- /dev/null +++ b/packages/backend/test/unit/server/api/drive/files/create.ts @@ -0,0 +1,108 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { S3Client } from '@aws-sdk/client-s3'; +import { Test, TestingModule } from '@nestjs/testing'; +import { mockClient } from 'aws-sdk-client-mock'; +import { FastifyInstance } from 'fastify'; +import request from 'supertest'; +import { CoreModule } from '@/core/CoreModule.js'; +import { RoleService } from '@/core/RoleService.js'; +import { DI } from '@/di-symbols.js'; +import { GlobalModule } from '@/GlobalModule.js'; +import { MiRole, UserProfilesRepository, UsersRepository } from '@/models/_.js'; +import { MiUser } from '@/models/User.js'; +import { ServerModule } from '@/server/ServerModule.js'; +import { ServerService } from '@/server/ServerService.js'; + +describe('/drive/files/create', () => { + let module: TestingModule; + let server: FastifyInstance; + const s3Mock = mockClient(S3Client); + let roleService: RoleService; + + let root: MiUser; + let role_tinyAttachment: MiRole; + + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [GlobalModule, CoreModule, ServerModule], + }).compile(); + module.enableShutdownHooks(); + + const serverService = module.get<ServerService>(ServerService); + server = await serverService.launch(); + + const usersRepository = module.get<UsersRepository>(DI.usersRepository); + root = await usersRepository.insert({ + id: 'root', + username: 'root', + usernameLower: 'root', + token: '1234567890123456', + }).then(x => usersRepository.findOneByOrFail(x.identifiers[0])); + + const userProfilesRepository = module.get<UserProfilesRepository>(DI.userProfilesRepository); + await userProfilesRepository.insert({ + userId: root.id, + }); + + roleService = module.get<RoleService>(RoleService); + role_tinyAttachment = await roleService.create({ + name: 'test-role001', + description: 'Test role001 description', + target: 'manual', + policies: { + maxFileSizeMb: { + useDefault: false, + priority: 1, + // 10byte + value: 10 / 1024 / 1024, + }, + }, + }); + }); + + beforeEach(async () => { + s3Mock.reset(); + await roleService.unassign(root.id, role_tinyAttachment.id).catch(() => {}); + }); + + afterAll(async () => { + await server.close(); + await module.close(); + }); + + test('200 ok', async () => { + const result = await request(server.server) + .post('/api/drive/files/create') + .set('Content-Type', 'multipart/form-data') + .set('Authorization', `Bearer ${root.token}`) + .attach('file', Buffer.from('a'.repeat(1024 * 1024))); + expect(result.statusCode).toBe(200); + }); + + test('200 ok(with role)', async () => { + await roleService.assign(root.id, role_tinyAttachment.id); + + const result = await request(server.server) + .post('/api/drive/files/create') + .set('Content-Type', 'multipart/form-data') + .set('Authorization', `Bearer ${root.token}`) + .attach('file', Buffer.from('a'.repeat(10))); + expect(result.statusCode).toBe(200); + }); + + test('413 too large', async () => { + await roleService.assign(root.id, role_tinyAttachment.id); + + const result = await request(server.server) + .post('/api/drive/files/create') + .set('Content-Type', 'multipart/form-data') + .set('Authorization', `Bearer ${root.token}`) + .attach('file', Buffer.from('a'.repeat(11))); + expect(result.statusCode).toBe(413); + expect(result.body.error.code).toBe('FILE_SIZE_TOO_LARGE'); + }); +}); diff --git a/packages/frontend-embed/package.json b/packages/frontend-embed/package.json index 40e8802ab4..7788d65305 100644 --- a/packages/frontend-embed/package.json +++ b/packages/frontend-embed/package.json @@ -26,15 +26,15 @@ "json5": "2.2.3", "misskey-js": "workspace:*", "punycode.js": "2.3.1", - "rollup": "4.39.0", - "sass": "1.86.3", - "shiki": "3.2.2", + "rollup": "4.40.0", + "sass": "1.87.0", + "shiki": "3.3.0", "tinycolor2": "1.6.0", "tsc-alias": "1.8.15", "tsconfig-paths": "4.2.0", "typescript": "5.8.3", "uuid": "11.1.0", - "vite": "6.3.1", + "vite": "6.3.3", "vue": "3.5.13" }, "devDependencies": { @@ -42,13 +42,13 @@ "@testing-library/vue": "8.1.0", "@types/estree": "1.0.7", "@types/micromatch": "4.0.9", - "@types/node": "22.14.0", + "@types/node": "22.15.2", "@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/tinycolor2": "1.4.6", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.29.1", - "@typescript-eslint/parser": "8.29.1", - "@vitest/coverage-v8": "3.1.1", + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", + "@vitest/coverage-v8": "3.1.2", "@vue/runtime-core": "3.5.13", "acorn": "8.14.1", "cross-env": "7.0.3", @@ -58,13 +58,13 @@ "happy-dom": "17.4.4", "intersection-observer": "0.12.2", "micromatch": "4.0.8", - "msw": "2.7.3", - "nodemon": "3.1.9", + "msw": "2.7.5", + "nodemon": "3.1.10", "prettier": "3.5.3", "start-server-and-test": "2.0.11", "vite-plugin-turbosnap": "1.0.3", - "vue-component-type-helpers": "2.2.8", + "vue-component-type-helpers": "2.2.10", "vue-eslint-parser": "10.1.3", - "vue-tsc": "2.2.8" + "vue-tsc": "2.2.10" } } diff --git a/packages/frontend-shared/js/collapsed.ts b/packages/frontend-shared/js/collapsed.ts index af1f88cb73..aa24c43bcb 100644 --- a/packages/frontend-shared/js/collapsed.ts +++ b/packages/frontend-shared/js/collapsed.ts @@ -6,17 +6,30 @@ import * as Misskey from 'misskey-js'; export function shouldCollapsed(note: Misskey.entities.Note, urls: string[]): boolean { - const collapsed = note.cw == null && ( - (note.text != null && ( - (note.text.includes('$[x2')) || - (note.text.includes('$[x3')) || - (note.text.includes('$[x4')) || - (note.text.includes('$[scale')) || - (note.text.split('\n').length > 9) || - (note.text.length > 500) || - (urls.length >= 4) - )) || (note.files != null && note.files.length >= 5) - ); + if (note.cw != null) { + return false; + } - return collapsed; + if (note.text != null) { + if ( + note.text.includes('$[x2') || + note.text.includes('$[x3') || + note.text.includes('$[x4') || + note.text.includes('$[scale') || + note.text.split('\n').length > 9 || + note.text.length > 500 + ) { + return true; + } + } + + if (urls.length >= 4) { + return true; + } + + if (note.files != null && note.files.length >= 5) { + return true; + } + + return false; } diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend-shared/js/const.ts index 22e4e36292..0aba2b486e 100644 --- a/packages/frontend-shared/js/const.ts +++ b/packages/frontend-shared/js/const.ts @@ -157,6 +157,7 @@ export const ROLE_POLICIES = [ 'canUseTranslator', 'canHideAds', 'driveCapacityMb', + 'maxFileSizeMb', 'alwaysMarkNsfw', 'canUpdateBioMedia', 'pinLimit', diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json index a4874b8f77..f129121d19 100644 --- a/packages/frontend-shared/package.json +++ b/packages/frontend-shared/package.json @@ -21,12 +21,12 @@ "lint": "pnpm typecheck && pnpm eslint" }, "devDependencies": { - "@types/node": "22.14.0", - "@typescript-eslint/eslint-plugin": "8.29.1", - "@typescript-eslint/parser": "8.29.1", - "esbuild": "0.25.2", + "@types/node": "22.15.2", + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", + "esbuild": "0.25.3", "eslint-plugin-vue": "10.0.0", - "nodemon": "3.1.9", + "nodemon": "3.1.10", "typescript": "5.8.3", "vue-eslint-parser": "10.1.3" }, diff --git a/packages/frontend/.storybook/changes.ts b/packages/frontend/.storybook/changes.ts index 1299910499..c7e0048818 100644 --- a/packages/frontend/.storybook/changes.ts +++ b/packages/frontend/.storybook/changes.ts @@ -55,7 +55,7 @@ await fs.readFile( '../../locales/ja-JP.yml', 'assets/**', 'public/**', - '../../pnpm-lock.yaml', + 'package.json', ]).length ) { return; diff --git a/packages/frontend/package.json b/packages/frontend/package.json index d3453fb8cb..4810d40fc6 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -25,7 +25,7 @@ "@rollup/plugin-replace": "6.0.2", "@rollup/pluginutils": "5.1.4", "@ruffle-rs/ruffle": "0.1.0-nightly.2024.10.15", - "@sentry/vue": "9.12.0", + "@sentry/vue": "9.14.0", "@syuilo/aiscript": "0.19.0", "@transfem-org/sfm-js": "0.24.6", "@twemoji/parser": "15.1.1", @@ -36,12 +36,12 @@ "broadcast-channel": "7.1.0", "buraha": "0.0.1", "canvas-confetti": "1.9.3", - "chart.js": "4.4.8", + "chart.js": "4.4.9", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-matrix": "2.1.1", "chartjs-plugin-gradient": "0.6.1", "chartjs-plugin-zoom": "2.2.0", - "chromatic": "11.28.0", + "chromatic": "11.28.2", "compare-versions": "6.1.1", "cropperjs": "2.0.0", "date-fns": "4.1.0", @@ -61,13 +61,13 @@ "moment": "^2.30.1", "photoswipe": "5.4.4", "punycode.js": "2.3.1", - "rollup": "4.39.0", - "sanitize-html": "2.15.0", - "sass": "1.86.3", - "shiki": "3.2.2", + "rollup": "4.40.0", + "sanitize-html": "2.16.0", + "sass": "1.87.0", + "shiki": "3.3.0", "strict-event-emitter-types": "2.0.0", "textarea-caret": "3.1.0", - "three": "0.175.0", + "three": "0.176.0", "throttle-debounce": "5.0.2", "tinycolor2": "1.6.0", "tsc-alias": "1.8.15", @@ -75,13 +75,13 @@ "typescript": "5.8.3", "uuid": "11.1.0", "v-code-diff": "1.13.1", - "vite": "6.3.1", + "vite": "6.3.3", "vue": "3.5.13", "vuedraggable": "next", "wanakana": "5.3.1" }, "optionalDependencies": { - "cypress": "13.15.2" + "cypress": "14.3.2" }, "devDependencies": { "@misskey-dev/summaly": "5.2.1", @@ -109,16 +109,16 @@ "@types/katex": "^0.16.7", "@types/matter-js": "0.19.8", "@types/micromatch": "4.0.9", - "@types/node": "22.14.0", + "@types/node": "22.15.2", "@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/sanitize-html": "2.15.0", "@types/seedrandom": "3.0.8", "@types/throttle-debounce": "5.0.2", "@types/tinycolor2": "1.4.6", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.29.1", - "@typescript-eslint/parser": "8.29.1", - "@vitest/coverage-v8": "3.1.1", + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", + "@vitest/coverage-v8": "3.1.2", "@vue/compiler-core": "3.5.13", "@vue/runtime-core": "3.5.13", "acorn": "8.14.1", @@ -130,9 +130,9 @@ "intersection-observer": "0.12.2", "micromatch": "4.0.8", "minimatch": "10.0.1", - "msw": "2.7.3", + "msw": "2.7.5", "msw-storybook-addon": "2.0.4", - "nodemon": "3.1.9", + "nodemon": "3.1.10", "prettier": "3.5.3", "react": "19.1.0", "react-dom": "19.1.0", @@ -141,10 +141,10 @@ "storybook": "8.6.12", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "vite-plugin-turbosnap": "1.0.3", - "vitest": "3.1.1", + "vitest": "3.1.2", "vitest-fetch-mock": "0.4.5", - "vue-component-type-helpers": "2.2.8", + "vue-component-type-helpers": "2.2.10", "vue-eslint-parser": "10.1.3", - "vue-tsc": "2.2.8" + "vue-tsc": "2.2.10" } } diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index dbac5e9dd7..61297fdc76 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </I18n> </template> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps_m" :class="$style.root"> <div class=""> <MkTextarea v-model="comment"> @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary full :disabled="comment.length === 0" @click="send">{{ i18n.ts.send }}</MkButton> </div> </div> - </MkSpacer> + </div> </MkWindow> </template> diff --git a/packages/frontend/src/components/MkAntennaEditor.vue b/packages/frontend/src/components/MkAntennaEditor.vue index 59099d54bd..e2febf7225 100644 --- a/packages/frontend/src/components/MkAntennaEditor.vue +++ b/packages/frontend/src/components/MkAntennaEditor.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="700"> +<div class="_spacer" style="--MI_SPACER-w: 700px;"> <div> <div class="_gaps_m"> <MkInput v-model="name"> @@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </div> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue index 54fda6bf7c..ed5a20b4eb 100644 --- a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue +++ b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkModalWindow ref="dialogEl" @close="cancel()" @closed="emit('closed')"> <template #header>:{{ emoji.name }}:</template> <template #default> - <MkSpacer> + <div class="_spacer"> <div style="display: flex; flex-direction: column; gap: 1em;"> <div :class="$style.emojiImgWrapper"> <MkCustomEmoji :name="emoji.name" :normal="true" :useOriginalSize="true" style="height: 100%;"></MkCustomEmoji> @@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </MkKeyValue> </div> - </MkSpacer> + </div> </template> </MkModalWindow> </template> diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index cb7c270e91..3627704c1b 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -52,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only @contextmenu.stop="onContextmenu" > <div ref="contents"> + <MkInfo v-if="!store.r.readDriveTip.value" closable @close="closeTip()"><div v-html="i18n.ts.driveAboutTip"></div></MkInfo> <div v-show="folders.length > 0" ref="foldersContainer" :class="$style.folders"> <XFolder v-for="(f, i) in folders" @@ -108,6 +109,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MkButton from './MkButton.vue'; +import MkInfo from './MkInfo.vue'; import type { MenuItem } from '@/types/menu.js'; import XNavFolder from '@/components/MkDrive.navFolder.vue'; import XFolder from '@/components/MkDrive.folder.vue'; @@ -121,6 +123,7 @@ import { uploadFile, uploads } from '@/utility/upload.js'; import { claimAchievement } from '@/utility/achievements.js'; import { prefer } from '@/preferences.js'; import { chooseFileFromPc } from '@/utility/select-file.js'; +import { store } from '@/store.js'; const searchQuery = ref(''); @@ -723,6 +726,10 @@ function onContextmenu(ev: MouseEvent) { os.contextMenu(getMenu(), ev); } +function closeTip() { + store.set('readDriveTip', true); +} + onMounted(() => { if (prefer.s.enableInfiniteScroll && loadMoreFiles.value) { nextTick(() => { diff --git a/packages/frontend/src/components/MkFileCaptionEditWindow.vue b/packages/frontend/src/components/MkFileCaptionEditWindow.vue index 7b5eefdc10..bdfbe13fb4 100644 --- a/packages/frontend/src/components/MkFileCaptionEditWindow.vue +++ b/packages/frontend/src/components/MkFileCaptionEditWindow.vue @@ -15,12 +15,12 @@ SPDX-License-Identifier: AGPL-3.0-only @closed="emit('closed')" > <template #header>{{ i18n.ts.describeFile }}</template> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <MkDriveFileThumbnail :file="file" fit="contain" style="height: 193px; margin-bottom: 16px;"/> <MkTextarea v-model="caption" autofocus :placeholder="i18n.ts.inputNewDescription" @keydown="onKeydown($event)"> <template #label>{{ i18n.ts.caption }}</template> </MkTextarea> - </MkSpacer> + </div> </MkModalWindow> </template> diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue index c228853bea..2e5d0a3dea 100644 --- a/packages/frontend/src/components/MkFolder.vue +++ b/packages/frontend/src/components/MkFolder.vue @@ -31,10 +31,6 @@ SPDX-License-Identifier: AGPL-3.0-only :leaveActiveClass="prefer.s.animation ? $style.transition_toggle_leaveActive : ''" :enterFromClass="prefer.s.animation ? $style.transition_toggle_enterFrom : ''" :leaveToClass="prefer.s.animation ? $style.transition_toggle_leaveTo : ''" - @enter="enter" - @afterEnter="afterEnter" - @leave="leave" - @afterLeave="afterLeave" > <KeepAlive> <div v-show="opened"> @@ -45,9 +41,9 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> - <MkSpacer v-if="withSpacer" :marginMin="spacerMin" :marginMax="spacerMax"> + <div v-if="withSpacer" class="_spacer" :style="{ '--MI_SPACER-min': props.spacerMin + 'px', '--MI_SPACER-max': props.spacerMax + 'px' }"> <slot></slot> - </MkSpacer> + </div> <div v-else> <slot></slot> </div> @@ -90,32 +86,6 @@ const bgSame = ref(false); const opened = ref(props.defaultOpen); const openedAtLeastOnce = ref(props.defaultOpen); -function enter(el: Element) { - if (!(el instanceof HTMLElement)) return; - const elementHeight = el.getBoundingClientRect().height; - el.style.height = '0'; - el.offsetHeight; // reflow - el.style.height = `${Math.min(elementHeight, props.maxHeight ?? Infinity)}px`; -} - -function afterEnter(el: Element) { - if (!(el instanceof HTMLElement)) return; - el.style.height = ''; -} - -function leave(el: Element) { - if (!(el instanceof HTMLElement)) return; - const elementHeight = el.getBoundingClientRect().height; - el.style.height = `${elementHeight}px`; - el.offsetHeight; // reflow - el.style.height = '0'; -} - -function afterLeave(el: Element) { - if (!(el instanceof HTMLElement)) return; - el.style.height = ''; -} - function toggle() { if (!opened.value) { openedAtLeastOnce.value = true; @@ -137,16 +107,18 @@ onMounted(() => { <style lang="scss" module> .transition_toggle_enterActive, .transition_toggle_leaveActive { - overflow-y: clip; - transition: opacity 0.3s, height 0.3s, transform 0.3s !important; + overflow-y: hidden; // 子要素のmarginが突き出るため clip を使ってはいけない + transition: opacity 0.3s, height 0.3s !important; } .transition_toggle_enterFrom, .transition_toggle_leaveTo { opacity: 0; + height: 0; } .root { display: block; + interpolate-size: allow-keywords; // heightのtransitionを動作させるために必要 } .header { diff --git a/packages/frontend/src/components/MkForgotPassword.vue b/packages/frontend/src/components/MkForgotPassword.vue index 35112ad45d..57946aaf2b 100644 --- a/packages/frontend/src/components/MkForgotPassword.vue +++ b/packages/frontend/src/components/MkForgotPassword.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only > <template #header>{{ i18n.ts.forgotPassword }}</template> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <form v-if="instance.enableEmail" @submit.prevent="onSubmit"> <div class="_gaps_m"> <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required> @@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-else> {{ i18n.ts._forgotPassword.contactAdmin }} </div> - </MkSpacer> + </div> </MkModalWindow> </template> diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue index 4756079e76..0884cdc016 100644 --- a/packages/frontend/src/components/MkFormDialog.vue +++ b/packages/frontend/src/components/MkFormDialog.vue @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ title }} </template> - <MkSpacer :marginMin="20" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 32px;"> <div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m"> <template v-for="(v, k) in Object.fromEntries(Object.entries(form))"> <template v-if="typeof v.hidden == 'function' ? v.hidden(values) : v.hidden"></template> @@ -66,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only <img :src="infoImageUrl" draggable="false"/> <div>{{ i18n.ts.nothing }}</div> </div> - </MkSpacer> + </div> </MkModalWindow> </template> diff --git a/packages/frontend/src/components/MkInfo.vue b/packages/frontend/src/components/MkInfo.vue index 410e3accab..fb66098a54 100644 --- a/packages/frontend/src/components/MkInfo.vue +++ b/packages/frontend/src/components/MkInfo.vue @@ -39,7 +39,6 @@ function closeInfo() { background: color-mix(in srgb, var(--MI_THEME-infoBg) 65%, transparent); color: var(--MI_THEME-infoFg); border-radius: var(--MI-radius); - white-space: pre-wrap; z-index: 1; &.warn { diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 51836ce093..55efc3c193 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -114,7 +114,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA> </bdi> </div> - <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" :note="appearNote" :maxNumber="16" @click.stop @mockUpdateMyReaction="emitUpdReaction"> + <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" style="margin-top: 6px;" :note="appearNote" :maxNumber="16" @click.stop @mockUpdateMyReaction="emitUpdReaction"> <template #more> <MkA :to="`/notes/${appearNote.id}/reactions`" :class="[$style.reactionOmitted]">{{ i18n.ts.more }}</MkA> </template> diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 3c3136a705..488ee16e62 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -133,7 +133,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkTime :time="appearNote.createdAt" mode="detail" colored/> </MkA> </div> - <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" ref="reactionsViewer" :note="appearNote"/> + <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" ref="reactionsViewer" style="margin-top: 6px;" :note="appearNote"/> <button class="_button" :class="$style.noteFooterButton" @click="reply()"> <i class="ti ti-arrow-back-up"></i> <p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p> diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue index 5edae908b0..602619402a 100644 --- a/packages/frontend/src/components/MkNotes.vue +++ b/packages/frontend/src/components/MkNotes.vue @@ -13,16 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <template #default="{ items: notes }"> - <component - :is="prefer.s.animation ? TransitionGroup : 'div'" - :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap, [$style.reverse]: pagination.reversed }]" - :enterActiveClass="$style.transition_x_enterActive" - :leaveActiveClass="$style.transition_x_leaveActive" - :enterFromClass="$style.transition_x_enterFrom" - :leaveToClass="$style.transition_x_leaveTo" - :moveClass=" $style.transition_x_move" - tag="div" - > + <div :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap, [$style.reverse]: pagination.reversed }]"> <template v-for="(note, i) in notes" :key="note.id"> <div v-if="note._shouldInsertAd_" :class="[$style.noteWithAd, { '_gaps': !noGap }]" :data-scroll-anchor="note.id"> <DynamicNote :class="$style.note" :note="note as Misskey.entities.Note" :withHardMute="true"/> @@ -32,20 +23,19 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <DynamicNote v-else :class="$style.note" :note="note as Misskey.entities.Note" :withHardMute="true" :data-scroll-anchor="note.id"/> </template> - </component> + </div> </template> </MkPagination> </template> <script lang="ts" setup> import * as Misskey from 'misskey-js'; -import { useTemplateRef, TransitionGroup } from 'vue'; +import { useTemplateRef } from 'vue'; import type { Paging } from '@/components/MkPagination.vue'; import DynamicNote from '@/components/DynamicNote.vue'; import MkPagination from '@/components/MkPagination.vue'; import { i18n } from '@/i18n.js'; import { infoImageUrl } from '@/instance.js'; -import { prefer } from '@/preferences.js'; const props = defineProps<{ pagination: Paging; @@ -61,20 +51,6 @@ defineExpose({ </script> <style lang="scss" module> -.transition_x_move, -.transition_x_enterActive, -.transition_x_leaveActive { - transition: opacity 0.3s cubic-bezier(0,.5,.5,1), transform 0.3s cubic-bezier(0,.5,.5,1) !important; -} -.transition_x_enterFrom, -.transition_x_leaveTo { - opacity: 0; - transform: translateY(-50%); -} -.transition_x_leaveActive { - position: absolute; -} - .reverse { display: flex; flex-direction: column-reverse; diff --git a/packages/frontend/src/components/MkNotificationSelectWindow.vue b/packages/frontend/src/components/MkNotificationSelectWindow.vue index d074dceb2f..bb01a008bd 100644 --- a/packages/frontend/src/components/MkNotificationSelectWindow.vue +++ b/packages/frontend/src/components/MkNotificationSelectWindow.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only > <template #header>{{ i18n.ts.notificationSetting }}</template> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps_m"> <MkInfo>{{ i18n.ts.notificationSettingDesc }}</MkInfo> <div class="_buttons"> @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype].value">{{ i18n.ts._notification._types[ntype] }}</MkSwitch> </div> - </MkSpacer> + </div> </MkModalWindow> </template> diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index d5b43cbf2e..a9e4704b24 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </template> - <div :class="$style.root"> + <div :class="$style.root" class="_forceShrinkSpacer"> <StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :key="reloadCount" :router="windowRouter"/> <RouterView v-else :key="reloadCount" :router="windowRouter"/> </div> @@ -121,7 +121,6 @@ provideMetadataReceiver((metadataGetter) => { provideReactiveMetadata(pageMetadata); provide('shouldOmitHeaderTitle', true); provide('shouldHeaderThin', true); -provide(DI.forceSpacerMin, true); provide('shouldBackButton', false); const contextmenu = computed(() => ([{ diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue index 2abf8669ed..826081ffe5 100644 --- a/packages/frontend/src/components/MkPasswordDialog.vue +++ b/packages/frontend/src/components/MkPasswordDialog.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only > <template #header>{{ i18n.ts.authentication }}</template> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div style="padding: 0 0 16px 0; text-align: center;"> <img src="/client-assets/locked_with_key_3d.png" alt="🔐" style="display: block; margin: 0 auto; width: 48px;"> <div style="margin-top: 16px;">{{ i18n.ts.authenticationRequiredToContinue }}</div> @@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton :disabled="(password ?? '') == '' || ($i.twoFactorEnabled && (token ?? '') == '')" type="submit" primary rounded style="margin: 0 auto;"><i class="ti ti-lock-open"></i> {{ i18n.ts.continue }}</MkButton> </div> </form> - </MkSpacer> + </div> </MkModalWindow> </template> diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index e66a056a3f..494b61ca9d 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -182,7 +182,6 @@ if (!mock) { .root { display: inline-flex; height: 42px; - margin: 2px; padding: 0 6px; font-size: 1.5em; border-radius: var(--MI-radius-sm); diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue index 6b19f4a55c..70d0ddca0a 100644 --- a/packages/frontend/src/components/MkReactionsViewer.vue +++ b/packages/frontend/src/components/MkReactionsViewer.vue @@ -106,7 +106,7 @@ watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumbe display: flex; flex-wrap: wrap; align-items: center; - margin: 4px -2px 0 -2px; + gap: 4px; cursor: auto; /* not clickToOpen-able */ &:empty { diff --git a/packages/frontend/src/components/MkRemoteEmojiEditDialog.vue b/packages/frontend/src/components/MkRemoteEmojiEditDialog.vue index dc9bacf481..cb50df1743 100644 --- a/packages/frontend/src/components/MkRemoteEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkRemoteEmojiEditDialog.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header>:{{ name }}:</template> <div style="display: flex; flex-direction: column; min-height: 100%;"> - <MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px; flex-grow: 1;"> <div class="_gaps_m"> <div v-if="imgUrl != null" :class="$style.imgs"> <div style="background: #000;" :class="$style.imgContainer"> @@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #value>{{ license }}</template> </MkKeyValue> </div> - </MkSpacer> + </div> <div :class="$style.footer"> <MkButton primary rounded style="margin: 0 auto;" @click="done"> <i class="ti ti-plus"></i> {{ i18n.ts.import }} diff --git a/packages/frontend/src/components/MkRoleSelectDialog.vue b/packages/frontend/src/components/MkRoleSelectDialog.vue index fd56e4902c..6888824437 100644 --- a/packages/frontend/src/components/MkRoleSelectDialog.vue +++ b/packages/frontend/src/components/MkRoleSelectDialog.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only @closed="emit('closed')" > <template #header>{{ title }}</template> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <MkLoading v-if="fetching"/> <div v-else class="_gaps" :class="$style.root"> <div :class="$style.header"> @@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton @click="onCancelClicked">{{ i18n.ts.cancel }}</MkButton> </div> </div> - </MkSpacer> + </div> </MkModalWindow> </template> @@ -51,7 +51,6 @@ import MkInfo from '@/components/MkInfo.vue'; import MkRolePreview from '@/components/MkRolePreview.vue'; import { misskeyApi } from '@/utility/misskey-api.js'; import * as os from '@/os.js'; -import MkSpacer from '@/components/global/MkSpacer.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import MkLoading from '@/components/global/MkLoading.vue'; diff --git a/packages/frontend/src/components/MkSchedulePostListDialog.vue b/packages/frontend/src/components/MkSchedulePostListDialog.vue index 0bcbb41192..41f366b082 100644 --- a/packages/frontend/src/components/MkSchedulePostListDialog.vue +++ b/packages/frontend/src/components/MkSchedulePostListDialog.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only @close="cancel()" > <template #header>{{ i18n.ts.schedulePostList }}</template> - <MkSpacer :marginMin="14" :marginMax="16"> + <div class="_spacer" style="--MI_SPACER-min: 14px; --MI_SPACER-max: 16px;"> <MkPagination ref="paginationEl" :pagination="pagination"> <template #empty> <div class="_fullinfo"> @@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> </MkPagination> - </MkSpacer> + </div> </MkModalWindow> </template> diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index b152ba81a6..365b23f4ce 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.banner"> <i class="ti ti-user-edit"></i> </div> - <MkSpacer :marginMin="20" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 32px;"> <form class="_gaps_m" autocomplete="new-password" @submit.prevent="onSubmit"> <MkInput v-if="instance.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required> <template #label>{{ i18n.ts.invitationCode }}</template> @@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else>{{ i18n.ts.start }}</template> </MkButton> </form> - </MkSpacer> + </div> </div> </template> diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue index f3d358c874..3034f2269b 100644 --- a/packages/frontend/src/components/MkSignupDialog.rules.vue +++ b/packages/frontend/src/components/MkSignupDialog.rules.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.banner"> <i class="ti ti-checklist"></i> </div> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps_m"> <div v-if="instance.disableRegistration || instance.federation !== 'all'" class="_gaps_s"> <MkInfo v-if="instance.disableRegistration" warn>{{ i18n.ts.invitationRequiredToRegister }}</MkInfo> @@ -59,7 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton inline primary rounded gradate :disabled="!agreed" data-cy-signup-rules-continue @click="emit('done')">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton> </div> </div> - </MkSpacer> + </div> </div> </template> diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.vue b/packages/frontend/src/components/MkSystemWebhookEditor.vue index 86e755a3c3..cd72204fce 100644 --- a/packages/frontend/src/components/MkSystemWebhookEditor.vue +++ b/packages/frontend/src/components/MkSystemWebhookEditor.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <div style="display: flex; flex-direction: column; min-height: 100%;"> - <MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px; flex-grow: 1;"> <MkLoading v-if="loading !== 0"/> <div v-else :class="$style.root" class="_gaps_m"> <MkInput v-model="title"> @@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> </div> - </MkSpacer> + </div> <div :class="$style.footer" class="_buttonsCenter"> <MkButton primary rounded :disabled="disableSubmitButton" @click="onSubmitClicked"> <i class="ti ti-check"></i> diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 7b740167ec..32b2e1ae11 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -5,29 +5,55 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkPullToRefresh ref="prComponent" :refresher="() => reloadTimeline()"> - <MkNotes - v-if="paginationQuery" - ref="tlComponent" - :pagination="paginationQuery" - :noGap="!prefer.s.showGapBetweenNotesInTimeline" - @queue="emit('queue', $event)" - @status="prComponent?.setDisabled($event)" - /> + <MkPagination v-if="paginationQuery" ref="pagingComponent" :pagination="paginationQuery" @queue="emit('queue', $event)" @status="prComponent?.setDisabled($event)"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" draggable="false"/> + <div>{{ i18n.ts.noNotes }}</div> + </div> + </template> + + <template #default="{ items: notes }"> + <component + :is="prefer.s.animation ? TransitionGroup : 'div'" + :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap, [$style.reverse]: paginationQuery.reversed }]" + :enterActiveClass="$style.transition_x_enterActive" + :leaveActiveClass="$style.transition_x_leaveActive" + :enterFromClass="$style.transition_x_enterFrom" + :leaveToClass="$style.transition_x_leaveTo" + :moveClass=" $style.transition_x_move" + tag="div" + > + <template v-for="(note, i) in notes" :key="note.id"> + <div v-if="note._shouldInsertAd_" :class="[$style.noteWithAd, { '_gaps': !noGap }]" :data-scroll-anchor="note.id"> + <MkNote :class="$style.note" :note="note" :withHardMute="true"/> + <div :class="$style.ad"> + <MkAd :preferForms="['horizontal', 'horizontal-big']"/> + </div> + </div> + <MkNote v-else :class="$style.note" :note="note" :withHardMute="true" :data-scroll-anchor="note.id"/> + </template> + </component> + </template> + </MkPagination> </MkPullToRefresh> </template> <script lang="ts" setup> -import { computed, watch, onUnmounted, provide, useTemplateRef } from 'vue'; +import { computed, watch, onUnmounted, provide, useTemplateRef, TransitionGroup } from 'vue'; import * as Misskey from 'misskey-js'; import type { BasicTimelineType } from '@/timelines.js'; import type { Paging } from '@/components/MkPagination.vue'; -import MkNotes from '@/components/MkNotes.vue'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; import { useStream } from '@/stream.js'; import * as sound from '@/utility/sound.js'; import { $i } from '@/i.js'; import { instance } from '@/instance.js'; import { prefer } from '@/preferences.js'; +import MkNote from '@/components/MkNote.vue'; +import MkPagination from '@/components/MkPagination.vue'; +import { i18n } from '@/i18n.js'; +import { infoImageUrl } from '@/instance.js'; const props = withDefaults(defineProps<{ src: BasicTimelineType | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role'; @@ -71,12 +97,12 @@ type TimelineQueryType = { }; const prComponent = useTemplateRef('prComponent'); -const tlComponent = useTemplateRef('tlComponent'); +const pagingComponent = useTemplateRef('pagingComponent'); let tlNotesCount = 0; function prepend(note: Misskey.entities.Note) { - if (tlComponent.value == null) return; + if (pagingComponent.value == null) return; tlNotesCount++; @@ -84,7 +110,7 @@ function prepend(note: Misskey.entities.Note) { note._shouldInsertAd_ = true; } - tlComponent.value.pagingComponent?.prepend(note); + pagingComponent.value.prepend(note); emit('note'); @@ -96,6 +122,7 @@ function prepend(note: Misskey.entities.Note) { let connection: Misskey.ChannelConnection | null = null; let connection2: Misskey.ChannelConnection | null = null; let paginationQuery: Paging | null = null; +const noGap = !prefer.s.showGapBetweenNotesInTimeline; const stream = useStream(); @@ -290,11 +317,11 @@ onUnmounted(() => { function reloadTimeline() { return new Promise<void>((res) => { - if (tlComponent.value == null) return; + if (pagingComponent.value == null) return; tlNotesCount = 0; - tlComponent.value.pagingComponent?.reload().then(() => { + pagingComponent.value.reload().then(() => { res(); }); }); @@ -304,3 +331,56 @@ defineExpose({ reloadTimeline, }); </script> + +<style lang="scss" module> +.transition_x_move, +.transition_x_enterActive, +.transition_x_leaveActive { + transition: opacity 0.3s cubic-bezier(0,.5,.5,1), transform 0.3s cubic-bezier(0,.5,.5,1) !important; +} +.transition_x_enterFrom, +.transition_x_leaveTo { + opacity: 0; + transform: translateY(-50%); +} +.transition_x_leaveActive { + position: absolute; +} + +.reverse { + display: flex; + flex-direction: column-reverse; +} + +.root { + container-type: inline-size; + + &.noGap { + background: var(--MI_THEME-panel); + + .note { + border-bottom: solid 0.5px var(--MI_THEME-divider); + } + + .ad { + padding: 8px; + background-size: auto auto; + background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--MI_THEME-bg) 8px, var(--MI_THEME-bg) 14px); + border-bottom: solid 0.5px var(--MI_THEME-divider); + } + } + + &:not(.noGap) { + background: var(--MI_THEME-bg); + + .note { + background: var(--MI_THEME-panel); + border-radius: var(--MI-radius); + } + } +} + +.ad:empty { + display: none; +} +</style> diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue index b449155edb..42cb6f1e82 100644 --- a/packages/frontend/src/components/MkTokenGenerateWindow.vue +++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only > <template #header>{{ title || i18n.ts.generateAccessToken }}</template> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps_m"> <div v-if="information"> <MkInfo warn>{{ information }}</MkInfo> @@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </div> - </MkSpacer> + </div> </MkModalWindow> </template> diff --git a/packages/frontend/src/components/MkTutorialDialog.vue b/packages/frontend/src/components/MkTutorialDialog.vue index 92f71b01af..d6abbf6504 100644 --- a/packages/frontend/src/components/MkTutorialDialog.vue +++ b/packages/frontend/src/components/MkTutorialDialog.vue @@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-if="page === 0"> <div :class="$style.centerPage"> <MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps" style="text-align: center;"> <i class="ti ti-confetti" style="display: block; margin: auto; font-size: 3em; color: var(--MI_THEME-accent);"></i> <div style="font-size: 120%;">{{ i18n.ts._initialTutorial._landing.title }}</div> @@ -37,15 +37,15 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary rounded gradate style="margin: 16px auto 0 auto;" @click="page++">{{ i18n.ts._initialTutorial.launchTutorial }} <i class="ti ti-arrow-right"></i></MkButton> <MkButton style="margin: 0 auto;" transparent rounded @click="close(true)">{{ i18n.ts.close }}</MkButton> </div> - </MkSpacer> + </div> </div> </template> <template v-else-if="page === 1"> <div style="height: 100cqh; overflow: auto;"> <div :class="$style.pageRoot"> - <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain"> <XNote phase="aboutNote"/> - </MkSpacer> + </div> <div :class="$style.pageFooter"> <div class="_buttonsCenter"> <MkButton v-if="initialPage !== 1" rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> @@ -58,12 +58,12 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else-if="page === 2"> <div style="height: 100cqh; overflow: auto;"> <div :class="$style.pageRoot"> - <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain"> <div class="_gaps"> <XNote phase="howToReact" @reacted="isReactionTutorialPushed = true"/> <div v-if="!isReactionTutorialPushed">{{ i18n.ts._initialTutorial._reaction.reactToContinue }}</div> </div> - </MkSpacer> + </div> <div :class="$style.pageFooter"> <div class="_buttonsCenter"> <MkButton v-if="initialPage !== 2" rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> @@ -76,9 +76,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else-if="page === 3"> <div style="height: 100cqh; overflow: auto;"> <div :class="$style.pageRoot"> - <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain"> <XTimeline/> - </MkSpacer> + </div> <div :class="$style.pageFooter"> <div class="_buttonsCenter"> <MkButton v-if="initialPage !== 3" rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> @@ -91,9 +91,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else-if="page === 4"> <div style="height: 100cqh; overflow: auto;"> <div :class="$style.pageRoot"> - <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain"> <XPostNote/> - </MkSpacer> + </div> <div :class="$style.pageFooter"> <div class="_buttonsCenter"> <MkButton v-if="initialPage !== 3" rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> @@ -106,12 +106,12 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else-if="page === 5"> <div style="height: 100cqh; overflow: auto;"> <div :class="$style.pageRoot"> - <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain"> <div class="_gaps"> <XSensitive @succeeded="isSensitiveTutorialSucceeded = true"/> <div v-if="!isSensitiveTutorialSucceeded">{{ i18n.ts._initialTutorial._howToMakeAttachmentsSensitive.doItToContinue }}</div> </div> - </MkSpacer> + </div> <div :class="$style.pageFooter"> <div class="_buttonsCenter"> <MkButton v-if="initialPage !== 2" rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> @@ -124,7 +124,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else-if="page === 6"> <div :class="$style.centerPage"> <MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps" style="text-align: center;"> <i class="ti ti-check" style="display: block; margin: auto; font-size: 3em; color: var(--MI_THEME-accent);"></i> <div style="font-size: 120%;">{{ i18n.ts._initialTutorial._done.title }}</div> @@ -139,7 +139,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton rounded primary gradate @click="close(false)">{{ i18n.ts.close }}</MkButton> </div> </div> - </MkSpacer> + </div> </div> </template> </Transition> diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue index 34e86444ad..aaefa5036a 100644 --- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue +++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else #header>New announcement</template> <div> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps_m"> <MkInput v-model="title"> <template #label>{{ i18n.ts.title }}</template> @@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> <MkButton v-if="announcement" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> - </MkSpacer> + </div> <div :class="$style.footer"> <MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.announcement ? i18n.ts.update : i18n.ts.create }}</MkButton> </div> diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue index 767f5c591a..82214ed5a5 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.vue @@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-if="page === 0"> <div :class="$style.centerPage"> <MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps" style="text-align: center;"> <i class="ti ti-confetti" style="display: block; margin: auto; font-size: 3em; color: var(--MI_THEME-accent);"></i> <div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.accountCreated }}</div> @@ -41,15 +41,15 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary rounded gradate style="margin: 16px auto 0 auto;" data-cy-user-setup-continue @click="page++">{{ i18n.ts._initialAccountSetting.profileSetting }} <i class="ti ti-arrow-right"></i></MkButton> <MkButton style="margin: 0 auto;" transparent rounded @click="later(true)">{{ i18n.ts.later }}</MkButton> </div> - </MkSpacer> + </div> </div> </template> <template v-else-if="page === 1"> <div style="height: 100cqh; overflow: auto;"> <div :class="$style.pageRoot"> - <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain"> <XProfile/> - </MkSpacer> + </div> <div :class="$style.pageFooter"> <div class="_buttonsCenter"> <MkButton rounded data-cy-user-setup-back @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> @@ -62,9 +62,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else-if="page === 2"> <div style="height: 100cqh; overflow: auto;"> <div :class="$style.pageRoot"> - <MkSpacer :marginMin="20" :marginMax="28" :class="$style.pageMain"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;" :class="$style.pageMain"> <XPrivacy/> - </MkSpacer> + </div> <div :class="$style.pageFooter"> <div class="_buttonsCenter"> <MkButton rounded data-cy-user-setup-back @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> @@ -76,9 +76,9 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <template v-else-if="page === 3"> <div style="height: 100cqh; overflow: auto;"> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <XFollow/> - </MkSpacer> + </div> <div :class="$style.pageFooter"> <div class="_buttonsCenter"> <MkButton rounded data-cy-user-setup-back @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> @@ -89,7 +89,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <template v-else-if="page === 4"> <div :class="$style.centerPage"> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps" style="text-align: center;"> <i class="ti ti-bell-ringing-2" style="display: block; margin: auto; font-size: 3em; color: var(--MI_THEME-accent);"></i> <div style="font-size: 120%;">{{ i18n.ts.pushNotification }}</div> @@ -100,13 +100,13 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary rounded gradate data-cy-user-setup-continue @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton> </div> </div> - </MkSpacer> + </div> </div> </template> <template v-else-if="page === 5"> <div :class="$style.centerPage"> <MkAnimBg style="position: absolute; top: 0;" :scale="1.5"/> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps" style="text-align: center;"> <i class="ti ti-check" style="display: block; margin: auto; font-size: 3em; color: var(--MI_THEME-accent);"></i> <div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.initialAccountSettingCompleted }}</div> @@ -119,7 +119,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton rounded primary data-cy-user-setup-continue @click="setupComplete()">{{ i18n.ts.close }}</MkButton> </div> </div> - </MkSpacer> + </div> </div> </template> </Transition> @@ -147,7 +147,7 @@ const emit = defineEmits<{ }>(); const dialog = useTemplateRef('dialog'); - + const page = ref(store.s.accountSetupWizard); watch(page, () => { diff --git a/packages/frontend/src/components/SkMfmWindow.vue b/packages/frontend/src/components/SkMfmWindow.vue index a628758a0f..14d309b7ba 100644 --- a/packages/frontend/src/components/SkMfmWindow.vue +++ b/packages/frontend/src/components/SkMfmWindow.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only MFM Cheatsheet </template> <MkStickyContainer> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <div class="mfm-cheat-sheet"> <div>{{ i18n.ts._mfm.intro }}</div> <br/> @@ -402,7 +402,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </div> - </MkSpacer> + </div> </MkStickyContainer> </MkWindow> </template> diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index 42bb49e8d9..c939f0b44e 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -49,19 +49,12 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> -<script lang="ts" setup> -import { onMounted, onUnmounted, ref, inject, useTemplateRef, computed } from 'vue'; -import { scrollToTop } from '@@/js/scroll.js'; -import XTabs from './MkPageHeader.tabs.vue'; -import type { Tab } from './MkPageHeader.tabs.vue'; +<script lang="ts"> import type { PageHeaderItem } from '@/types/page-header.js'; import type { PageMetadata } from '@/page.js'; -import { globalEvents } from '@/events.js'; -import { openAccountMenu as openAccountMenu_ } from '@/accounts.js'; -import { $i } from '@/i.js'; -import { DI } from '@/di.js'; +import type { Tab } from './MkPageHeader.tabs.vue'; -const props = withDefaults(defineProps<{ +export type PageHeaderProps = { overridePageMetadata?: PageMetadata; tabs?: Tab[]; tab?: string; @@ -70,7 +63,19 @@ const props = withDefaults(defineProps<{ hideTitle?: boolean; displayMyAvatar?: boolean; displayBackButton?: boolean; -}>(), { +}; +</script> + +<script lang="ts" setup> +import { onMounted, onUnmounted, ref, inject, useTemplateRef, computed } from 'vue'; +import { scrollToTop } from '@@/js/scroll.js'; +import XTabs from './MkPageHeader.tabs.vue'; +import { globalEvents } from '@/events.js'; +import { openAccountMenu as openAccountMenu_ } from '@/accounts.js'; +import { $i } from '@/i.js'; +import { DI } from '@/di.js'; + +const props = withDefaults(defineProps<PageHeaderProps>(), { tabs: () => ([] as Tab[]), }); diff --git a/packages/frontend/src/components/global/MkSpacer.vue b/packages/frontend/src/components/global/MkSpacer.vue deleted file mode 100644 index c3bc37cb92..0000000000 --- a/packages/frontend/src/components/global/MkSpacer.vue +++ /dev/null @@ -1,58 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and misskey-project -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<div :class="[$style.root, { [$style.rootMin]: forceSpacerMin }]"> - <div :class="$style.content"> - <slot></slot> - </div> -</div> -</template> - -<script lang="ts" setup> -import { inject } from 'vue'; -import { deviceKind } from '@/utility/device-kind.js'; -import { DI } from '@/di.js'; - -const props = withDefaults(defineProps<{ - contentMax?: number | null; - marginMin?: number; - marginMax?: number; -}>(), { - contentMax: null, - marginMin: 12, - marginMax: 24, -}); - -const forceSpacerMin = inject(DI.forceSpacerMin, false) || deviceKind === 'smartphone'; -</script> - -<style lang="scss" module> -.root { - box-sizing: border-box; - width: 100%; -} -.rootMin { - padding: v-bind('props.marginMin + "px"') !important; -} - -.content { - margin: 0 auto; - max-width: v-bind('props.contentMax + "px"'); - container-type: inline-size; -} - -@container (max-width: 450px) { - .root { - padding: v-bind('props.marginMin + "px"'); - } -} - -@container (min-width: 451px) { - .root { - padding: v-bind('props.marginMax + "px"'); - } -} -</style> diff --git a/packages/frontend/src/components/global/PageWithHeader.vue b/packages/frontend/src/components/global/PageWithHeader.vue index 85e61fd532..58c222038a 100644 --- a/packages/frontend/src/components/global/PageWithHeader.vue +++ b/packages/frontend/src/components/global/PageWithHeader.vue @@ -6,9 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div ref="rootEl" :class="[$style.root, reversed ? '_pageScrollableReversed' : '_pageScrollable']"> <MkStickyContainer> - <template #header><MkPageHeader v-model:tab="tab" :actions="actions" :tabs="tabs" :displayBackButton="displayBackButton"/></template> + <template #header><MkPageHeader v-model:tab="tab" v-bind="pageHeaderProps"/></template> <div :class="$style.body"> - <slot></slot> + <MkSwiper v-if="swipable && (props.tabs?.length ?? 1) > 1" v-model:tab="tab" :class="$style.swiper" :tabs="props.tabs"> + <slot></slot> + </MkSwiper> + <slot v-else></slot> </div> <template #footer><slot name="footer"></slot></template> </MkStickyContainer> @@ -16,22 +19,24 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { useTemplateRef } from 'vue'; +import { computed, useTemplateRef } from 'vue'; import { scrollInContainer } from '@@/js/scroll.js'; -import type { PageHeaderItem } from '@/types/page-header.js'; -import type { Tab } from './MkPageHeader.tabs.vue'; +import type { PageHeaderProps } from './MkPageHeader.vue'; import { useScrollPositionKeeper } from '@/use/use-scroll-position-keeper.js'; +import MkSwiper from '@/components/MkSwiper.vue'; +import { useRouter } from '@/router.js'; -const props = withDefaults(defineProps<{ - tabs?: Tab[]; - actions?: PageHeaderItem[] | null; - thin?: boolean; - hideTitle?: boolean; - displayMyAvatar?: boolean; +const props = withDefaults(defineProps<PageHeaderProps & { reversed?: boolean; - displayBackButton?: boolean; + swipable?: boolean; }>(), { - tabs: () => ([] as Tab[]), + reversed: false, + swipable: true, +}); + +const pageHeaderProps = computed(() => { + const { reversed, ...rest } = props; + return rest; }); const tab = defineModel<string>('tab'); @@ -39,10 +44,18 @@ const rootEl = useTemplateRef('rootEl'); useScrollPositionKeeper(rootEl); +const router = useRouter(); + +router.useListener('same', () => { + scrollToTop(); +}); + +function scrollToTop() { + if (rootEl.value) scrollInContainer(rootEl.value, { top: 0, behavior: 'smooth' }); +} + defineExpose({ - scrollToTop: () => { - if (rootEl.value) scrollInContainer(rootEl.value, { top: 0, behavior: 'smooth' }); - }, + scrollToTop, }); </script> @@ -51,7 +64,7 @@ defineExpose({ } -.body { +.body, .swiper { min-height: calc(100cqh - (var(--MI-stickyTop, 0px) + var(--MI-stickyBottom, 0px))); } </style> diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue index 78ac6900a3..27f7b18559 100644 --- a/packages/frontend/src/components/global/RouterView.vue +++ b/packages/frontend/src/components/global/RouterView.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div ref="rootEl" class="_pageContainer" :class="$style.root"> +<div class="_pageContainer" :class="$style.root"> <KeepAlive :max="prefer.s.numberOfPageCache"> <Suspense :timeout="0"> <component :is="currentPageComponent" :key="key" v-bind="Object.fromEntries(currentPageProps)"/> @@ -42,37 +42,6 @@ provide(DI.viewId, viewId); const currentDepth = inject(DI.routerCurrentDepth, 0); provide(DI.routerCurrentDepth, currentDepth + 1); -const rootEl = useTemplateRef('rootEl'); -onMounted(() => { - if (prefer.s.animation) { - rootEl.value.style.viewTransitionName = viewId; // view-transition-nameにcss varが使えないっぽいため直接代入 - } -}); - -// view-transition-newなどの<pt-name-selector>にはcss varが使えず、v-bindできないため直接スタイルを生成 -const viewTransitionStylesTag = window.document.createElement('style'); -viewTransitionStylesTag.textContent = ` -@keyframes ${viewId}-old { - to { transform: scale(0.95); opacity: 0; } -} - -@keyframes ${viewId}-new { - from { transform: scale(0.95); opacity: 0; } -} - -::view-transition-old(${viewId}) { - animation-duration: 0.2s; - animation-name: ${viewId}-old; -} - -::view-transition-new(${viewId}) { - animation-duration: 0.2s; - animation-name: ${viewId}-new; -} -`; - -window.document.head.appendChild(viewTransitionStylesTag); - const current = router.current!; const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage); const currentPageProps = ref(current.props); @@ -90,18 +59,7 @@ router.useListener('change', ({ resolved }) => { currentRoutePath = resolved.route.path; } - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (prefer.s.animation && window.document.startViewTransition) { - window.document.startViewTransition(() => new Promise((res) => { - _(); - nextTick(() => { - res(); - //setTimeout(res, 100); - }); - })); - } else { - _(); - } + _(); }); </script> diff --git a/packages/frontend/src/components/grid/MkGrid.vue b/packages/frontend/src/components/grid/MkGrid.vue index c37f3df0d3..f80f037285 100644 --- a/packages/frontend/src/components/grid/MkGrid.vue +++ b/packages/frontend/src/components/grid/MkGrid.vue @@ -38,7 +38,6 @@ SPDX-License-Identifier: AGPL-3.0-only :setting="rowSetting" :bus="bus" :using="row.using" - :class="[lastLine === row.index ? 'last_row' : '']" @operation:beginEdit="onCellEditBegin" @operation:endEdit="onCellEditEnd" @change:value="onChangeCellValue" @@ -1301,8 +1300,6 @@ onMounted(() => { </style> <style lang="scss"> -$borderSetting: solid 0.5px var(--MI_THEME-divider); - // 配下コンポーネントを含めて一括してコントロールするため、scopedもmoduleも使用できない .mk_grid_border { --rootBorderSetting: none; @@ -1310,66 +1307,39 @@ $borderSetting: solid 0.5px var(--MI_THEME-divider); border-spacing: 0; - &.mk_grid_root_border { - --rootBorderSetting: #{$borderSetting}; - } - &.mk_grid_root_rounded { --borderRadius: var(--MI-radius); } .mk_grid_thead { + position: sticky; + z-index: 1; + left: 0; + top: 0; + -webkit-backdrop-filter: var(--MI-blur, blur(8px)); + backdrop-filter: var(--MI-blur, blur(20px)); + background: color(from var(--MI_THEME-bg) srgb r g b / 0.5); + .mk_grid_tr { .mk_grid_th { - border-left: $borderSetting; - border-top: var(--rootBorderSetting); - &:first-child { - // 左上セル - border-left: var(--rootBorderSetting); - border-top-left-radius: var(--borderRadius); - } - - &:last-child { - // 右上セル - border-top-right-radius: var(--borderRadius); - border-right: var(--rootBorderSetting); - } } } } .mk_grid_tbody { .mk_grid_tr { - .mk_grid_td, .mk_grid_th { - border-left: $borderSetting; - border-top: $borderSetting; - - &:first-child { - // 左端の列 - border-left: var(--rootBorderSetting); - } + &:nth-child(odd) { + background: var(--MI_THEME-panel); + } - &:last-child { - // 一番右端の列 - border-right: var(--rootBorderSetting); - } + &:nth-child(even) { + background: var(--MI_THEME-bg); } - } - .last_row { .mk_grid_td, .mk_grid_th { - // 一番下の行 - border-bottom: var(--rootBorderSetting); - - &:first-child { - // 左下セル - border-bottom-left-radius: var(--borderRadius); - } - - &:last-child { - // 右下セル - border-bottom-right-radius: var(--borderRadius); + &:hover { + box-shadow: 0 0 0 1px var(--MI_THEME-divider) inset; } } } diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index 34cf598b84..ec6ea7c569 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -22,7 +22,6 @@ import MkLoading from './global/MkLoading.vue'; import MkError from './global/MkError.vue'; import MkAd from './global/MkAd.vue'; import MkPageHeader from './global/MkPageHeader.vue'; -import MkSpacer from './global/MkSpacer.vue'; import MkStickyContainer from './global/MkStickyContainer.vue'; import MkLazy from './global/MkLazy.vue'; import PageWithHeader from './global/PageWithHeader.vue'; @@ -60,7 +59,6 @@ export const components = { MkError: MkError, MkAd: MkAd, MkPageHeader: MkPageHeader, - MkSpacer: MkSpacer, MkStickyContainer: MkStickyContainer, MkLazy: MkLazy, PageWithHeader: PageWithHeader, @@ -92,7 +90,6 @@ declare module '@vue/runtime-core' { MkError: typeof MkError; MkAd: typeof MkAd; MkPageHeader: typeof MkPageHeader; - MkSpacer: typeof MkSpacer; MkStickyContainer: typeof MkStickyContainer; MkLazy: typeof MkLazy; PageWithHeader: typeof PageWithHeader; diff --git a/packages/frontend/src/di.ts b/packages/frontend/src/di.ts index 58a2cce207..e2590da60b 100644 --- a/packages/frontend/src/di.ts +++ b/packages/frontend/src/di.ts @@ -17,5 +17,4 @@ export const DI = { mfmEmojiReactCallback: Symbol() as InjectionKey<(emoji: string) => void>, inModal: Symbol() as InjectionKey<boolean>, inAppSearchMarkerId: Symbol() as InjectionKey<Ref<string | null>>, - forceSpacerMin: Symbol() as InjectionKey<boolean>, }; diff --git a/packages/frontend/src/pages/about-sharkey.vue b/packages/frontend/src/pages/about-sharkey.vue index 833aed2c89..94e899e408 100644 --- a/packages/frontend/src/pages/about-sharkey.vue +++ b/packages/frontend/src/pages/about-sharkey.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <div style="overflow: clip;"> - <MkSpacer :contentMax="600" :marginMin="20"> + <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 20px;"> <div class="_gaps_m znqjceqz"> <div v-panel class="about"> <div ref="containerEl" class="container" :class="{ playing: easterEggEngine != null }"> @@ -85,7 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-if="section.link" #description><MkLink :url="section.link.url">{{ section.link.label }}</MkLink></template> </FormSection> </div> - </MkSpacer> + </div> </div> </MkStickyContainer> </template> diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index d955857e20..6b7b650a75 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -4,21 +4,19 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <MkSpacer v-if="tab === 'overview'" :contentMax="600" :marginMin="20"> - <XOverview/> - </MkSpacer> - <MkSpacer v-else-if="tab === 'emojis'" :contentMax="1000" :marginMin="20"> - <XEmojis/> - </MkSpacer> - <MkSpacer v-else-if="instance.federation !== 'none' && tab === 'federation'" :contentMax="1000" :marginMin="20"> - <XFederation/> - </MkSpacer> - <MkSpacer v-else-if="tab === 'charts'" :contentMax="1000" :marginMin="20"> - <MkInstanceStats/> - </MkSpacer> - </MkSwiper> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div v-if="tab === 'overview'" class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 20px;"> + <XOverview/> + </div> + <div v-else-if="tab === 'emojis'" class="_spacer" style="--MI_SPACER-w: 1000px; --MI_SPACER-min: 20px;"> + <XEmojis/> + </div> + <div v-else-if="instance.federation !== 'none' && tab === 'federation'" class="_spacer" style="--MI_SPACER-w: 1000px; --MI_SPACER-min: 20px;"> + <XFederation/> + </div> + <div v-else-if="tab === 'charts'" class="_spacer" style="--MI_SPACER-w: 1000px; --MI_SPACER-min: 20px;"> + <MkInstanceStats/> + </div> </PageWithHeader> </template> @@ -28,7 +26,6 @@ import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { claimAchievement } from '@/utility/achievements.js'; import { definePage } from '@/page.js'; -import MkSwiper from '@/components/MkSwiper.vue'; const XOverview = defineAsyncComponent(() => import('@/pages/about.overview.vue')); const XEmojis = defineAsyncComponent(() => import('@/pages/about.emojis.vue')); diff --git a/packages/frontend/src/pages/achievements.vue b/packages/frontend/src/pages/achievements.vue index 423e709da4..1560403b70 100644 --- a/packages/frontend/src/pages/achievements.vue +++ b/packages/frontend/src/pages/achievements.vue @@ -5,9 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader> - <MkSpacer :contentMax="1200"> + <div class="_spacer" style="--MI_SPACER-w: 1200px;"> <MkAchievements :user="$i"/> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue index 1e3e106842..8495642a8c 100644 --- a/packages/frontend/src/pages/admin-file.vue +++ b/packages/frontend/src/pages/admin-file.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer v-if="file" :contentMax="600" :marginMin="16" :marginMax="32"> + <div v-if="file" class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <div v-if="tab === 'overview'" class="cxqhhsmd _gaps_m"> <a class="thumbnail" :href="file.url" target="_blank"> <MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain"/> @@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkObjectView v-if="info" tall :value="info"> </MkObjectView> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index 06d557f045..efe547ff21 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="600" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <FormSuspense :p="init"> <div v-if="tab === 'overview'" class="_gaps_m"> <div class="aeakzknw"> @@ -201,7 +201,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkObjectView> </div> </FormSuspense> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue index 10925fa4ab..b69c818b48 100644 --- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue +++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ mode === 'create' ? i18n.ts._abuseReport._notificationRecipient.createRecipient : i18n.ts._abuseReport._notificationRecipient.modifyRecipient }} </template> <div v-if="loading === 0" style="display: flex; flex-direction: column; min-height: 100%;"> - <MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px; flex-grow: 1;"> <div :class="$style.root" class="_gaps_m"> <MkInput v-model="title"> <template #label>{{ i18n.ts.title }}</template> @@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> </div> - </MkSpacer> + </div> <div :class="$style.footer" class="_buttonsCenter"> <MkButton primary rounded :disabled="disableSubmitButton" @click="onSubmitClicked"><i class="ti ti-check"></i> {{ i18n.ts.ok }}</MkButton> diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue index a569ab7c33..f5e77cbe4e 100644 --- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue +++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <div :class="$style.root" class="_gaps_m"> <div :class="$style.addButton"> <MkButton primary @click="onAddButtonClicked"> @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only /> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue index 3f1df95f25..4ec4372492 100644 --- a/packages/frontend/src/pages/admin/abuses.vue +++ b/packages/frontend/src/pages/admin/abuses.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <div :class="$style.root" class="_gaps"> <div :class="$style.subMenus" class="_gaps"> <MkButton link to="/admin/abuse-report-notification-recipient" primary>{{ i18n.ts.notificationSetting }}</MkButton> @@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkPagination> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index aa8ba2f7c3..c5baeda7b0 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <MkSelect v-model="filterType" :class="$style.input" @update:modelValue="filterItems"> <template #label>{{ i18n.ts.state }}</template> <option value="all">{{ i18n.ts.all }}</option> @@ -77,7 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i class="ti ti-reload"></i>{{ i18n.ts.more }} </MkButton> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue index ea7f0cc73d..b2d7b4889a 100644 --- a/packages/frontend/src/pages/admin/announcements.vue +++ b/packages/frontend/src/pages/admin/announcements.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <div class="_gaps"> <MkInfo>{{ i18n.ts._announcement.shouldNotBeUsedToPresentPermanentInfo }}</MkInfo> <MkInfo v-if="announcements.length > 5" warn>{{ i18n.ts._announcement.tooManyActiveAnnouncementDescription }}</MkInfo> @@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkButton> </template> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/approvals.vue b/packages/frontend/src/pages/admin/approvals.vue index c972728c85..77b4cc38a1 100644 --- a/packages/frontend/src/pages/admin/approvals.vue +++ b/packages/frontend/src/pages/admin/approvals.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div> <PageWithHeader :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <div class="_gaps_m"> <MkPagination ref="paginationComponent" :pagination="pagination" :displayLimit="50"> <template #default="{ items }"> @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </MkPagination> </div> - </MkSpacer> + </div> </PageWithHeader> </div> </template> diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index 1afc643a0a..64faf49848 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <FormSuspense :p="init"> <div class="_gaps_m"> <MkInput v-model="iconUrl" type="url"> @@ -105,12 +105,12 @@ SPDX-License-Identifier: AGPL-3.0-only </MkTextarea> </div> </FormSuspense> - </MkSpacer> + </div> <template #footer> <div :class="$style.footer"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;"> <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> - </MkSpacer> + </div> </div> </template> </PageWithHeader> diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.logs.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.logs.vue index 4b145db0ed..c544561b13 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.logs.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.logs.vue @@ -14,9 +14,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header> <i class="ti ti-notes" style="margin-right: 0.5em;"></i> {{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }} </template> - <MkSpacer> + <div class="_spacer"> <XRegisterLogs :logs="logs"/> - </MkSpacer> + </div> </MkWindow> </template> diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue index ae43507d66..9938d5cc4a 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.search.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i class="ti ti-search" style="margin-right: 0.5em;"></i> {{ i18n.ts.search }} </template> <div :class="$style.root"> - <MkSpacer> + <div class="_spacer"> <div class="_gaps"> <div class="_gaps_s"> <MkInput @@ -107,7 +107,7 @@ SPDX-License-Identifier: AGPL-3.0-only /> </MkFolder> </div> - </MkSpacer> + </div> <div :class="$style.footerActions"> <MkButton primary @click="onSearchRequest"> {{ i18n.ts.search }} diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue index 260177c894..59b780bff6 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue @@ -285,7 +285,7 @@ const searchQuery = ref<EmojiSearchQuery>({ localOnly: null, roles: [], sortOrders: [], - limit: 25, + limit: 100, }); let searchWindowOpening = false; diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.vue deleted file mode 100644 index 6e7e7e53e3..0000000000 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.vue +++ /dev/null @@ -1,35 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<MkStickyContainer> - <template #header> - <MkPageHeader v-model:tab="headerTab" :tabs="headerTabs" hideTitle thin/> - </template> - <XListComponent v-if="headerTab === 'list'" key="localList"/> - <MkSpacer v-else key="localRegister"> - <XRegisterComponent/> - </MkSpacer> -</MkStickyContainer> -</template> - -<script setup lang="ts"> -import { ref, computed } from 'vue'; -import { i18n } from '@/i18n.js'; -import XListComponent from '@/pages/admin/custom-emojis-manager.local.list.vue'; -import XRegisterComponent from '@/pages/admin/custom-emojis-manager.local.register.vue'; - -type PageMode = 'list' | 'register'; - -const headerTab = ref<PageMode>('list'); - -const headerTabs = computed(() => [{ - key: 'list', - title: i18n.ts._customEmojisManager._local.tabTitleList, -}, { - key: 'register', - title: i18n.ts._customEmojisManager._local.tabTitleRegister, -}]); -</script> diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.register.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue index eff7efd0fa..e8e944df32 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.register.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue @@ -4,67 +4,69 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps"> - <MkFolder> - <template #icon><i class="ti ti-settings"></i></template> - <template #label>{{ i18n.ts._customEmojisManager._local._register.uploadSettingTitle }}</template> - <template #caption>{{ i18n.ts._customEmojisManager._local._register.uploadSettingDescription }}</template> +<div class="_spacer"> + <div class="_gaps"> + <MkFolder> + <template #icon><i class="ti ti-settings"></i></template> + <template #label>{{ i18n.ts._customEmojisManager._local._register.uploadSettingTitle }}</template> + <template #caption>{{ i18n.ts._customEmojisManager._local._register.uploadSettingDescription }}</template> - <div class="_gaps"> - <MkSelect v-model="selectedFolderId"> - <template #label>{{ i18n.ts.uploadFolder }}</template> - <option v-for="folder in uploadFolders" :key="folder.id" :value="folder.id"> - {{ folder.name }} - </option> - </MkSelect> + <div class="_gaps"> + <MkSelect v-model="selectedFolderId"> + <template #label>{{ i18n.ts.uploadFolder }}</template> + <option v-for="folder in uploadFolders" :key="folder.id" :value="folder.id"> + {{ folder.name }} + </option> + </MkSelect> - <MkSwitch v-model="directoryToCategory"> - <template #label>{{ i18n.ts._customEmojisManager._local._register.directoryToCategoryLabel }}</template> - <template #caption>{{ i18n.ts._customEmojisManager._local._register.directoryToCategoryCaption }}</template> - </MkSwitch> - </div> - </MkFolder> + <MkSwitch v-model="directoryToCategory"> + <template #label>{{ i18n.ts._customEmojisManager._local._register.directoryToCategoryLabel }}</template> + <template #caption>{{ i18n.ts._customEmojisManager._local._register.directoryToCategoryCaption }}</template> + </MkSwitch> + </div> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-notes"></i></template> - <template #label>{{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}</template> - <template #caption> - {{ i18n.ts._customEmojisManager._gridCommon.registrationLogsCaption }} - </template> - <XRegisterLogs :logs="requestLogs"/> - </MkFolder> + <MkFolder> + <template #icon><i class="ti ti-notes"></i></template> + <template #label>{{ i18n.ts._customEmojisManager._gridCommon.registrationLogs }}</template> + <template #caption> + {{ i18n.ts._customEmojisManager._gridCommon.registrationLogsCaption }} + </template> + <XRegisterLogs :logs="requestLogs"/> + </MkFolder> - <div - :class="[$style.uploadBox, [isDragOver ? $style.dragOver : {}]]" - @dragover.prevent="isDragOver = true" - @dragleave.prevent="isDragOver = false" - @drop.prevent.stop="onDrop" - > - <div style="margin-top: 1em"> - {{ i18n.ts._customEmojisManager._local._register.emojiInputAreaCaption }} + <div + :class="[$style.uploadBox, [isDragOver ? $style.dragOver : {}]]" + @dragover.prevent="isDragOver = true" + @dragleave.prevent="isDragOver = false" + @drop.prevent.stop="onDrop" + > + <div style="margin-top: 1em"> + {{ i18n.ts._customEmojisManager._local._register.emojiInputAreaCaption }} + </div> + <ul> + <li>{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList1 }}</li> + <li><a @click.prevent="onFileSelectClicked">{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList2 }}</a></li> + <li><a @click.prevent="onDriveSelectClicked">{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList3 }}</a></li> + </ul> </div> - <ul> - <li>{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList1 }}</li> - <li><a @click.prevent="onFileSelectClicked">{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList2 }}</a></li> - <li><a @click.prevent="onDriveSelectClicked">{{ i18n.ts._customEmojisManager._local._register.emojiInputAreaList3 }}</a></li> - </ul> - </div> - <div v-if="gridItems.length > 0" :class="$style.gridArea"> - <MkGrid - :data="gridItems" - :settings="setupGrid()" - @event="onGridEvent" - /> - </div> + <div v-if="gridItems.length > 0" :class="$style.gridArea"> + <MkGrid + :data="gridItems" + :settings="setupGrid()" + @event="onGridEvent" + /> + </div> - <div v-if="gridItems.length > 0" :class="$style.footer"> - <MkButton primary :disabled="registerButtonDisabled" @click="onRegistryClicked"> - {{ i18n.ts.registration }} - </MkButton> - <MkButton @click="onClearClicked"> - {{ i18n.ts.clear }} - </MkButton> + <div v-if="gridItems.length > 0" :class="$style.footer"> + <MkButton primary :disabled="registerButtonDisabled" @click="onRegistryClicked"> + {{ i18n.ts.registration }} + </MkButton> + <MkButton @click="onClearClicked"> + {{ i18n.ts.clear }} + </MkButton> + </div> </div> </div> </template> @@ -407,7 +409,7 @@ function fromDriveFile(it: Misskey.entities.DriveFile): GridItem { return { fileId: it.id, url: it.url, - name: it.name.replace(/(\.[a-zA-Z0-9]+)+$/, ''), + name: it.name.replace(/(\.[a-zA-Z0-9]+)+$/, '').replaceAll('-', '_').replaceAll(' ', '_'), host: '', category: '', aliases: '', diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue index c868a700f1..2fd7e331a2 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue @@ -142,6 +142,10 @@ SPDX-License-Identifier: AGPL-3.0-only <script setup lang="ts"> import { computed, onMounted, ref, useCssModule } from 'vue'; import * as Misskey from 'misskey-js'; +import type { GridSortOrderKey, RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js'; +import type { GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js'; +import type { GridSetting } from '@/components/grid/grid.js'; +import type { SortOrder } from '@/components/MkSortOrderEditor.define.js'; import MkRemoteEmojiEditDialog from '@/components/MkRemoteEmojiEditDialog.vue'; import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; @@ -157,11 +161,6 @@ import MkPagingButtons from '@/components/MkPagingButtons.vue'; import MkSortOrderEditor from '@/components/MkSortOrderEditor.vue'; import { useLoading } from '@/components/hook/useLoading.js'; -import type { GridSortOrderKey, RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js'; -import type { GridCellValueChangeEvent, GridEvent } from '@/components/grid/grid-event.js'; -import type { GridSetting } from '@/components/grid/grid.js'; -import type { SortOrder } from '@/components/MkSortOrderEditor.define.js'; - type GridItem = { checked: boolean; id: string; @@ -260,7 +259,7 @@ const queryHost = ref<string | null>(null); const queryLicense = ref<string | null>(null); const queryUri = ref<string | null>(null); const queryPublicUrl = ref<string | null>(null); -const queryLimit = ref<number>(25); +const queryLimit = ref<number>(100); const previousQuery = ref<string | undefined>(undefined); const sortOrders = ref<SortOrder<GridSortOrderKey>[]>([]); const requestLogs = ref<RequestLogItem[]>([]); diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager2.vue b/packages/frontend/src/pages/admin/custom-emojis-manager2.vue index 7667206fa8..14773d7f04 100644 --- a/packages/frontend/src/pages/admin/custom-emojis-manager2.vue +++ b/packages/frontend/src/pages/admin/custom-emojis-manager2.vue @@ -4,25 +4,20 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header> - <MkPageHeader v-model:tab="headerTab" :tabs="headerTabs"/> - </template> - <XGridLocalComponent v-if="headerTab === 'local'" :class="$style.local"/> - <XGridRemoteComponent v-else/> - </MkStickyContainer> -</div> +<PageWithHeader v-model:tab="headerTab" :tabs="headerTabs"> + <XGridLocalComponent v-if="headerTab === 'local'" :class="$style.local"/> + <XGridRemoteComponent v-else-if="headerTab === 'remote'" :class="$style.remote"/> + <XRegisterComponent v-else-if="headerTab === 'register'" :class="$style.register"/> +</PageWithHeader> </template> <script setup lang="ts"> import { computed, ref } from 'vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; -import XGridLocalComponent from '@/pages/admin/custom-emojis-manager.local.vue'; +import XGridLocalComponent from '@/pages/admin/custom-emojis-manager.local.list.vue'; import XGridRemoteComponent from '@/pages/admin/custom-emojis-manager.remote.vue'; -import MkPageHeader from '@/components/global/MkPageHeader.vue'; -import MkStickyContainer from '@/components/global/MkStickyContainer.vue'; +import XRegisterComponent from '@/pages/admin/custom-emojis-manager.register.vue'; type PageMode = 'local' | 'remote'; @@ -34,6 +29,9 @@ const headerTabs = computed(() => [{ }, { key: 'remote', title: i18n.ts.remote, +}, { + key: 'register', + title: i18n.ts._customEmojisManager._local.tabTitleRegister, }]); definePage(computed(() => ({ diff --git a/packages/frontend/src/pages/admin/database.vue b/packages/frontend/src/pages/admin/database.vue index 6691142a64..d51f43c098 100644 --- a/packages/frontend/src/pages/admin/database.vue +++ b/packages/frontend/src/pages/admin/database.vue @@ -5,14 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 800px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <FormSuspense v-slot="{ result: database }" :p="databasePromiseFactory"> <MkKeyValue v-for="table in database" :key="table[0]" oneline style="margin: 1em 0;"> <template #key>{{ table[0] }}</template> <template #value>{{ bytes(table[1].size) }} ({{ number(table[1].count) }} recs)</template> </MkKeyValue> </FormSuspense> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue index f1827d756b..cdbca12435 100644 --- a/packages/frontend/src/pages/admin/email-settings.vue +++ b/packages/frontend/src/pages/admin/email-settings.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <FormSuspense :p="init"> <div class="_gaps_m"> <MkSwitch v-model="enableEmail"> @@ -48,15 +48,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </div> </FormSuspense> - </MkSpacer> + </div> <template #footer> <div :class="$style.footer"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;"> <div class="_buttons"> <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> <MkButton rounded @click="testEmail"><i class="ti ti-send"></i> {{ i18n.ts.testEmail }}</MkButton> </div> - </MkSpacer> + </div> </div> </template> </PageWithHeader> diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue index 131989844a..3cfc51af00 100644 --- a/packages/frontend/src/pages/admin/external-services.vue +++ b/packages/frontend/src/pages/admin/external-services.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <FormSuspense :p="init"> <div class="_gaps_m"> <MkFolder> @@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> </div> </FormSuspense> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/federation-job-queue.vue b/packages/frontend/src/pages/admin/federation-job-queue.vue index 77e460d0eb..173cffedc2 100644 --- a/packages/frontend/src/pages/admin/federation-job-queue.vue +++ b/packages/frontend/src/pages/admin/federation-job-queue.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <XQueue v-if="tab === 'deliver'" domain="deliver"/> <XQueue v-else-if="tab === 'inbox'" domain="inbox"/> <br> @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton @click="promoteAllQueues"><i class="ti ti-reload"></i> {{ i18n.ts.retryAllQueuesNow }}</MkButton> <MkButton danger @click="clear"><i class="ti ti-trash"></i> {{ i18n.ts.clearQueue }}</MkButton> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue index dcc8c52e55..16bed50002 100644 --- a/packages/frontend/src/pages/admin/federation.vue +++ b/packages/frontend/src/pages/admin/federation.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <div class="_gaps"> <div> <MkInput v-model="host" :debounce="true" class=""> @@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkPagination> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue index 12c633bf7f..87595a820b 100644 --- a/packages/frontend/src/pages/admin/files.vue +++ b/packages/frontend/src/pages/admin/files.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <div class="_gaps"> <div class="inputs" style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> <MkSelect v-model="origin" style="margin: 0; flex: 1;"> @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <MkFileListForAdmin :pagination="pagination" :viewMode="viewMode"/> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index c366a7cd6a..0cb9d54bbe 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div ref="el" class="hiyeyicy" :class="{ wide: !narrow }"> <div v-if="!narrow || currentPage?.route.name == null" class="nav"> - <MkSpacer :contentMax="700" :marginMin="16"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px;"> <div class="lxpfedzu _gaps"> <div class="banner"> <img :src="instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu> </div> - </MkSpacer> + </div> </div> <div v-if="!(narrow && currentPage?.route.name == null)" class="main _pageContainer" style="height: 100%;"> <NestedRouterView/> diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue index 7f0c35d6bc..83b040935b 100644 --- a/packages/frontend/src/pages/admin/invites.vue +++ b/packages/frontend/src/pages/admin/invites.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <div class="_gaps_m"> <MkFolder :expanded="false"> <template #icon><i class="ti ti-plus"></i></template> @@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </MkPagination> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/job-queue.vue b/packages/frontend/src/pages/admin/job-queue.vue index 528c473c4f..3d405c566f 100644 --- a/packages/frontend/src/pages/admin/job-queue.vue +++ b/packages/frontend/src/pages/admin/job-queue.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer> + <div class="_spacer"> <div v-if="tab === '-'" class="_gaps"> <div :class="$style.queues"> <div v-for="q in queueInfos" :key="q.name" :class="$style.queue" @click="tab = q.name"> @@ -139,7 +139,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> - <MkSpacer> + <div class="_spacer"> <MkInput v-model="searchQuery" :placeholder="i18n.ts.search" @@ -163,10 +163,10 @@ SPDX-License-Identifier: AGPL-3.0-only <XJob :job="job" :queueType="tab" style="margin: 4px 0;" @needRefresh="refreshJob(job.id)"/> </template> </MkTl> - </MkSpacer> + </div> </MkFolder> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index 5491a2df45..9675bdc21a 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <FormSuspense :p="init"> <div class="_gaps_m"> <MkSwitch :modelValue="enableRegistration" @update:modelValue="onChange_enableRegistration"> @@ -148,7 +148,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> </div> </FormSuspense> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue index 2d43e3b790..cf7d46153f 100644 --- a/packages/frontend/src/pages/admin/modlog.vue +++ b/packages/frontend/src/pages/admin/modlog.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <div class="_gaps"> <div style="display: flex; gap: var(--MI-margin); flex-wrap: wrap;"> <MkSelect v-model="type" style="margin: 0; flex: 1;"> @@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary rounded style="margin: 0 auto;" @click="fetchMore">{{ i18n.ts.loadMore }}</MkButton> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue index 36f4392142..7a46ae41c6 100644 --- a/packages/frontend/src/pages/admin/object-storage.vue +++ b/packages/frontend/src/pages/admin/object-storage.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <FormSuspense :p="init"> <div class="_gaps_m"> <MkSwitch v-model="useObjectStorage">{{ i18n.ts.useObjectStorage }}</MkSwitch> @@ -70,12 +70,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </div> </FormSuspense> - </MkSpacer> + </div> <template #footer> <div :class="$style.footer"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;"> <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> - </MkSpacer> + </div> </div> </template> </PageWithHeader> diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue index 616815a6a6..caa888b51d 100644 --- a/packages/frontend/src/pages/admin/overview.vue +++ b/packages/frontend/src/pages/admin/overview.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="1000"> +<div class="_spacer" style="--MI_SPACER-w: 1000px;"> <div ref="rootEl" :class="$style.root"> <MkFoldableSection class="item"> <template #header>Stats</template> @@ -61,7 +61,7 @@ SPDX-License-Identifier: AGPL-3.0-only <XQueue domain="inbox"/> </MkFoldableSection> </div> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/admin/performance.vue b/packages/frontend/src/pages/admin/performance.vue index 075db4ebef..a272b9adea 100644 --- a/packages/frontend/src/pages/admin/performance.vue +++ b/packages/frontend/src/pages/admin/performance.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <div class="_gaps"> <div class="_panel" style="padding: 16px;"> <MkSwitch v-model="enableServerMachineStats" @change="onChange_enableServerMachineStats"> @@ -102,7 +102,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue index 7803edc360..aabf64342e 100644 --- a/packages/frontend/src/pages/admin/relays.vue +++ b/packages/frontend/src/pages/admin/relays.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <div class="_gaps"> <div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;"> <div>{{ relay.inbox }}</div> @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue index 62777f59ef..7790fe3925 100644 --- a/packages/frontend/src/pages/admin/roles.edit.vue +++ b/packages/frontend/src/pages/admin/roles.edit.vue @@ -5,14 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> - <MkSpacer :contentMax="600" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <XEditor v-if="data" v-model="data"/> - </MkSpacer> + </div> <template #footer> <div :class="$style.footer"> - <MkSpacer :contentMax="600" :marginMin="16" :marginMax="16"> + <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;"> <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> - </MkSpacer> + </div> </div> </template> </PageWithHeader> diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index c49a1bf286..bca619c2e1 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -445,6 +445,26 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.maxFileSize, 'maxFileSizeMb'])"> + <template #label>{{ i18n.ts._role._options.maxFileSize }}</template> + <template #suffix> + <span v-if="role.policies.maxFileSizeMb.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.maxFileSizeMb.value + 'MB' }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.maxFileSizeMb)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.maxFileSizeMb.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkInput v-model="role.policies.maxFileSizeMb.value" :disabled="role.policies.maxFileSizeMb.useDefault" type="number" :readonly="readonly"> + <template #suffix>MB</template> + </MkInput> + <MkRange v-model="role.policies.maxFileSizeMb.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])"> <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template> <template #suffix> diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue index 992f47bff5..04e6405f9b 100644 --- a/packages/frontend/src/pages/admin/roles.role.vue +++ b/packages/frontend/src/pages/admin/roles.role.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div class="_gaps"> <div class="_buttons"> <MkButton primary rounded @click="edit"><i class="ti ti-pencil"></i> {{ i18n.ts.edit }}</MkButton> @@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> <MkInfo v-else>{{ i18n.ts._role.isConditionalRole }}</MkInfo> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 2e9a33d1b8..52b8240733 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div class="_gaps"> <MkFolder> <template #label>{{ i18n.ts._role.baseRole }}</template> @@ -165,6 +165,14 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.maxFileSize, 'maxFileSizeMb'])"> + <template #label>{{ i18n.ts._role._options.maxFileSize }}</template> + <template #suffix>{{ policies.maxFileSizeMb }}MB</template> + <MkInput v-model="policies.maxFileSizeMb" type="number"> + <template #suffix>MB</template> + </MkInput> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])"> <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template> <template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template> @@ -310,7 +318,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFoldableSection> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index 537c788ccf..414dc145ff 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <div class="_gaps_m"> <MkFolder v-if="meta.federation !== 'none'"> <template #label>{{ i18n.ts.authorizedFetchSection }}</template> @@ -87,7 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue index 98cd0775fe..4ad056edff 100644 --- a/packages/frontend/src/pages/admin/server-rules.vue +++ b/packages/frontend/src/pages/admin/server-rules.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <div class="_gaps_m"> <div>{{ i18n.ts._serverRules.description }}</div> <Sortable @@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index 21fd1b4b1a..40335e7914 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <div class="_gaps_m"> <MkFolder :defaultOpen="true"> <template #icon><i class="ti ti-info-circle"></i></template> @@ -282,7 +282,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/system-webhook.vue b/packages/frontend/src/pages/admin/system-webhook.vue index a3214028e6..d5402f608c 100644 --- a/packages/frontend/src/pages/admin/system-webhook.vue +++ b/packages/frontend/src/pages/admin/system-webhook.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <div class="_gaps_m"> <MkButton primary @click="onCreateWebhookClicked"> <i class="ti ti-plus"></i> {{ i18n.ts._webhookSettings.createWebhook }} @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </FormSection> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue index d37f7dbf80..080ed9b90c 100644 --- a/packages/frontend/src/pages/admin/users.vue +++ b/packages/frontend/src/pages/admin/users.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <div class="_gaps"> <div :class="$style.inputs"> <MkButton style="margin-left: auto" @click="resetQuery">{{ i18n.ts.reset }}</MkButton> @@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkPagination> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/ads.vue b/packages/frontend/src/pages/ads.vue index 700ac0bd1a..31b66584d9 100644 --- a/packages/frontend/src/pages/ads.vue +++ b/packages/frontend/src/pages/ads.vue @@ -5,11 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader> - <MkSpacer :contentMax="500"> + <div class="_spacer" style="--MI_SPACER-w: 500px;"> <div class="_gaps"> <MkAd v-for="ad in instance.ads" :key="ad.id" :specify="ad"/> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/announcement.vue b/packages/frontend/src/pages/announcement.vue index 4bba0acdc2..9fa06dfd9a 100644 --- a/packages/frontend/src/pages/announcement.vue +++ b/packages/frontend/src/pages/announcement.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <Transition :enterActiveClass="prefer.s.animation ? $style.fadeEnterActive : ''" :leaveActiveClass="prefer.s.animation ? $style.fadeLeaveActive : ''" @@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkError v-else-if="error" @retry="fetch()"/> <MkLoading v-else/> </Transition> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue index d529ad592c..e145121e23 100644 --- a/packages/frontend/src/pages/announcements.vue +++ b/packages/frontend/src/pages/announcements.vue @@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div class="_gaps"> <MkInfo v-if="$i && $i.hasUnreadAnnouncement && tab === 'current'" warn>{{ i18n.ts.youHaveUnreadAnnouncements }}</MkInfo> @@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkPagination> </div> </MkSwiper> - </MkSpacer> + </div> </PageWithHeader> </template> @@ -50,7 +50,6 @@ import { ref, computed } from 'vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; import MkInfo from '@/components/MkInfo.vue'; -import MkSwiper from '@/components/MkSwiper.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue index c5c5b2144f..1bf7e257e5 100644 --- a/packages/frontend/src/pages/antenna-timeline.vue +++ b/packages/frontend/src/pages/antenna-timeline.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <div ref="rootEl"> <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> <div :class="$style.tl"> @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only /> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/api-console.vue b/packages/frontend/src/pages/api-console.vue index 53020bfb08..140974e695 100644 --- a/packages/frontend/src/pages/api-console.vue +++ b/packages/frontend/src/pages/api-console.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div class="_gaps_m"> <div class="_gaps_m"> <MkInput v-model="endpoint" :datalist="endpoints" debounce @update:modelValue="onEndpointChange()"> @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkTextarea> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue index 5aa8dbcc5d..26a4edfe0c 100644 --- a/packages/frontend/src/pages/auth.vue +++ b/packages/frontend/src/pages/auth.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="500"> + <div class="_spacer" style="--MI_SPACER-w: 500px;"> <div v-if="state == 'fetch-session-error'"> <p>{{ i18n.ts.somethingHappened }}</p> </div> @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only <p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p> <MkSignin @login="onLogin"/> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue index 5a5e305f80..cb0e1666f8 100644 --- a/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue +++ b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else #header>New decoration</template> <div style="display: flex; flex-direction: column; min-height: 100%;"> - <MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px; flex-grow: 1;"> <div class="_gaps_m"> <div :class="$style.preview"> <div :class="[$style.previewItem, $style.light]"> @@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> <MkButton v-if="avatarDecoration" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> - </MkSpacer> + </div> <div :class="$style.footer"> <MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.avatarDecoration ? i18n.ts.update : i18n.ts.create }}</MkButton> </div> diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue index 2bab449089..675e558de9 100644 --- a/packages/frontend/src/pages/avatar-decorations.vue +++ b/packages/frontend/src/pages/avatar-decorations.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <div class="_gaps"> <div :class="$style.decorations"> <div @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue index 084fee15cf..009514cdc8 100644 --- a/packages/frontend/src/pages/channel-editor.vue +++ b/packages/frontend/src/pages/channel-editor.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div v-if="channelId == null || channel != null" class="_gaps_m"> <MkInput v-model="name"> <template #label>{{ i18n.ts.name }}</template> @@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton v-if="channelId" danger @click="archive()"><i class="ti ti-trash"></i> {{ i18n.ts.archive }}</MkButton> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index 606fb06324..56d037758f 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div v-if="channel && tab === 'overview'" class="_gaps"> <div class="_panel" :class="$style.bannerContainer"> @@ -58,14 +58,14 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </MkSwiper> - </MkSpacer> + </div> <template #footer> <div :class="$style.footer"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;"> <div class="_buttonsCenter"> <MkButton inline rounded primary gradate @click="openPostForm()"><i class="ti ti-pencil"></i> {{ i18n.ts.postToTheChannel }}</MkButton> </div> - </MkSpacer> + </div> </div> </template> </PageWithHeader> @@ -93,7 +93,6 @@ import { prefer } from '@/preferences.js'; import MkNote from '@/components/MkNote.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import MkSwiper from '@/components/MkSwiper.vue'; import { isSupportShare } from '@/utility/navigator.js'; import { copyToClipboard } from '@/utility/copy-to-clipboard.js'; import { notesSearchAvailable } from '@/utility/check-permissions.js'; diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue index 27a6a6168d..b2b2bc02d2 100644 --- a/packages/frontend/src/pages/channels.vue +++ b/packages/frontend/src/pages/channels.vue @@ -4,57 +4,55 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="1200"> - <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <div v-if="tab === 'search'" :class="$style.searchRoot"> - <div class="_gaps"> - <MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search"> - <template #prefix><i class="ti ti-search"></i></template> - </MkInput> - <MkRadios v-model="searchType" @update:modelValue="search()"> - <option value="nameAndDescription">{{ i18n.ts._channel.nameAndDescription }}</option> - <option value="nameOnly">{{ i18n.ts._channel.nameOnly }}</option> - </MkRadios> - <MkButton large primary gradate rounded @click="search">{{ i18n.ts.search }}</MkButton> - </div> - - <MkFoldableSection v-if="channelPagination"> - <template #header>{{ i18n.ts.searchResult }}</template> - <MkChannelList :key="key" :pagination="channelPagination"/> - </MkFoldableSection> - </div> - <div v-if="tab === 'featured'"> - <MkPagination v-slot="{items}" :pagination="featuredPagination"> - <div :class="$style.root"> - <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/> - </div> - </MkPagination> - </div> - <div v-else-if="tab === 'favorites'"> - <MkPagination v-slot="{items}" :pagination="favoritesPagination"> - <div :class="$style.root"> - <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/> - </div> - </MkPagination> - </div> - <div v-else-if="tab === 'following'"> - <MkPagination v-slot="{items}" :pagination="followingPagination"> - <div :class="$style.root"> - <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/> - </div> - </MkPagination> - </div> - <div v-else-if="tab === 'owned'"> - <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> - <MkPagination v-slot="{items}" :pagination="ownedPagination"> - <div :class="$style.root"> - <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/> - </div> - </MkPagination> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div class="_spacer" style="--MI_SPACER-w: 1200px;"> + <div v-if="tab === 'search'" :class="$style.searchRoot"> + <div class="_gaps"> + <MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search"> + <template #prefix><i class="ti ti-search"></i></template> + </MkInput> + <MkRadios v-model="searchType" @update:modelValue="search()"> + <option value="nameAndDescription">{{ i18n.ts._channel.nameAndDescription }}</option> + <option value="nameOnly">{{ i18n.ts._channel.nameOnly }}</option> + </MkRadios> + <MkButton large primary gradate rounded @click="search">{{ i18n.ts.search }}</MkButton> </div> - </MkSwiper> - </MkSpacer> + + <MkFoldableSection v-if="channelPagination"> + <template #header>{{ i18n.ts.searchResult }}</template> + <MkChannelList :key="key" :pagination="channelPagination"/> + </MkFoldableSection> + </div> + <div v-if="tab === 'featured'"> + <MkPagination v-slot="{items}" :pagination="featuredPagination"> + <div :class="$style.root"> + <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/> + </div> + </MkPagination> + </div> + <div v-else-if="tab === 'favorites'"> + <MkPagination v-slot="{items}" :pagination="favoritesPagination"> + <div :class="$style.root"> + <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/> + </div> + </MkPagination> + </div> + <div v-else-if="tab === 'following'"> + <MkPagination v-slot="{items}" :pagination="followingPagination"> + <div :class="$style.root"> + <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/> + </div> + </MkPagination> + </div> + <div v-else-if="tab === 'owned'"> + <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> + <MkPagination v-slot="{items}" :pagination="ownedPagination"> + <div :class="$style.root"> + <MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/> + </div> + </MkPagination> + </div> + </div> </PageWithHeader> </template> @@ -67,7 +65,6 @@ import MkInput from '@/components/MkInput.vue'; import MkRadios from '@/components/MkRadios.vue'; import MkButton from '@/components/MkButton.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import MkSwiper from '@/components/MkSwiper.vue'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; import { useRouter } from '@/router.js'; diff --git a/packages/frontend/src/pages/chat/home.vue b/packages/frontend/src/pages/chat/home.vue index 1edd18ddf0..652ab04be6 100644 --- a/packages/frontend/src/pages/chat/home.vue +++ b/packages/frontend/src/pages/chat/home.vue @@ -4,16 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> <MkPolkadots v-if="tab === 'home'" accented/> - <MkSpacer :contentMax="700"> - <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <XHome v-if="tab === 'home'"/> - <XInvitations v-else-if="tab === 'invitations'"/> - <XJoiningRooms v-else-if="tab === 'joiningRooms'"/> - <XOwnedRooms v-else-if="tab === 'ownedRooms'"/> - </MkSwiper> - </MkSpacer> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> + <XHome v-if="tab === 'home'"/> + <XInvitations v-else-if="tab === 'invitations'"/> + <XJoiningRooms v-else-if="tab === 'joiningRooms'"/> + <XOwnedRooms v-else-if="tab === 'ownedRooms'"/> + </div> </PageWithHeader> </template> @@ -25,7 +23,6 @@ import XJoiningRooms from './home.joiningRooms.vue'; import XOwnedRooms from './home.ownedRooms.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; -import MkSwiper from '@/components/MkSwiper.vue'; import MkPolkadots from '@/components/MkPolkadots.vue'; const tab = ref('home'); diff --git a/packages/frontend/src/pages/chat/message.vue b/packages/frontend/src/pages/chat/message.vue index 3ac90a93fd..a04ec7fd87 100644 --- a/packages/frontend/src/pages/chat/message.vue +++ b/packages/frontend/src/pages/chat/message.vue @@ -5,14 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div v-if="initializing || message == null"> <MkLoading/> </div> <div v-else> <XMessage :message="message" :isSearchResult="true"/> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/chat/room.vue b/packages/frontend/src/pages/chat/room.vue index 8b351c1ec8..e05125a3b2 100644 --- a/packages/frontend/src/pages/chat/room.vue +++ b/packages/frontend/src/pages/chat/room.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :reversed="tab === 'chat'" :tabs="headerTabs" :actions="headerActions"> - <MkSpacer v-if="tab === 'chat'" :contentMax="700"> + <div v-if="tab === 'chat'" class="_spacer" style="--MI_SPACER-w: 700px;"> <div class="_gaps"> <div v-if="initializing"> <MkLoading/> @@ -56,19 +56,19 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInfo v-if="$i.policies.chatAvailability !== 'available'" warn>{{ $i.policies.chatAvailability === 'readonly' ? i18n.ts._chat.chatIsReadOnlyForThisAccountOrServer : i18n.ts._chat.chatNotAvailableForThisAccountOrServer }}</MkInfo> </div> - </MkSpacer> + </div> - <MkSpacer v-else-if="tab === 'search'" :contentMax="700"> + <div v-else-if="tab === 'search'" class="_spacer" style="--MI_SPACER-w: 700px;"> <XSearch :userId="userId" :roomId="roomId"/> - </MkSpacer> + </div> - <MkSpacer v-else-if="tab === 'members'" :contentMax="700"> + <div v-else-if="tab === 'members'" class="_spacer" style="--MI_SPACER-w: 700px;"> <XMembers v-if="room != null" :room="room" @inviteUser="inviteUser"/> - </MkSpacer> + </div> - <MkSpacer v-else-if="tab === 'info'" :contentMax="700"> + <div v-else-if="tab === 'info'" class="_spacer" style="--MI_SPACER-w: 700px;"> <XInfo v-if="room != null" :room="room"/> - </MkSpacer> + </div> <template #footer> <div v-if="tab === 'chat'" :class="$style.footer"> diff --git a/packages/frontend/src/pages/clicker.vue b/packages/frontend/src/pages/clicker.vue index 479204f39b..d418a78ee5 100644 --- a/packages/frontend/src/pages/clicker.vue +++ b/packages/frontend/src/pages/clicker.vue @@ -5,9 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <MkClickerGame/> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue index 22c8a6b49d..87a361c1e2 100644 --- a/packages/frontend/src/pages/clip.vue +++ b/packages/frontend/src/pages/clip.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <div v-if="clip" class="_gaps"> <div class="_panel"> <div class="_gaps_s" :class="$style.description"> @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkNotes :pagination="pagination" :detail="true"/> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/contact.vue b/packages/frontend/src/pages/contact.vue index 39d70cafc7..eb94f23ac9 100644 --- a/packages/frontend/src/pages/contact.vue +++ b/packages/frontend/src/pages/contact.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader> - <MkSpacer :contentMax="600" :marginMin="20"> + <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 20px;"> <div class="_gaps_m"> <MkKeyValue :copy="instance.maintainerName"> <template #key>{{ i18n.ts.administrator }}</template> @@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </MkKeyValue> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index 7205cca42f..0bca5c1076 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="900"> + <div class="_spacer" style="--MI_SPACER-w: 900px;"> <div class="ogwlenmc"> <div v-if="tab === 'local'" class="local"> <MkInput v-model="query" :debounce="true" type="search" autocapitalize="off"> @@ -66,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkPagination> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/drive.file.vue b/packages/frontend/src/pages/drive.file.vue index 170d48064f..2a7924f56f 100644 --- a/packages/frontend/src/pages/drive.file.vue +++ b/packages/frontend/src/pages/drive.file.vue @@ -10,13 +10,13 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <MkSpacer v-if="tab === 'info'" :contentMax="800"> + <div v-if="tab === 'info'" class="_spacer" style="--MI_SPACER-w: 800px;"> <XFileInfo :fileId="fileId"/> - </MkSpacer> + </div> - <MkSpacer v-else-if="tab === 'notes'" :contentMax="800"> + <div v-else-if="tab === 'notes'" class="_spacer" style="--MI_SPACER-w: 800px;"> <XNotes :fileId="fileId"/> - </MkSpacer> + </div> </MkSwiper> </MkStickyContainer> </template> diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue index eee174a6af..6b17c07b1c 100644 --- a/packages/frontend/src/pages/drop-and-fusion.game.vue +++ b/packages/frontend/src/pages/drop-and-fusion.game.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="800"> +<div class="_spacer" style="--MI_SPACER-w: 800px;"> <div :class="$style.root"> <div v-if="!gameLoaded" :class="$style.loadingScreen"> <div>{{ i18n.ts.loading }}<MkEllipsis/></div> @@ -187,7 +187,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </div> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue index 7f571a7c36..bc957ff38a 100644 --- a/packages/frontend/src/pages/drop-and-fusion.vue +++ b/packages/frontend/src/pages/drop-and-fusion.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only :moveClass="$style.transition_zoom_move" mode="out-in" > - <MkSpacer v-if="!gameStarted" :contentMax="800"> + <div v-if="!gameStarted" class="_spacer" style="--MI_SPACER-w: 800px;"> <div :class="$style.root"> <div class="_gaps"> <div class="_woodenFrame" style="text-align: center;"> @@ -80,7 +80,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </div> - </MkSpacer> + </div> <XGame v-else :gameMode="gameMode" :mute="mute" @end="onGameEnd"/> </Transition> </template> diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue index dc570b05e4..2c10dadab7 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/emoji-edit-dialog.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else #header>New emoji</template> <div style="display: flex; flex-direction: column; min-height: 100%;"> - <MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px; flex-grow: 1;"> <div class="_gaps_m"> <div v-if="imgUrl != null" :class="$style.imgs"> <div style="background: #000;" :class="$style.imgContainer"> @@ -70,7 +70,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> <MkButton v-if="emoji" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> - </MkSpacer> + </div> <div :class="$style.footer"> <MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> </div> diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue index 8b16a88ff3..a47e3efbc8 100644 --- a/packages/frontend/src/pages/explore.featured.vue +++ b/packages/frontend/src/pages/explore.featured.vue @@ -4,14 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="800"> +<div class="_spacer" style="--MI_SPACER-w: 800px;"> <MkTab v-model="tab" style="margin-bottom: var(--MI-margin);"> <option value="notes">{{ i18n.ts.notes }}</option> <option value="polls">{{ i18n.ts.poll }}</option> </MkTab> <MkNotes v-if="tab === 'notes'" :pagination="paginationForNotes"/> <MkNotes v-else-if="tab === 'polls'" :pagination="paginationForPolls"/> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/explore.roles.vue b/packages/frontend/src/pages/explore.roles.vue index ffefeb9618..7ee01610a7 100644 --- a/packages/frontend/src/pages/explore.roles.vue +++ b/packages/frontend/src/pages/explore.roles.vue @@ -4,11 +4,11 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="700"> +<div class="_spacer" style="--MI_SPACER-w: 700px;"> <div class="_gaps_s"> <MkRolePreview v-for="role in roles" :key="role.id" :role="role" :forModeration="false"/> </div> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue index 4db26e799c..6375944edf 100644 --- a/packages/frontend/src/pages/explore.users.vue +++ b/packages/frontend/src/pages/explore.users.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="1200"> +<div class="_spacer" style="--MI_SPACER-w: 1200px;"> <MkTab v-if="instance.federation !== 'none'" v-model="origin" style="margin-bottom: var(--MI-margin);"> <option value="local">{{ i18n.ts.local }}</option> <option value="remote">{{ i18n.ts.remote }}</option> @@ -59,7 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFoldableSection> </template> </div> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue index bcece47e35..c4f6ddc33e 100644 --- a/packages/frontend/src/pages/explore.vue +++ b/packages/frontend/src/pages/explore.vue @@ -4,18 +4,16 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <div v-if="tab === 'featured'"> - <XFeatured/> - </div> - <div v-else-if="tab === 'users'"> - <XUsers/> - </div> - <div v-else-if="tab === 'roles'"> - <XRoles/> - </div> - </MkSwiper> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div v-if="tab === 'featured'"> + <XFeatured/> + </div> + <div v-else-if="tab === 'users'"> + <XUsers/> + </div> + <div v-else-if="tab === 'roles'"> + <XRoles/> + </div> </PageWithHeader> </template> @@ -24,8 +22,6 @@ import { computed, watch, ref, useTemplateRef } from 'vue'; import XFeatured from './explore.featured.vue'; import XUsers from './explore.users.vue'; import XRoles from './explore.roles.vue'; -import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import MkSwiper from '@/components/MkSwiper.vue'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue index 6c95d37296..aa18f44e88 100644 --- a/packages/frontend/src/pages/favorites.vue +++ b/packages/frontend/src/pages/favorites.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <MkPagination :pagination="pagination"> <template #empty> <div class="_fullinfo"> @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkDateSeparatedList> </template> </MkPagination> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue index 825a3be7c1..4386209f7c 100644 --- a/packages/frontend/src/pages/flash/flash-edit.vue +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div class="_gaps"> <MkInput v-model="title"> <template #label>{{ i18n.ts._play.title }}</template> @@ -24,16 +24,16 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts._play.script }}</template> </MkCodeEditor> </div> - </MkSpacer> + </div> <template #footer> <div :class="$style.footer"> - <MkSpacer> + <div class="_spacer"> <div class="_buttons"> <MkButton primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> <MkButton @click="show"><i class="ti ti-eye"></i> {{ i18n.ts.show }}</MkButton> <MkButton v-if="flash" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> - </MkSpacer> + </div> </div> </template> </PageWithHeader> diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue index 4ef33cbe0f..f3365fcedf 100644 --- a/packages/frontend/src/pages/flash/flash-index.vue +++ b/packages/frontend/src/pages/flash/flash-index.vue @@ -4,37 +4,35 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> - <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <div v-if="tab === 'featured'"> - <MkPagination v-slot="{items}" :pagination="featuredFlashsPagination"> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> + <div v-if="tab === 'featured'"> + <MkPagination v-slot="{items}" :pagination="featuredFlashsPagination"> + <div class="_gaps_s"> + <MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash"/> + </div> + </MkPagination> + </div> + + <div v-else-if="tab === 'my'"> + <div class="_gaps"> + <MkButton gradate rounded style="margin: 0 auto;" @click="create()"><i class="ti ti-plus"></i></MkButton> + <MkPagination v-slot="{items}" :pagination="myFlashsPagination"> <div class="_gaps_s"> <MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash"/> </div> </MkPagination> </div> + </div> - <div v-else-if="tab === 'my'"> - <div class="_gaps"> - <MkButton gradate rounded style="margin: 0 auto;" @click="create()"><i class="ti ti-plus"></i></MkButton> - <MkPagination v-slot="{items}" :pagination="myFlashsPagination"> - <div class="_gaps_s"> - <MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash"/> - </div> - </MkPagination> + <div v-else-if="tab === 'liked'"> + <MkPagination v-slot="{items}" :pagination="likedFlashsPagination"> + <div class="_gaps_s"> + <MkFlashPreview v-for="like in items" :key="like.flash.id" :flash="like.flash"/> </div> - </div> - - <div v-else-if="tab === 'liked'"> - <MkPagination v-slot="{items}" :pagination="likedFlashsPagination"> - <div class="_gaps_s"> - <MkFlashPreview v-for="like in items" :key="like.flash.id" :flash="like.flash"/> - </div> - </MkPagination> - </div> - </MkSwiper> - </MkSpacer> + </MkPagination> + </div> + </div> </PageWithHeader> </template> @@ -43,7 +41,6 @@ import { computed, ref } from 'vue'; import MkFlashPreview from '@/components/MkFlashPreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; -import MkSwiper from '@/components/MkSwiper.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { useRouter } from '@/router.js'; diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue index e1a51ccaad..b17faca2a9 100644 --- a/packages/frontend/src/pages/flash/flash.vue +++ b/packages/frontend/src/pages/flash/flash.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in"> <div v-if="flash" :key="flash.id"> <Transition :name="prefer.s.animation ? 'zoom' : ''" mode="out-in"> @@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkError v-else-if="error" @retry="fetchFlash()"/> <MkLoading v-else/> </Transition> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue index f6357ba1b1..dd8ec34214 100644 --- a/packages/frontend/src/pages/follow-requests.vue +++ b/packages/frontend/src/pages/follow-requests.vue @@ -4,39 +4,37 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> - <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <MkPagination ref="paginationComponent" :pagination="pagination"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" draggable="false"/> - <div>{{ i18n.ts.noFollowRequests }}</div> - </div> - </template> - <template #default="{items}"> - <div class="mk-follow-requests _gaps"> - <div v-for="req in items" :key="req.id" class="user _panel"> - <MkAvatar class="avatar" :user="displayUser(req)" indicator link preview/> - <div class="body"> - <div class="name"> - <MkA v-user-preview="displayUser(req).id" class="name" :to="userPage(displayUser(req))"><MkUserName :user="displayUser(req)"/></MkA> - <p class="acct">@{{ acct(displayUser(req)) }}</p> - </div> - <div v-if="tab === 'list'" class="commands"> - <MkButton class="command" rounded primary @click="accept(displayUser(req))"><i class="ti ti-check"/> {{ i18n.ts.accept }}</MkButton> - <MkButton class="command" rounded danger @click="reject(displayUser(req))"><i class="ti ti-x"/> {{ i18n.ts.reject }}</MkButton> - </div> - <div v-else class="commands"> - <MkButton class="command" rounded danger @click="cancel(displayUser(req))"><i class="ti ti-x"/> {{ i18n.ts.cancel }}</MkButton> - </div> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> + <MkPagination ref="paginationComponent" :pagination="pagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" draggable="false"/> + <div>{{ i18n.ts.noFollowRequests }}</div> + </div> + </template> + <template #default="{items}"> + <div class="mk-follow-requests _gaps"> + <div v-for="req in items" :key="req.id" class="user _panel"> + <MkAvatar class="avatar" :user="displayUser(req)" indicator link preview/> + <div class="body"> + <div class="name"> + <MkA v-user-preview="displayUser(req).id" class="name" :to="userPage(displayUser(req))"><MkUserName :user="displayUser(req)"/></MkA> + <p class="acct">@{{ acct(displayUser(req)) }}</p> + </div> + <div v-if="tab === 'list'" class="commands"> + <MkButton class="command" rounded primary @click="accept(displayUser(req))"><i class="ti ti-check"/> {{ i18n.ts.accept }}</MkButton> + <MkButton class="command" rounded danger @click="reject(displayUser(req))"><i class="ti ti-x"/> {{ i18n.ts.reject }}</MkButton> + </div> + <div v-else class="commands"> + <MkButton class="command" rounded danger @click="cancel(displayUser(req))"><i class="ti ti-x"/> {{ i18n.ts.cancel }}</MkButton> </div> </div> </div> - </template> - </MkPagination> - </MkSwiper> - </MkSpacer> + </div> + </template> + </MkPagination> + </div> </PageWithHeader> </template> @@ -52,7 +50,6 @@ import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { infoImageUrl } from '@/instance.js'; import { $i } from '@/i.js'; -import MkSwiper from '@/components/MkSwiper.vue'; const paginationComponent = useTemplateRef('paginationComponent'); diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue index 7831e084a2..caae30f9fd 100644 --- a/packages/frontend/src/pages/gallery/edit.vue +++ b/packages/frontend/src/pages/gallery/edit.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 800px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <FormSuspense :p="init" class="_gaps"> <MkInput v-model="title"> <template #label>{{ i18n.ts.title }}</template> @@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton v-if="postId" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> </FormSuspense> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue index f9e1c9c9a3..f56a1dddf9 100644 --- a/packages/frontend/src/pages/gallery/index.vue +++ b/packages/frontend/src/pages/gallery/index.vue @@ -4,44 +4,42 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="1400"> - <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <div v-if="tab === 'explore'"> - <MkFoldableSection class="_margin"> - <template #header><i class="ti ti-clock"></i>{{ i18n.ts.recentPosts }}</template> - <MkPagination v-slot="{items}" :pagination="recentPostsPagination" :disableAutoLoad="true"> - <div :class="$style.items"> - <MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/> - </div> - </MkPagination> - </MkFoldableSection> - <MkFoldableSection class="_margin"> - <template #header><i class="ti ti-comet"></i>{{ i18n.ts.popularPosts }}</template> - <MkPagination v-slot="{items}" :pagination="popularPostsPagination" :disableAutoLoad="true"> - <div :class="$style.items"> - <MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/> - </div> - </MkPagination> - </MkFoldableSection> - </div> - <div v-else-if="tab === 'liked'"> - <MkPagination v-slot="{items}" :pagination="likedPostsPagination"> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div class="_spacer" style="--MI_SPACER-w: 1400px;"> + <div v-if="tab === 'explore'"> + <MkFoldableSection class="_margin"> + <template #header><i class="ti ti-clock"></i>{{ i18n.ts.recentPosts }}</template> + <MkPagination v-slot="{items}" :pagination="recentPostsPagination" :disableAutoLoad="true"> <div :class="$style.items"> - <MkGalleryPostPreview v-for="like in items" :key="like.id" :post="like.post" class="post"/> + <MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/> </div> </MkPagination> - </div> - <div v-else-if="tab === 'my'"> - <MkA to="/gallery/new" class="_link" style="margin: 16px;"><i class="ti ti-plus"></i> {{ i18n.ts.postToGallery }}</MkA> - <MkPagination v-slot="{items}" :pagination="myPostsPagination"> + </MkFoldableSection> + <MkFoldableSection class="_margin"> + <template #header><i class="ti ti-comet"></i>{{ i18n.ts.popularPosts }}</template> + <MkPagination v-slot="{items}" :pagination="popularPostsPagination" :disableAutoLoad="true"> <div :class="$style.items"> <MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/> </div> </MkPagination> - </div> - </MkSwiper> - </MkSpacer> + </MkFoldableSection> + </div> + <div v-else-if="tab === 'liked'"> + <MkPagination v-slot="{items}" :pagination="likedPostsPagination"> + <div :class="$style.items"> + <MkGalleryPostPreview v-for="like in items" :key="like.id" :post="like.post" class="post"/> + </div> + </MkPagination> + </div> + <div v-else-if="tab === 'my'"> + <MkA to="/gallery/new" class="_link" style="margin: 16px;"><i class="ti ti-plus"></i> {{ i18n.ts.postToGallery }}</MkA> + <MkPagination v-slot="{items}" :pagination="myPostsPagination"> + <div :class="$style.items"> + <MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/> + </div> + </MkPagination> + </div> + </div> </PageWithHeader> </template> @@ -50,7 +48,6 @@ import { watch, ref, computed } from 'vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue'; -import MkSwiper from '@/components/MkSwiper.vue'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; import { useRouter } from '@/router.js'; diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue index 27f4687eb4..7c754f0b03 100644 --- a/packages/frontend/src/pages/gallery/post.vue +++ b/packages/frontend/src/pages/gallery/post.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="1000" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 1000px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <div class="_root"> <Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in"> <div v-if="post" class="rkxwuolj"> @@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkLoading v-else/> </Transition> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/games.vue b/packages/frontend/src/pages/games.vue index 7436c13332..12b84d19aa 100644 --- a/packages/frontend/src/pages/games.vue +++ b/packages/frontend/src/pages/games.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <div class="_gaps"> <div class="_panel" :class="$style.link"> <MkA to="/bubble-game"> @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkA> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/install-extensions.vue b/packages/frontend/src/pages/install-extensions.vue index bf57b0c231..4e814ef84f 100644 --- a/packages/frontend/src/pages/install-extensions.vue +++ b/packages/frontend/src/pages/install-extensions.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithAnimBg> - <MkSpacer :contentMax="550" :marginMax="50"> + <div class="_spacer" style="--MI_SPACER-w: 550px; --MI_SPACER-max: 50px;"> <MkLoading v-if="uiPhase === 'fetching'"/> <MkExtensionInstaller v-else-if="uiPhase === 'confirm' && data" :extension="data" @confirm="install()" @cancel="close_()"> <template #additionalInfo> @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton @click="close_()">{{ i18n.ts.close }}</MkButton> </div> </div> - </MkSpacer> + </div> </PageWithAnimBg> </template> diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index eff513d241..479774faef 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer v-if="instance" :contentMax="600" :marginMin="16" :marginMax="32"> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div v-if="instance" class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <MkSwiper v-model:tab="tab" :tabs="headerTabs"> <div v-if="tab === 'overview'" class="_gaps_m"> <div class="fnfelxur"> @@ -166,7 +166,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkObjectView> </div> </MkSwiper> - </MkSpacer> + </div> </PageWithHeader> </template> @@ -192,7 +192,6 @@ import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkPagination from '@/components/MkPagination.vue'; -import MkSwiper from '@/components/MkSwiper.vue'; import { getProxiedImageUrlNullable } from '@/utility/media-proxy.js'; import { dateString } from '@/filters/date.js'; import MkTextarea from '@/components/MkTextarea.vue'; diff --git a/packages/frontend/src/pages/invite.vue b/packages/frontend/src/pages/invite.vue index 77ad1cdd96..0342d5c604 100644 --- a/packages/frontend/src/pages/invite.vue +++ b/packages/frontend/src/pages/invite.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader> - <MkSpacer v-if="!instance.disableRegistration || !($i && ($i.isAdmin || $i.policies.canInvite))" :contentMax="1200"> + <div v-if="!instance.disableRegistration || !($i && ($i.isAdmin || $i.policies.canInvite))" class="_spacer" style="--MI_SPACER-w: 1200px;"> <div :class="$style.root"> <img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/> <div :class="$style.text"> @@ -13,8 +13,8 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.nothing }} </div> </div> - </MkSpacer> - <MkSpacer v-else :contentMax="800"> + </div> + <div v-else class="_spacer" style="--MI_SPACER-w: 800px;"> <div class="_gaps_m" style="text-align: center;"> <div v-if="resetCycle && inviteLimit">{{ i18n.tsx.inviteLimitResetCycle({ time: resetCycle, limit: inviteLimit }) }}</div> <MkButton inline primary rounded :disabled="currentInviteLimit !== null && currentInviteLimit <= 0" @click="create"><i class="ti ti-user-plus"></i> {{ i18n.ts.createInviteCode }}</MkButton> @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </MkPagination> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue index 3a1f98db74..1676441191 100644 --- a/packages/frontend/src/pages/list.vue +++ b/packages/frontend/src/pages/list.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer v-if="error != null" :contentMax="1200"> + <div v-if="error != null" class="_spacer" style="--MI_SPACER-w: 1200px;"> <div :class="$style.root"> <img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/> <p :class="$style.text"> @@ -13,8 +13,8 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.nothing }} </p> </div> - </MkSpacer> - <MkSpacer v-else-if="list" :contentMax="700"> + </div> + <div v-else-if="list" class="_spacer" style="--MI_SPACER-w: 700px;"> <div v-if="list" class="members _margin"> <div :class="$style.member_text">{{ i18n.ts.members }}</div> <div class="_gaps_s"> @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton v-if="list.isLiked" v-tooltip="i18n.ts.unlike" inline :class="$style.button" asLike primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="list.likedCount > 0" class="count">{{ list.likedCount }}</span></MkButton> <MkButton v-if="!list.isLiked" v-tooltip="i18n.ts.like" inline :class="$style.button" asLike @click="like()"><i class="ti ti-heart"></i><span v-if="1 > 0" class="count">{{ list.likedCount }}</span></MkButton> <MkButton inline @click="create()"><i class="ti ti-download" :class="$style.import"></i>{{ i18n.ts.import }}</MkButton> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/lookup.vue b/packages/frontend/src/pages/lookup.vue index 623c2a6779..c969473b19 100644 --- a/packages/frontend/src/pages/lookup.vue +++ b/packages/frontend/src/pages/lookup.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <div v-if="state === 'done'" class="_buttonsCenter"> <MkButton @click="close">{{ i18n.ts.close }}</MkButton> <MkButton @click="goToMisskey">{{ i18n.ts.goToMisskey }}</MkButton> @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-else class="_fullInfo"> <MkLoading/> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue index 297436ad61..a19f7126d0 100644 --- a/packages/frontend/src/pages/my-antennas/index.vue +++ b/packages/frontend/src/pages/my-antennas/index.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div> <div v-if="antennas.length === 0" class="empty"> <div class="_fullinfo"> @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkA> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue index 5b9b3af90b..9e427ecf35 100644 --- a/packages/frontend/src/pages/my-clips/index.vue +++ b/packages/frontend/src/pages/my-clips/index.vue @@ -4,21 +4,19 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> - <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <div v-if="tab === 'my'" class="_gaps"> - <MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> + <div v-if="tab === 'my'" class="_gaps"> + <MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> - <MkPagination v-slot="{ items }" ref="pagingComponent" :pagination="pagination" class="_gaps"> - <MkClipPreview v-for="item in items" :key="item.id" :clip="item" :noUserInfo="true"/> - </MkPagination> - </div> - <div v-else-if="tab === 'favorites'" class="_gaps"> - <MkClipPreview v-for="item in favorites" :key="item.id" :clip="item"/> - </div> - </MkSwiper> - </MkSpacer> + <MkPagination v-slot="{ items }" ref="pagingComponent" :pagination="pagination" class="_gaps"> + <MkClipPreview v-for="item in items" :key="item.id" :clip="item" :noUserInfo="true"/> + </MkPagination> + </div> + <div v-else-if="tab === 'favorites'" class="_gaps"> + <MkClipPreview v-for="item in favorites" :key="item.id" :clip="item"/> + </div> + </div> </PageWithHeader> </template> @@ -33,7 +31,6 @@ import { misskeyApi } from '@/utility/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { clipsCache } from '@/cache.js'; -import MkSwiper from '@/components/MkSwiper.vue'; const pagination = { endpoint: 'clips/list' as const, diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue index 6e23769083..f4a5eafb71 100644 --- a/packages/frontend/src/pages/my-lists/index.vue +++ b/packages/frontend/src/pages/my-lists/index.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div class="_gaps"> <div v-if="items.length === 0" class="empty"> <div class="_fullinfo"> @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkA> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index c187435af9..0b76fb4725 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div v-if="list" class="_gaps"> <MkFolder> <template #label>{{ i18n.ts.settings }}</template> @@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue index ff4e9c7514..d801db017e 100644 --- a/packages/frontend/src/pages/note.vue +++ b/packages/frontend/src/pages/note.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <div> <Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in"> <div v-if="note"> @@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkLoading v-else/> </Transition> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index 61a1b2725c..5cb71945dd 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <div v-if="tab === 'all'"> <XNotifications :class="$style.notifications" :excludeTypes="excludeTypes"/> </div> @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-else-if="tab === 'directNotes'"> <MkNotes :pagination="directNotesPagination"/> </div> - </MkSpacer> + </div> </PageWithHeader> </template> @@ -24,7 +24,6 @@ import { computed, ref } from 'vue'; import { notificationTypes } from '@@/js/const.js'; import XNotifications from '@/components/MkNotifications.vue'; import MkNotes from '@/components/MkNotes.vue'; -import MkSwiper from '@/components/MkSwiper.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue index 67134f0976..7368e0329a 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div class="jqqmcavi"> <MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="ti ti-external-link"></i> {{ i18n.ts._pages.viewPage }}</MkButton> <MkButton v-if="!readonly" inline primary class="button" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> @@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton v-if="!readonly" rounded class="add" @click="add()"><i class="ti ti-plus"></i></MkButton> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue index 9e76a450e6..59b1a5a137 100644 --- a/packages/frontend/src/pages/page.vue +++ b/packages/frontend/src/pages/page.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <Transition :enterActiveClass="prefer.s.animation ? $style.fadeEnterActive : ''" :leaveActiveClass="prefer.s.animation ? $style.fadeLeaveActive : ''" @@ -92,7 +92,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkError v-else-if="error" @retry="fetchPage()"/> <MkLoading v-else/> </Transition> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/pages.vue b/packages/frontend/src/pages/pages.vue index d412bad616..880c4deb25 100644 --- a/packages/frontend/src/pages/pages.vue +++ b/packages/frontend/src/pages/pages.vue @@ -4,35 +4,33 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="700"> - <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <div v-if="tab === 'featured'"> - <MkPagination v-slot="{items}" :pagination="featuredPagesPagination"> - <div class="_gaps"> - <MkPagePreview v-for="page in items" :key="page.id" :page="page"/> - </div> - </MkPagination> - </div> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> + <div v-if="tab === 'featured'"> + <MkPagination v-slot="{items}" :pagination="featuredPagesPagination"> + <div class="_gaps"> + <MkPagePreview v-for="page in items" :key="page.id" :page="page"/> + </div> + </MkPagination> + </div> - <div v-else-if="tab === 'my'" class="_gaps"> - <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> - <MkPagination v-slot="{items}" :pagination="myPagesPagination"> - <div class="_gaps"> - <MkPagePreview v-for="page in items" :key="page.id" :page="page"/> - </div> - </MkPagination> - </div> + <div v-else-if="tab === 'my'" class="_gaps"> + <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> + <MkPagination v-slot="{items}" :pagination="myPagesPagination"> + <div class="_gaps"> + <MkPagePreview v-for="page in items" :key="page.id" :page="page"/> + </div> + </MkPagination> + </div> - <div v-else-if="tab === 'liked'"> - <MkPagination v-slot="{items}" :pagination="likedPagesPagination"> - <div class="_gaps"> - <MkPagePreview v-for="like in items" :key="like.page.id" :page="like.page"/> - </div> - </MkPagination> - </div> - </MkSwiper> - </MkSpacer> + <div v-else-if="tab === 'liked'"> + <MkPagination v-slot="{items}" :pagination="likedPagesPagination"> + <div class="_gaps"> + <MkPagePreview v-for="like in items" :key="like.page.id" :page="like.page"/> + </div> + </MkPagination> + </div> + </div> </PageWithHeader> </template> @@ -41,7 +39,6 @@ import { computed, ref } from 'vue'; import MkPagePreview from '@/components/MkPagePreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; -import MkSwiper from '@/components/MkSwiper.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { useRouter } from '@/router.js'; diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue index 9140555f86..9dea3eba73 100644 --- a/packages/frontend/src/pages/registry.keys.vue +++ b/packages/frontend/src/pages/registry.keys.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="600" :marginMin="16"> + <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px;"> <div class="_gaps_m"> <FormSplit> <MkKeyValue> @@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </FormSection> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue index 7c0a7f20bb..5c5bbfba39 100644 --- a/packages/frontend/src/pages/registry.value.vue +++ b/packages/frontend/src/pages/registry.value.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="600" :marginMin="16"> + <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px;"> <div class="_gaps_m"> <FormInfo warn>{{ i18n.ts.editTheseSettingsMayBreakAccount }}</FormInfo> @@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </template> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue index c60833920b..5e59082b50 100644 --- a/packages/frontend/src/pages/registry.vue +++ b/packages/frontend/src/pages/registry.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="600" :marginMin="16"> + <div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px;"> <MkButton primary @click="createKey">{{ i18n.ts._registry.createKey }}</MkButton> <div v-if="scopesWithDomain" class="_gaps_m"> @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </FormSection> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue index 0a7726a7f8..6584888148 100644 --- a/packages/frontend/src/pages/reset-password.vue +++ b/packages/frontend/src/pages/reset-password.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer v-if="token" :contentMax="700" :marginMin="16" :marginMax="32"> + <div v-if="token" class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <div class="_gaps_m"> <MkInput v-model="password" type="password"> <template #prefix><i class="ti ti-lock"></i></template> @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary @click="save">{{ i18n.ts.save }}</MkButton> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue index b7434bff9f..c0c90cb993 100644 --- a/packages/frontend/src/pages/reversi/game.board.vue +++ b/packages/frontend/src/pages/reversi/game.board.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="500"> +<div class="_spacer" style="--MI_SPACER-w: 500px;"> <div :class="$style.root" class="_gaps"> <div style="display: flex; align-items: center; justify-content: center; gap: 10px;"> <span>({{ i18n.ts._reversi.black }})</span> @@ -138,7 +138,7 @@ SPDX-License-Identifier: AGPL-3.0-only <img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; width: 200px; margin: auto;"/> </MkA> </div> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/reversi/game.setting.vue b/packages/frontend/src/pages/reversi/game.setting.vue index 957b1cfc3d..8392384963 100644 --- a/packages/frontend/src/pages/reversi/game.setting.vue +++ b/packages/frontend/src/pages/reversi/game.setting.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkStickyContainer> - <MkSpacer :contentMax="600"> + <div class="_spacer" style="--MI_SPACER-w: 600px;"> <div style="text-align: center;"><b><MkUserName :user="game.user1"/></b> vs <b><MkUserName :user="game.user2"/></b></div> <div :class="{ [$style.disallow]: isReady }"> @@ -82,10 +82,10 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </div> </div> - </MkSpacer> + </div> <template #footer> <div :class="$style.footer"> - <MkSpacer :contentMax="700" :marginMin="16" :marginMax="16"> + <div class="_spacer" style="--MI_SPACER-w: 700px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;"> <div style="text-align: center;" class="_gaps_s"> <div v-if="opponentHasSettingsChanged" style="color: var(--MI_THEME-warn);">{{ i18n.ts._reversi.opponentHasSettingsChanged }}</div> <div> @@ -103,7 +103,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="shareWhenStart">{{ i18n.ts._reversi.shareToTlTheGameWhenStart }}</MkSwitch> </div> </div> - </MkSpacer> + </div> </div> </template> </MkStickyContainer> diff --git a/packages/frontend/src/pages/reversi/index.vue b/packages/frontend/src/pages/reversi/index.vue index e3f01d9938..f3252402d7 100644 --- a/packages/frontend/src/pages/reversi/index.vue +++ b/packages/frontend/src/pages/reversi/index.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer v-if="!matchingAny && !matchingUser" :contentMax="600"> +<div v-if="!matchingAny && !matchingUser" class="_spacer" style="--MI_SPACER-w: 600px;"> <div class="_gaps"> <div> <img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/> @@ -83,8 +83,8 @@ SPDX-License-Identifier: AGPL-3.0-only </MkPagination> </MkFolder> </div> -</MkSpacer> -<MkSpacer v-else :contentMax="600"> +</div> +<div v-else class="_spacer" style="--MI_SPACER-w: 600px;"> <div :class="$style.waitingScreen"> <div v-if="matchingUser" :class="$style.waitingScreenTitle"> <I18n :src="i18n.ts.waitingFor" tag="span"> @@ -101,12 +101,13 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton inline rounded @click="cancelMatching">{{ i18n.ts.cancel }}</MkButton> </div> </div> -</MkSpacer> +</div> </template> <script lang="ts" setup> import { onDeactivated, onMounted, onUnmounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; +import { useInterval } from '@@/js/use-interval.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { definePage } from '@/page.js'; import { useStream } from '@/stream.js'; @@ -117,7 +118,6 @@ import { $i } from '@/i.js'; import MkPagination from '@/components/MkPagination.vue'; import { useRouter } from '@/router.js'; import * as os from '@/os.js'; -import { useInterval } from '@@/js/use-interval.js'; import { pleaseLogin } from '@/utility/please-login.js'; import * as sound from '@/utility/sound.js'; diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue index 5a1ed70e2f..f85dd8696a 100644 --- a/packages/frontend/src/pages/role.vue +++ b/packages/frontend/src/pages/role.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader v-model:tab="tab" :displayBackButton="true" :tabs="headerTabs"> - <MkSpacer v-if="error != null" :contentMax="1200"> + <div v-if="error != null" class="_spacer" style="--MI_SPACER-w: 1200px;"> <div :class="$style.root"> <img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/> <p :class="$style.text"> @@ -13,8 +13,8 @@ SPDX-License-Identifier: AGPL-3.0-only {{ error }} </p> </div> - </MkSpacer> - <MkSpacer v-else-if="tab === 'users'" :contentMax="1200"> + </div> + <div v-else-if="tab === 'users'" class="_spacer" style="--MI_SPACER-w: 1200px;"> <div class="_gaps_s"> <div v-if="role">{{ role.description }}</div> <MkUserList v-if="visible" :pagination="users" :extractor="(item) => item.user"/> @@ -23,14 +23,14 @@ SPDX-License-Identifier: AGPL-3.0-only <div>{{ i18n.ts.nothing }}</div> </div> </div> - </MkSpacer> - <MkSpacer v-else-if="tab === 'timeline'" :contentMax="700"> + </div> + <div v-else-if="tab === 'timeline'" class="_spacer" style="--MI_SPACER-w: 700px;"> <MkTimeline v-if="visible" ref="timeline" src="role" :role="props.roleId"/> <div v-else-if="!visible" class="_fullinfo"> <img :src="infoImageUrl" draggable="false"/> <div>{{ i18n.ts.nothing }}</div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue index 3655620f4e..21ba637958 100644 --- a/packages/frontend/src/pages/scratchpad.vue +++ b/packages/frontend/src/pages/scratchpad.vue @@ -5,8 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader> - <MkSpacer :contentMax="800"> - <div :class="$style.root"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> + <div class="_gaps"> <div class="_gaps_s"> <div :class="$style.editor" class="_panel"> <MkCodeEditor v-model="code" lang="aiscript" debounce/> @@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.scratchpadDescription }} </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> @@ -207,9 +207,6 @@ definePage(() => ({ <style lang="scss" module> .root { - display: flex; - flex-direction: column; - gap: var(--MI-margin); } .editor { diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue index 814ddf3cb9..b6d21a4616 100644 --- a/packages/frontend/src/pages/search.vue +++ b/packages/frontend/src/pages/search.vue @@ -4,21 +4,19 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"> - <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <MkSpacer v-if="tab === 'note'" :contentMax="800"> - <div v-if="notesSearchAvailable || ignoreNotesSearchAvailable"> - <XNote v-bind="props"/> - </div> - <div v-else> - <MkInfo warn>{{ i18n.ts.notesSearchNotAvailable }}</MkInfo> - </div> - </MkSpacer> +<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs" :swipable="true"> + <div v-if="tab === 'note'" class="_spacer" style="--MI_SPACER-w: 800px;"> + <div v-if="notesSearchAvailable || ignoreNotesSearchAvailable"> + <XNote v-bind="props"/> + </div> + <div v-else> + <MkInfo warn>{{ i18n.ts.notesSearchNotAvailable }}</MkInfo> + </div> + </div> - <MkSpacer v-else-if="tab === 'user'" :contentMax="800"> - <XUser v-bind="props"/> - </MkSpacer> - </MkSwiper> + <div v-else-if="tab === 'user'" class="_spacer" style="--MI_SPACER-w: 800px;"> + <XUser v-bind="props"/> + </div> </PageWithHeader> </template> @@ -28,7 +26,6 @@ import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { notesSearchAvailable } from '@/utility/check-permissions.js'; import MkInfo from '@/components/MkInfo.vue'; -import MkSwiper from '@/components/MkSwiper.vue'; const props = withDefaults(defineProps<{ query?: string, diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue index 03f973a33e..5bb125e67c 100644 --- a/packages/frontend/src/pages/settings/2fa.qrdialog.vue +++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only > <template v-if="page === 0"> <div style="height: 100cqh; overflow: auto; text-align: center;"> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps"> <MkInfo><MkLink url="https://misskey-hub.net/docs/for-users/stepped-guides/how-to-enable-2fa/" target="_blank">{{ i18n.ts._2fa.moreDetailedGuideHere }}</MkLink></MkInfo> @@ -50,12 +50,12 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton rounded @click="cancel">{{ i18n.ts.cancel }}</MkButton> <MkButton primary rounded gradate @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton> </div> - </MkSpacer> + </div> </div> </template> <template v-else-if="page === 1"> <div style="height: 100cqh; overflow: auto;"> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps"> <div>{{ i18n.ts._2fa.step3Title }}</div> <MkInput v-model="token" autocomplete="one-time-code" inputmode="numeric"></MkInput> @@ -65,12 +65,12 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> <MkButton primary rounded gradate @click="tokenDone">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton> </div> - </MkSpacer> + </div> </div> </template> <template v-else-if="page === 2"> <div style="height: 100cqh; overflow: auto;"> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div class="_gaps"> <div style="text-align: center;">{{ i18n.ts._2fa.setupCompleted }}🎉</div> <div style="text-align: center;">{{ i18n.ts._2fa.step4 }}</div> @@ -97,7 +97,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_buttonsCenter" style="margin-top: 16px;"> <MkButton primary rounded gradate @click="allDone">{{ i18n.ts.done }}</MkButton> </div> - </MkSpacer> + </div> </div> </template> </Transition> diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue index 1fd977cbd4..91280dccb9 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header>{{ i18n.ts.avatarDecorations }}</template> <div> - <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 28px;"> <div style="text-align: center;"> <div :class="$style.name">{{ decoration.name }}</div> <MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decorations="decorationsForPreview" forceShowDecoration/> @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.flip }}</template> </MkSwitch> </div> - </MkSpacer> + </div> <div :class="$style.footer" class="_buttonsCenter"> <MkButton v-if="usingIndex != null" primary rounded @click="update"><i class="ti ti-check"></i> {{ i18n.ts.update }}</MkButton> diff --git a/packages/frontend/src/pages/settings/emoji-palette.vue b/packages/frontend/src/pages/settings/emoji-palette.vue index 4e92b7a3ab..e1a1c39f03 100644 --- a/packages/frontend/src/pages/settings/emoji-palette.vue +++ b/packages/frontend/src/pages/settings/emoji-palette.vue @@ -260,12 +260,6 @@ definePage(() => ({ </script> <style lang="scss" module> -.tab { - margin: calc(var(--MI-margin) / 2) 0; - padding: calc(var(--MI-margin) / 2) 0; - background: var(--MI_THEME-bg); -} - .emojis { padding: 12px; font-size: 1.1em; diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index a11ae2a6f6..61e3ca8b6c 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :tabs="headerTabs" :actions="headerActions"> - <MkSpacer :contentMax="900" :marginMin="20" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 900px; --MI_SPACER-min: 20px; --MI_SPACER-max: 32px;"> <div ref="el" class="vvcocwet" :class="{ wide: !narrow }"> <div class="body"> <div v-if="!narrow || currentPage?.route.name == null" class="nav"> @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index 519ce6db2f..7c588acae2 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -42,6 +42,14 @@ SPDX-License-Identifier: AGPL-3.0-only </SearchMarker> <div class="_gaps_s"> + <SearchMarker :keywords="['titlebar', 'show']"> + <MkPreferenceContainer k="showTitlebar"> + <MkSwitch v-model="showTitlebar"> + <template #label><SearchLabel>{{ i18n.ts.showTitlebar }}</SearchLabel></template> + </MkSwitch> + </MkPreferenceContainer> + </SearchMarker> + <SearchMarker :keywords="['avatar', 'icon', 'decoration', 'show']"> <MkPreferenceContainer k="showAvatarDecorations"> <MkSwitch v-model="showAvatarDecorations"> @@ -945,6 +953,7 @@ const lang = ref(miLocalStorage.getItem('lang')); const dataSaver = ref(prefer.s.dataSaver); const overridedDeviceKind = prefer.model('overridedDeviceKind'); +const showTitlebar = prefer.model('showTitlebar'); const keepCw = prefer.model('keepCw'); const serverDisconnectedBehavior = prefer.model('serverDisconnectedBehavior'); const hemisphere = prefer.model('hemisphere'); diff --git a/packages/frontend/src/pages/share.vue b/packages/frontend/src/pages/share.vue index 57afdb9121..71f572657b 100644 --- a/packages/frontend/src/pages/share.vue +++ b/packages/frontend/src/pages/share.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <MkPostForm v-if="state === 'writing'" fixed @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton primary @click="close">{{ i18n.ts.close }}</MkButton> <MkButton @click="goToMisskey">{{ i18n.ts.goToMisskey }}</MkButton> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue index 77e102f239..40562ff27e 100644 --- a/packages/frontend/src/pages/tag.vue +++ b/packages/frontend/src/pages/tag.vue @@ -5,14 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <MkNotes ref="notes" class="" :pagination="pagination"/> - </MkSpacer> + </div> <template v-if="$i" #footer> <div :class="$style.footer"> - <MkSpacer :contentMax="800" :marginMin="16" :marginMax="16"> + <div class="_spacer" style="--MI_SPACER-w: 800px; --MI_SPACER-min: 16px; --MI_SPACER-max: 16px;"> <MkButton rounded primary :class="$style.button" @click="post()"><i class="ti ti-pencil"></i>{{ i18n.ts.postToHashtag }}</MkButton> - </MkSpacer> + </div> </div> </template> </PageWithHeader> diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue index 2570c40fe0..585d96bd08 100644 --- a/packages/frontend/src/pages/theme-editor.vue +++ b/packages/frontend/src/pages/theme-editor.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :tabs="headerTabs"> - <MkSpacer :contentMax="800" :marginMin="16" :marginMax="32"> + <div class="_spacer" style="--MI_SPACER-w: 800px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <div class="cwepdizn _gaps_m"> <MkFolder :defaultOpen="true"> <template #label>{{ i18n.ts.backgroundColor }}</template> @@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 731242425c..f499c35c9a 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -4,37 +4,33 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div ref="rootEl" class="_pageScrollable"> - <MkStickyContainer> - <template #header><MkPageHeader v-model:tab="src" :displayMyAvatar="true" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin"/></template> - <MkSpacer :contentMax="800"> - <MkInfo v-if="isBasicTimeline(src) && !store.r.timelineTutorials.value[src]" style="margin-bottom: var(--MI-margin);" closable @close="closeTutorial()"> - {{ i18n.ts._timelineDescription[src] }} - </MkInfo> - <MkPostForm v-if="prefer.r.showFixedPostForm.value" :class="$style.postForm" class="_panel" fixed style="margin-bottom: var(--MI-margin);"/> - <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> - <MkTimeline - ref="tlComponent" - :key="src + withRenotes + withBots + withReplies + onlyFiles + withSensitive" - :class="$style.tl" - :src="src.split(':')[0]" - :list="src.split(':')[1]" - :withRenotes="withRenotes" - :withReplies="withReplies" - :withSensitive="withSensitive" - :onlyFiles="onlyFiles" - :withBots="withBots" - :sound="true" - @queue="queueUpdated" - /> - </MkSpacer> - </MkStickyContainer> -</div> +<PageWithHeader ref="pageComponent" v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :swipable="true" :displayMyAvatar="true"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> + <MkInfo v-if="isBasicTimeline(src) && !store.r.timelineTutorials.value[src]" style="margin-bottom: var(--MI-margin);" closable @close="closeTutorial()"> + {{ i18n.ts._timelineDescription[src] }} + </MkInfo> + <MkPostForm v-if="prefer.r.showFixedPostForm.value" :class="$style.postForm" class="_panel" fixed style="margin-bottom: var(--MI-margin);"/> + <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> + <MkTimeline + ref="tlComponent" + :key="src + withRenotes + withBots + withReplies + onlyFiles + withSensitive" + :class="$style.tl" + :src="src.split(':')[0]" + :list="src.split(':')[1]" + :withRenotes="withRenotes" + :withReplies="withReplies" + :withSensitive="withSensitive" + :onlyFiles="onlyFiles" + :withBots="withBots" + :sound="true" + @queue="queueUpdated" + /> + </div> +</PageWithHeader> </template> <script lang="ts" setup> import { computed, watch, provide, useTemplateRef, ref, onMounted, onActivated } from 'vue'; -import { scrollInContainer } from '@@/js/scroll.js'; import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; import type { MenuItem } from '@/types/menu.js'; import type { BasicTimelineType } from '@/timelines.js'; @@ -52,20 +48,11 @@ import { deepMerge } from '@/utility/merge.js'; import { miLocalStorage } from '@/local-storage.js'; import { availableBasicTimelines, hasWithReplies, isAvailableBasicTimeline, isBasicTimeline, basicTimelineIconClass } from '@/timelines.js'; import { prefer } from '@/preferences.js'; -import { useRouter } from '@/router.js'; -import { useScrollPositionKeeper } from '@/use/use-scroll-position-keeper.js'; provide('shouldOmitHeaderTitle', true); const tlComponent = useTemplateRef('tlComponent'); -const rootEl = useTemplateRef('rootEl'); - -useScrollPositionKeeper(rootEl); - -const router = useRouter(); -router.useListener('same', () => { - top(); -}); +const pageComponent = useTemplateRef('pageComponent'); type TimelinePageSrc = BasicTimelineType | `list:${string}`; @@ -138,7 +125,7 @@ function queueUpdated(q: number): void { } function top(): void { - if (rootEl.value) scrollInContainer(rootEl.value, { top: 0, behavior: 'instant' }); + if (pageComponent.value) pageComponent.value.scrollToTop(); } async function chooseList(ev: MouseEvent): Promise<void> { diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue index 754ae2467e..cb4d6f4240 100644 --- a/packages/frontend/src/pages/user-list-timeline.vue +++ b/packages/frontend/src/pages/user-list-timeline.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs"> - <MkSpacer :contentMax="800"> + <div class="_spacer" style="--MI_SPACER-w: 800px;"> <div ref="rootEl"> <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> <div :class="$style.tl"> @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only /> </div> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/user-tag.vue b/packages/frontend/src/pages/user-tag.vue index d1dc721a4b..959d449e40 100644 --- a/packages/frontend/src/pages/user-tag.vue +++ b/packages/frontend/src/pages/user-tag.vue @@ -5,11 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader> - <MkSpacer :contentMax="1200"> + <div class="_spacer" style="--MI_SPACER-w: 1200px;"> <div class="_gaps_s"> <MkUserList :pagination="tagUsers"/> </div> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/user/achievements.vue b/packages/frontend/src/pages/user/achievements.vue index 8f13e959e1..d40998c307 100644 --- a/packages/frontend/src/pages/user/achievements.vue +++ b/packages/frontend/src/pages/user/achievements.vue @@ -4,9 +4,9 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="1200"> +<div class="_spacer" style="--MI_SPACER-w: 1200px;"> <MkAchievements :user="user" :withLocked="false" :withDescription="$i != null && (props.user.id === $i.id)"/> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/user/activity.vue b/packages/frontend/src/pages/user/activity.vue index 994bd52705..a49b82e630 100644 --- a/packages/frontend/src/pages/user/activity.vue +++ b/packages/frontend/src/pages/user/activity.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="700"> +<div class="_spacer" style="--MI_SPACER-w: 700px;"> <div class="_gaps"> <MkFoldableSection class="item"> <template #header><i class="ti ti-activity"></i> Heatmap</template> @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only <XPv :user="user"/> </MkFoldableSection> </div> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/user/clips.vue b/packages/frontend/src/pages/user/clips.vue index 38ce78e8d5..c980c83a26 100644 --- a/packages/frontend/src/pages/user/clips.vue +++ b/packages/frontend/src/pages/user/clips.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="700"> +<div class="_spacer" style="--MI_SPACER-w: 700px;"> <div> <MkPagination v-slot="{items}" ref="list" :pagination="pagination"> <MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" :class="$style.item" class="_panel _margin"> @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkA> </MkPagination> </div> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/user/files.vue b/packages/frontend/src/pages/user/files.vue index b6c7c1c777..91ebcad0b2 100644 --- a/packages/frontend/src/pages/user/files.vue +++ b/packages/frontend/src/pages/user/files.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <MkSpacer :contentMax="1100"> + <div class="_spacer" style="--MI_SPACER-w: 1100px;"> <div :class="$style.root"> <MkPagination v-slot="{items}" :pagination="pagination"> <div :class="$style.stream"> @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkPagination> </div> - </MkSpacer> + </div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/user/flashs.vue b/packages/frontend/src/pages/user/flashs.vue index b3313476e1..16957a5a2b 100644 --- a/packages/frontend/src/pages/user/flashs.vue +++ b/packages/frontend/src/pages/user/flashs.vue @@ -4,11 +4,11 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="700"> +<div class="_spacer" style="--MI_SPACER-w: 700px;"> <MkPagination v-slot="{items}" ref="list" :pagination="pagination"> <MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash" class="_margin"/> </MkPagination> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/user/followers.vue b/packages/frontend/src/pages/user/followers.vue index b8ba023f74..ae9765b60a 100644 --- a/packages/frontend/src/pages/user/followers.vue +++ b/packages/frontend/src/pages/user/followers.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs"> - <MkSpacer :contentMax="1000"> + <div class="_spacer" style="--MI_SPACER-w: 1000px;"> <Transition name="fade" mode="out-in"> <div v-if="user"> <XFollowList :user="user" type="followers"/> @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkError v-else-if="error" @retry="fetchUser()"/> <MkLoading v-else/> </Transition> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/user/following.vue b/packages/frontend/src/pages/user/following.vue index 1fe64c3042..8fd594c4e0 100644 --- a/packages/frontend/src/pages/user/following.vue +++ b/packages/frontend/src/pages/user/following.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <PageWithHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs"> - <MkSpacer :contentMax="1000"> + <div class="_spacer" style="--MI_SPACER-w: 1000px;"> <Transition name="fade" mode="out-in"> <div v-if="user"> <XFollowList :user="user" type="following"/> @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkError v-else-if="error" @retry="fetchUser()"/> <MkLoading v-else/> </Transition> - </MkSpacer> + </div> </PageWithHeader> </template> diff --git a/packages/frontend/src/pages/user/gallery.vue b/packages/frontend/src/pages/user/gallery.vue index 0bc5628528..11874bfd87 100644 --- a/packages/frontend/src/pages/user/gallery.vue +++ b/packages/frontend/src/pages/user/gallery.vue @@ -4,13 +4,13 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="700"> +<div class="_spacer" style="--MI_SPACER-w: 700px;"> <MkPagination v-slot="{items}" :pagination="pagination"> <div :class="$style.root"> <MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/> </div> </MkPagination> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index c057312f63..f15e4d7bcd 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="narrow ? 800 : 1100" :style="background" style="transform: none !important;"> +<div class="_spacer" :style="{ '--MI_SPACER-w': narrow ? '800px' : '1100px', ...background, transform: 'none !important;' }"> <div ref="rootEl" class="ftskorzw" :class="{ wide: !narrow }" style="container-type: inline-size;"> <div class="main _gaps"> <MkInfo v-if="user.isSuspended" :warn="true">{{ i18n.ts.userSuspended }}</MkInfo> @@ -192,7 +192,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> <div class="background"></div> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue index 17dd1d5f3c..38b1590a51 100644 --- a/packages/frontend/src/pages/user/index.vue +++ b/packages/frontend/src/pages/user/index.vue @@ -4,24 +4,22 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<PageWithHeader v-model:tab="tab" :displayBackButton="true" :tabs="headerTabs" :actions="headerActions"> +<PageWithHeader v-model:tab="tab" :displayBackButton="true" :tabs="headerTabs" :actions="headerActions" :swipable="true"> <div v-if="user"> - <MkSwiper v-model:tab="tab" :tabs="headerTabs"> - <XHome v-if="tab === 'home'" :user="user" @unfoldFiles="() => { tab = 'files'; }"/> - <MkSpacer v-else-if="tab === 'notes'" :contentMax="800" style="padding-top: 0"> - <XTimeline :user="user"/> - </MkSpacer> - <XFiles v-else-if="tab === 'files'" :user="user"/> - <XActivity v-else-if="tab === 'activity'" :user="user"/> - <XAchievements v-else-if="tab === 'achievements'" :user="user"/> - <XReactions v-else-if="tab === 'reactions'" :user="user"/> - <XClips v-else-if="tab === 'clips'" :user="user"/> - <XLists v-else-if="tab === 'lists'" :user="user"/> - <XPages v-else-if="tab === 'pages'" :user="user"/> - <XFlashs v-else-if="tab === 'flashs'" :user="user"/> - <XGallery v-else-if="tab === 'gallery'" :user="user"/> - <XRaw v-else-if="tab === 'raw'" :user="user"/> - </MkSwiper> + <XHome v-if="tab === 'home'" :user="user" @unfoldFiles="() => { tab = 'files'; }"/> + <div v-else-if="tab === 'notes'" class="_spacer" style="--MI_SPACER-w: 800px;"> + <XTimeline :user="user"/> + </div> + <XFiles v-else-if="tab === 'files'" :user="user"/> + <XActivity v-else-if="tab === 'activity'" :user="user"/> + <XAchievements v-else-if="tab === 'achievements'" :user="user"/> + <XReactions v-else-if="tab === 'reactions'" :user="user"/> + <XClips v-else-if="tab === 'clips'" :user="user"/> + <XLists v-else-if="tab === 'lists'" :user="user"/> + <XPages v-else-if="tab === 'pages'" :user="user"/> + <XFlashs v-else-if="tab === 'flashs'" :user="user"/> + <XGallery v-else-if="tab === 'gallery'" :user="user"/> + <XRaw v-else-if="tab === 'raw'" :user="user"/> </div> <MkError v-else-if="error" @retry="fetchUser()"/> <MkLoading v-else/> @@ -36,7 +34,6 @@ import { misskeyApi } from '@/utility/misskey-api.js'; import { definePage } from '@/page.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/i.js'; -import MkSwiper from '@/components/MkSwiper.vue'; import { serverContext, assertServerContext } from '@/server-context.js'; const XHome = defineAsyncComponent(() => import('./home.vue')); diff --git a/packages/frontend/src/pages/user/lists.vue b/packages/frontend/src/pages/user/lists.vue index a3d1974ced..d8ebea41c0 100644 --- a/packages/frontend/src/pages/user/lists.vue +++ b/packages/frontend/src/pages/user/lists.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkStickyContainer> - <MkSpacer :contentMax="700"> + <div class="_spacer" style="--MI_SPACER-w: 700px;"> <div> <MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="lists"> <MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/list/${ list.id }`"> @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkA> </MkPagination> </div> - </MkSpacer> + </div> </MkStickyContainer> </template> @@ -23,7 +23,6 @@ import {} from 'vue'; import * as Misskey from 'misskey-js'; import MkPagination from '@/components/MkPagination.vue'; import MkStickyContainer from '@/components/global/MkStickyContainer.vue'; -import MkSpacer from '@/components/global/MkSpacer.vue'; import MkAvatars from '@/components/MkAvatars.vue'; const props = defineProps<{ diff --git a/packages/frontend/src/pages/user/pages.vue b/packages/frontend/src/pages/user/pages.vue index 6375bf7d74..fe6141285e 100644 --- a/packages/frontend/src/pages/user/pages.vue +++ b/packages/frontend/src/pages/user/pages.vue @@ -4,11 +4,11 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="700"> +<div class="_spacer" style="--MI_SPACER-w: 700px;"> <MkPagination v-slot="{items}" ref="list" :pagination="pagination"> <MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_margin"/> </MkPagination> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/user/raw.vue b/packages/frontend/src/pages/user/raw.vue index f24a215afc..35dc00fbaf 100644 --- a/packages/frontend/src/pages/user/raw.vue +++ b/packages/frontend/src/pages/user/raw.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="600" :marginMin="16" :marginMax="32"> +<div class="_spacer" style="--MI_SPACER-w: 600px; --MI_SPACER-min: 16px; --MI_SPACER-max: 32px;"> <div class="_gaps_m"> <div :class="$style.userMInfoRoot"> <MkAvatar :class="$style.userMInfoAvatar" :user="user" indicator link preview/> @@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkObjectView tall :value="user"></MkObjectView> </FormSection> </div> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/user/reactions.vue b/packages/frontend/src/pages/user/reactions.vue index 7168778e12..9b7a3bc3bd 100644 --- a/packages/frontend/src/pages/user/reactions.vue +++ b/packages/frontend/src/pages/user/reactions.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="700"> +<div class="_spacer" style="--MI_SPACER-w: 700px;"> <MkPagination v-slot="{items}" ref="list" :pagination="pagination"> <div v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="_panel _margin"> <div :class="$style.header"> @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkNote :key="item.id" :note="item.note"/> </div> </MkPagination> -</MkSpacer> +</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index 6dd6c15ec5..58e26cb005 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -334,6 +334,9 @@ export const PREF_DEF = { showNavbarSubButtons: { default: true, }, + showTitlebar: { + default: false, + }, plugins: { default: [] as Plugin[], }, diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 36949e272e..0626779869 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -38,6 +38,10 @@ export const store = markRaw(new Pizzax('base', { where: 'account', default: false, }, + readDriveTip: { + where: 'account', + default: false, + }, memo: { where: 'account', default: null, diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index 304df91617..18e67eaa95 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -205,6 +205,31 @@ rt { text-align: center; } +/* TODO: 引数は現在CSS変数経由で受け取っているが、将来的にはattr()を使った方が綺麗そう */ +._spacer { + width: 100%; + max-width: min(var(--MI_SPACER-w, 100%), calc(100% - (var(--MI_SPACER-max, 24px) * 2))); + margin: var(--MI_SPACER-max, 24px) auto; + container-type: inline-size; + + /* 子に継承させない */ + --MI_SPACER-w: initial; + --MI_SPACER-min: initial; + --MI_SPACER-max: initial; +} + +._forceShrinkSpacer ._spacer { + max-width: min(var(--MI_SPACER-w, 100%), calc(100% - (var(--MI_SPACER-min, 12px) * 2))); + margin: var(--MI_SPACER-min, 12px) auto; +} + +@container (max-width: 450px) { + ._spacer { + max-width: min(var(--MI_SPACER-w, 100%), calc(100% - (var(--MI_SPACER-min, 12px) * 2))); + margin: var(--MI_SPACER-min, 12px) auto; + } +} + ._pageContainer { container-type: size; contain: strict; diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue index fd7a89dc22..14c4424e67 100644 --- a/packages/frontend/src/ui/_common_/common.vue +++ b/packages/frontend/src/ui/_common_/common.vue @@ -421,7 +421,7 @@ function getPointerEvents() { #devTicker { position: fixed; - top: 0; + bottom: 0; left: 0; z-index: 2147483647; color: #ff0; diff --git a/packages/frontend/src/ui/_common_/mobile-footer-menu.vue b/packages/frontend/src/ui/_common_/mobile-footer-menu.vue index 7c2de12221..88c6191e5a 100644 --- a/packages/frontend/src/ui/_common_/mobile-footer-menu.vue +++ b/packages/frontend/src/ui/_common_/mobile-footer-menu.vue @@ -79,10 +79,9 @@ watch(rootEl, () => { .root { position: relative; z-index: 1; - padding: 12px 12px max(12px, env(safe-area-inset-bottom, 0px)) 12px; + padding-bottom: env(safe-area-inset-bottom, 0px); display: grid; grid-template-columns: 1fr 1fr 1fr 1fr 1fr; - grid-gap: 8px; width: 100%; box-sizing: border-box; background: var(--MI_THEME-navBg); @@ -91,6 +90,16 @@ watch(rootEl, () => { } .item { + padding: 12px 0; + + &:first-child { + padding-left: 12px; + } + + &:last-child { + padding-right: 12px; + } + &.post { .itemInner { background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB)); @@ -112,7 +121,7 @@ watch(rootEl, () => { padding: 0; aspect-ratio: 1; width: 100%; - max-width: 45px; + max-width: 42px; margin: auto; align-content: center; border-radius: 100%; diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 94f333da41..f61e178bce 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -121,6 +121,7 @@ function more() { display: inline-block; width: 38px; aspect-ratio: 1; + border-radius: 8px; } .wideInstanceIcon { diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 2708683acb..6bf0dfc17c 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -7,7 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="[$style.root, { [$style.iconOnly]: iconOnly }]"> <div :class="$style.body"> <div :class="$style.top"> - <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"></div> <button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu"> <img :src="instance.sidebarLogoUrl && !iconOnly ? instance.sidebarLogoUrl : instance.iconUrl || '/favicon.ico'" alt="" :class="instance.sidebarLogoUrl && !iconOnly ? $style.wideInstanceIcon : $style.instanceIcon" style="viewTransitionName: navbar-serverIcon;"/> </button> @@ -183,12 +182,9 @@ function menuEdit() { } .body { - position: fixed; - top: 0; - left: 0; - z-index: 1001; + position: relative; width: var(--nav-icon-only-width); - height: 100dvh; + height: 100%; box-sizing: border-box; overflow: auto; overflow-x: clip; @@ -303,18 +299,6 @@ function menuEdit() { backdrop-filter: var(--MI-blur, blur(8px)); } - .banner { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-size: cover; - background-position: center center; - -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%); - mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%); - } - .instance { position: relative; display: block; @@ -335,6 +319,7 @@ function menuEdit() { display: inline-block; width: 38px; aspect-ratio: 1; + border-radius: 8px; } .wideInstanceIcon { @@ -566,6 +551,7 @@ function menuEdit() { display: inline-block; width: 30px; aspect-ratio: 1; + border-radius: 8px; } .bottom { diff --git a/packages/frontend/src/ui/_common_/titlebar.vue b/packages/frontend/src/ui/_common_/titlebar.vue new file mode 100644 index 0000000000..c62b13b73a --- /dev/null +++ b/packages/frontend/src/ui/_common_/titlebar.vue @@ -0,0 +1,87 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div :class="$style.root"> + <div :class="$style.title"> + <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> + <span :class="$style.instanceTitle">{{ instance.name ?? host }}</span> + </div> + <div :class="$style.controls"> + <span :class="$style.left"> + <button v-if="canBack" class="_button" :class="$style.button" @click="goBack"><i class="ti ti-arrow-left"></i></button> + </span> + <span :class="$style.right"> + </span> + </div> +</div> +</template> + +<script lang="ts" setup> +import { host } from '@@/js/config.js'; +import { ref } from 'vue'; +import { instance } from '@/instance.js'; +import { prefer } from '@/preferences.js'; + +const canBack = ref(true); + +function goBack() { + window.history.back(); +} +</script> + +<style lang="scss" module> +.root { + --height: 36px; + + background: var(--MI_THEME-navBg); + height: var(--height); + font-size: 90%; +} + +.title { + display: flex; + justify-content: center; + align-items: center; + text-align: center; + height: var(--height); +} + +.controls { + position: absolute; + top: 0; + left: 0; + display: flex; + justify-content: center; + align-items: center; + height: var(--height); +} + +.instanceIcon { + display: inline-block; + width: 20px; + aspect-ratio: 1; + border-radius: 5px; + margin-right: 8px; +} + +.instanceTitle { + display: inline-block; +} + +.left { + margin-right: auto; +} + +.right { + margin-left: auto; +} + +.button { + display: inline-block; + height: var(--height); + aspect-ratio: 1; +} +</style> diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index fbe86bc4cb..ed85bb3bc5 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -4,76 +4,80 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="[$style.root, { [$style.withWallpaper]: withWallpaper }]"> - <XSidebar v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'left'"/> +<div :class="[$style.root]"> + <XTitlebar v-if="prefer.r.showTitlebar.value" style="flex-shrink: 0;"/> - <div :class="$style.main"> - <XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/> + <div :class="$style.nonTitlebarArea"> + <XSidebar v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'left'"/> - <XAnnouncements v-if="$i"/> - <XStatusBars/> - <div :class="$style.columnsWrapper"> - <!-- passive: https://bugs.webkit.org/show_bug.cgi?id=281300 --> - <div ref="columnsEl" :class="[$style.columns, { [$style.center]: prefer.r['deck.columnAlign'].value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.passive.self="onWheel"> - <!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため --> - <section - v-for="ids in layout" - :class="$style.section" - :style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }" - @wheel.passive.self="onWheel" - > - <Suspense> - <component - :is="columnComponents[columns.find(c => c.id === id)!.type] ?? XTlColumn" - v-for="id in ids" - :ref="id" - :key="id" - :class="[$style.column, { '_shadow': withWallpaper }]" - :column="columns.find(c => c.id === id)!" - :isStacked="ids.length > 1" - @headerWheel="onWheel" - /> - <template #fallback> - <MkLoading/> - </template> - </Suspense> - </section> - <div v-if="layout.length === 0" class="_panel" :class="$style.onboarding"> - <div>{{ i18n.ts._deck.introduction }}</div> - <div>{{ i18n.ts._deck.introduction2 }}</div> + <div :class="[$style.main, { [$style.withWallpaper]: withWallpaper, [$style.withSidebarAndTitlebar]: !isMobile && prefer.r['deck.navbarPosition'].value === 'left' && prefer.r.showTitlebar.value }]" :style="{ backgroundImage: prefer.s['deck.wallpaper'] != null ? `url(${ prefer.s['deck.wallpaper'] })` : null }"> + <XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/> + + <XAnnouncements v-if="$i"/> + <XStatusBars/> + <div :class="$style.columnsWrapper"> + <!-- passive: https://bugs.webkit.org/show_bug.cgi?id=281300 --> + <div ref="columnsEl" :class="[$style.columns, { [$style.center]: prefer.r['deck.columnAlign'].value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.passive.self="onWheel"> + <!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため --> + <section + v-for="ids in layout" + :class="$style.section" + :style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }" + @wheel.passive.self="onWheel" + > + <Suspense> + <component + :is="columnComponents[columns.find(c => c.id === id)!.type] ?? XTlColumn" + v-for="id in ids" + :ref="id" + :key="id" + :class="[$style.column, { '_shadow': withWallpaper }]" + :column="columns.find(c => c.id === id)!" + :isStacked="ids.length > 1" + @headerWheel="onWheel" + /> + <template #fallback> + <MkLoading/> + </template> + </Suspense> + </section> + <div v-if="layout.length === 0" class="_panel" :class="$style.onboarding"> + <div>{{ i18n.ts._deck.introduction }}</div> + <div>{{ i18n.ts._deck.introduction2 }}</div> + </div> + </div> + + <div v-if="prefer.r['deck.menuPosition'].value === 'right'" :class="$style.sideMenu"> + <div :class="$style.sideMenuTop"> + <button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.sideMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button> + <button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.sideMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button> + </div> + <div :class="$style.sideMenuMiddle"> + <button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.sideMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button> + </div> + <div :class="$style.sideMenuBottom"> + <button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.sideMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button> + </div> </div> </div> - <div v-if="prefer.r['deck.menuPosition'].value === 'right'" :class="$style.sideMenu"> - <div :class="$style.sideMenuTop"> - <button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.sideMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button> - <button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.sideMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button> + <div v-if="prefer.r['deck.menuPosition'].value === 'bottom'" :class="$style.bottomMenu"> + <div :class="$style.bottomMenuLeft"> + <button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.bottomMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button> + <button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.bottomMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button> </div> - <div :class="$style.sideMenuMiddle"> - <button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.sideMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button> + <div :class="$style.bottomMenuMiddle"> + <button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.bottomMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button> </div> - <div :class="$style.sideMenuBottom"> - <button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.sideMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button> + <div :class="$style.bottomMenuRight"> + <button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.bottomMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button> </div> </div> - </div> - - <div v-if="prefer.r['deck.menuPosition'].value === 'bottom'" :class="$style.bottomMenu"> - <div :class="$style.bottomMenuLeft"> - <button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.bottomMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button> - <button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.bottomMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button> - </div> - <div :class="$style.bottomMenuMiddle"> - <button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.bottomMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button> - </div> - <div :class="$style.bottomMenuRight"> - <button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.bottomMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button> - </div> - </div> - <XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'bottom'"/> + <XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'bottom'"/> - <XMobileFooterMenu v-if="isMobile" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/> + <XMobileFooterMenu v-if="isMobile" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/> + </div> </div> <XCommon v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/> @@ -87,6 +91,7 @@ import XCommon from './_common_/common.vue'; import XSidebar from '@/ui/_common_/navbar.vue'; import XNavbarH from '@/ui/_common_/navbar-h.vue'; import XMobileFooterMenu from '@/ui/_common_/mobile-footer-menu.vue'; +import XTitlebar from '@/ui/_common_/titlebar.vue'; import * as os from '@/os.js'; import { $i } from '@/i.js'; import { i18n } from '@/i18n.js'; @@ -216,30 +221,26 @@ async function deleteProfile() { window.document.documentElement.style.overflowY = 'hidden'; window.document.documentElement.style.scrollBehavior = 'auto'; - -if (prefer.s['deck.wallpaper'] != null) { - window.document.documentElement.style.backgroundImage = `url(${prefer.s['deck.wallpaper']})`; -} </script> <style lang="scss" module> .root { - $nav-hide-threshold: 650px; // TODO: どこかに集約したい - --MI-margin: var(--MI-marginHalf); --columnGap: v-bind("gap + 'px'"); display: flex; + flex-direction: column; height: 100dvh; box-sizing: border-box; flex: 1; + background: var(--MI_THEME-navBg); +} - &.withWallpaper { - .main { - background: transparent; - } - } +.nonTitlebarArea { + display: flex; + flex: 1; + min-height: 0; } .main { @@ -247,7 +248,15 @@ if (prefer.s['deck.wallpaper'] != null) { min-width: 0; display: flex; flex-direction: column; - background: var(--MI_THEME-deckBg); + + &:not(.withWallpaper) { + background: var(--MI_THEME-deckBg); + } + + &.withSidebarAndTitlebar { + border-radius: 12px 0 0 0; + overflow: clip; + } } .columnsWrapper { diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue index 4c816f1544..62c59468bd 100644 --- a/packages/frontend/src/ui/deck/column.vue +++ b/packages/frontend/src/ui/deck/column.vue @@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div + class="_forceShrinkSpacer" :class="[$style.root, { [$style.paged]: isMainColumn, [$style.naked]: naked, [$style.active]: active, [$style.draghover]: draghover, [$style.dragging]: dragging, [$style.dropready]: dropready, [$style.withWallpaper]: withWallpaper }]" @dragover.prevent.stop="onDragover" @dragleave="onDragleave" @@ -53,7 +54,6 @@ import { DI } from '@/di.js'; provide('shouldHeaderThin', true); provide('shouldOmitHeaderTitle', true); -provide(DI.forceSpacerMin, true); const withWallpaper = prefer.s['deck.wallpaper'] != null; diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 940cf72e28..fa2343ba27 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -4,22 +4,26 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="$style.root"> - <XSidebar v-if="!isMobile" :class="$style.sidebar" :showWidgetButton="!isDesktop" @widgetButtonClick="widgetsShowing = true"/> +<div :class="[$style.root, { '_forceShrinkSpacer': deviceKind === 'smartphone' }]"> + <XTitlebar v-if="prefer.r.showTitlebar.value" style="flex-shrink: 0;"/> - <div :class="$style.contents" @contextmenu.stop="onContextmenu"> - <div> - <XPreferenceRestore v-if="shouldSuggestRestoreBackup"/> - <XAnnouncements v-if="$i"/> - <XStatusBars :class="$style.statusbars"/> + <div :class="$style.nonTitlebarArea"> + <XSidebar v-if="!isMobile" :class="$style.sidebar" :showWidgetButton="!isDesktop" @widgetButtonClick="widgetsShowing = true"/> + + <div :class="[$style.contents, !isMobile && prefer.r.showTitlebar.value ? $style.withSidebarAndTitlebar : null]" @contextmenu.stop="onContextmenu"> + <div> + <XPreferenceRestore v-if="shouldSuggestRestoreBackup"/> + <XAnnouncements v-if="$i"/> + <XStatusBars :class="$style.statusbars"/> + </div> + <StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :class="$style.content"/> + <RouterView v-else :class="$style.content"/> + <XMobileFooterMenu v-if="isMobile" ref="navFooter" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/> </div> - <StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :class="$style.content"/> - <RouterView v-else :class="$style.content"/> - <XMobileFooterMenu v-if="isMobile" ref="navFooter" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/> - </div> - <div v-if="isDesktop && !pageMetadata?.needWideArea" :class="$style.widgets"> - <XWidgets/> + <div v-if="isDesktop && !pageMetadata?.needWideArea" :class="$style.widgets"> + <XWidgets/> + </div> </div> <XCommon v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/> @@ -34,6 +38,7 @@ import XCommon from './_common_/common.vue'; import type { PageMetadata } from '@/page.js'; import XMobileFooterMenu from '@/ui/_common_/mobile-footer-menu.vue'; import XPreferenceRestore from '@/ui/_common_/PreferenceRestore.vue'; +import XTitlebar from '@/ui/_common_/titlebar.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/i.js'; @@ -128,8 +133,15 @@ $widgets-hide-threshold: 1090px; height: 100dvh; overflow: clip; contain: strict; - box-sizing: border-box; display: flex; + flex-direction: column; + background: var(--MI_THEME-navBg); +} + +.nonTitlebarArea { + display: flex; + flex: 1; + min-height: 0; } .sidebar { @@ -142,7 +154,12 @@ $widgets-hide-threshold: 1090px; flex: 1; height: 100%; min-width: 0; - background: var(--MI_THEME-bg); + + &.withSidebarAndTitlebar { + background: var(--MI_THEME-navBg); + border-radius: 12px 0 0 0; + overflow: clip; + } } .content { diff --git a/packages/frontend/src/utility/upload.ts b/packages/frontend/src/utility/upload.ts index b43fea8e15..03240749e9 100644 --- a/packages/frontend/src/utility/upload.ts +++ b/packages/frontend/src/utility/upload.ts @@ -40,7 +40,7 @@ export function uploadFile( const _folder = typeof folder === 'string' ? folder : folder?.id; - if (file.size > instance.maxFileSize) { + if ((file.size > instance.maxFileSize) || (file.size > ($i.policies.maxFileSizeMb * 1024 * 1024))) { alert({ type: 'error', title: i18n.ts.failedToUpload, diff --git a/packages/megalodon/package.json b/packages/megalodon/package.json index 8b66d567f5..1ceb47759d 100644 --- a/packages/megalodon/package.json +++ b/packages/megalodon/package.json @@ -64,26 +64,25 @@ "@types/ws": "^8.5.10", "axios": "1.7.4", "dayjs": "^1.11.10", - "form-data": "^4.0.0", + "form-data": "4.0.2", "https-proxy-agent": "^7.0.2", - "oauth": "^0.10.0", + "oauth": "0.10.2", "object-assign-deep": "^0.4.0", "parse-link-header": "^2.0.0", "socks-proxy-agent": "^8.0.2", - "typescript": "5.1.6", - "uuid": "^9.0.1", + "typescript": "5.8.3", + "uuid": "11.1.0", "ws": "8.17.1" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "8.27.0", - "@typescript-eslint/parser": "8.27.0", - "eslint": "9.22.0", + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", + "eslint": "9.25.1", "eslint-config-prettier": "^9.0.0", - "jest": "^29.7.0", + "jest": "29.7.0", "jest-worker": "^29.7.0", "lodash": "4.17.21", - "prettier": "^3.1.0", - "ts-jest": "^29.1.1", - "typedoc": "^0.25.3" + "prettier": "3.5.3", + "ts-jest": "^29.1.1" } } diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json index 4f0c5b2577..522aa18d1d 100644 --- a/packages/misskey-bubble-game/package.json +++ b/packages/misskey-bubble-game/package.json @@ -24,14 +24,14 @@ "devDependencies": { "@types/matter-js": "0.19.8", "@types/seedrandom": "3.0.8", - "@types/node": "22.14.0", - "@typescript-eslint/eslint-plugin": "8.29.1", - "@typescript-eslint/parser": "8.29.1", - "nodemon": "3.1.9", + "@types/node": "22.15.2", + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", + "nodemon": "3.1.10", "execa": "9.5.2", "typescript": "5.8.3", - "esbuild": "0.25.2", - "glob": "11.0.1" + "esbuild": "0.25.3", + "glob": "11.0.2" }, "files": [ "built" diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index b2a4fb9cab..18cb070af5 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1087,15 +1087,9 @@ type ChatMessagesCreateToUserResponse = operations['chat___messages___create-to- type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json']; // @public (undocumented) -type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json']; - -// @public (undocumented) type ChatMessagesReactRequest = operations['chat___messages___react']['requestBody']['content']['application/json']; // @public (undocumented) -type ChatMessagesReactResponse = operations['chat___messages___react']['responses']['200']['content']['application/json']; - -// @public (undocumented) type ChatMessagesRoomTimelineRequest = operations['chat___messages___room-timeline']['requestBody']['content']['application/json']; // @public (undocumented) @@ -1117,9 +1111,6 @@ type ChatMessagesShowResponse = operations['chat___messages___show']['responses' type ChatMessagesUnreactRequest = operations['chat___messages___unreact']['requestBody']['content']['application/json']; // @public (undocumented) -type ChatMessagesUnreactResponse = operations['chat___messages___unreact']['responses']['200']['content']['application/json']; - -// @public (undocumented) type ChatMessagesUserTimelineRequest = operations['chat___messages___user-timeline']['requestBody']['content']['application/json']; // @public (undocumented) @@ -1144,9 +1135,6 @@ type ChatRoomsCreateResponse = operations['chat___rooms___create']['responses'][ type ChatRoomsDeleteRequest = operations['chat___rooms___delete']['requestBody']['content']['application/json']; // @public (undocumented) -type ChatRoomsDeleteResponse = operations['chat___rooms___delete']['responses']['200']['content']['application/json']; - -// @public (undocumented) type ChatRoomsInvitationsCreateRequest = operations['chat___rooms___invitations___create']['requestBody']['content']['application/json']; // @public (undocumented) @@ -1156,9 +1144,6 @@ type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invitations___ignore']['requestBody']['content']['application/json']; // @public (undocumented) -type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json']; - -// @public (undocumented) type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json']; // @public (undocumented) @@ -1180,15 +1165,9 @@ type ChatRoomsJoiningResponse = operations['chat___rooms___joining']['responses' type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json']; // @public (undocumented) -type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json']; - -// @public (undocumented) type ChatRoomsLeaveRequest = operations['chat___rooms___leave']['requestBody']['content']['application/json']; // @public (undocumented) -type ChatRoomsLeaveResponse = operations['chat___rooms___leave']['responses']['200']['content']['application/json']; - -// @public (undocumented) type ChatRoomsMembersRequest = operations['chat___rooms___members']['requestBody']['content']['application/json']; // @public (undocumented) @@ -1198,9 +1177,6 @@ type ChatRoomsMembersResponse = operations['chat___rooms___members']['responses' type ChatRoomsMuteRequest = operations['chat___rooms___mute']['requestBody']['content']['application/json']; // @public (undocumented) -type ChatRoomsMuteResponse = operations['chat___rooms___mute']['responses']['200']['content']['application/json']; - -// @public (undocumented) type ChatRoomsOwnedRequest = operations['chat___rooms___owned']['requestBody']['content']['application/json']; // @public (undocumented) @@ -1736,9 +1712,7 @@ declare namespace entities { ChatMessagesCreateToUserRequest, ChatMessagesCreateToUserResponse, ChatMessagesDeleteRequest, - ChatMessagesDeleteResponse, ChatMessagesReactRequest, - ChatMessagesReactResponse, ChatMessagesRoomTimelineRequest, ChatMessagesRoomTimelineResponse, ChatMessagesSearchRequest, @@ -1746,31 +1720,25 @@ declare namespace entities { ChatMessagesShowRequest, ChatMessagesShowResponse, ChatMessagesUnreactRequest, - ChatMessagesUnreactResponse, ChatMessagesUserTimelineRequest, ChatMessagesUserTimelineResponse, ChatRoomsCreateRequest, ChatRoomsCreateResponse, ChatRoomsDeleteRequest, - ChatRoomsDeleteResponse, ChatRoomsInvitationsCreateRequest, ChatRoomsInvitationsCreateResponse, ChatRoomsInvitationsIgnoreRequest, - ChatRoomsInvitationsIgnoreResponse, ChatRoomsInvitationsInboxRequest, ChatRoomsInvitationsInboxResponse, ChatRoomsInvitationsOutboxRequest, ChatRoomsInvitationsOutboxResponse, ChatRoomsJoinRequest, - ChatRoomsJoinResponse, ChatRoomsJoiningRequest, ChatRoomsJoiningResponse, ChatRoomsLeaveRequest, - ChatRoomsLeaveResponse, ChatRoomsMembersRequest, ChatRoomsMembersResponse, ChatRoomsMuteRequest, - ChatRoomsMuteResponse, ChatRoomsOwnedRequest, ChatRoomsOwnedResponse, ChatRoomsShowRequest, diff --git a/packages/misskey-js/generator/package.json b/packages/misskey-js/generator/package.json index b63ddd3e58..f45d5c49eb 100644 --- a/packages/misskey-js/generator/package.json +++ b/packages/misskey-js/generator/package.json @@ -8,15 +8,15 @@ }, "devDependencies": { "@readme/openapi-parser": "2.7.0", - "@types/node": "22.13.15", - "@typescript-eslint/eslint-plugin": "8.29.0", - "@typescript-eslint/parser": "8.29.0", - "eslint": "9.22.0", + "@types/node": "22.15.2", + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", + "eslint": "9.25.1", "openapi-types": "12.1.3", "openapi-typescript": "6.7.6", "ts-case-convert": "2.1.0", "tsx": "4.19.3", - "typescript": "5.8.2" + "typescript": "5.8.3" }, "files": [ "built" diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 8b52924b29..e7e9737897 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.4.1-alpha.2", + "version": "2025.4.1-beta.9", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", @@ -35,23 +35,23 @@ "directory": "packages/misskey-js" }, "devDependencies": { - "@microsoft/api-extractor": "7.52.2", - "@swc/jest": "0.2.37", + "@microsoft/api-extractor": "7.52.5", + "@swc/jest": "0.2.38", "@types/jest": "29.5.14", - "@types/node": "22.13.15", - "@typescript-eslint/eslint-plugin": "8.29.0", - "@typescript-eslint/parser": "8.29.0", + "@types/node": "22.15.2", + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", "jest": "29.7.0", "jest-fetch-mock": "3.0.3", "jest-websocket-mock": "2.5.0", "mock-socket": "9.3.1", "ncp": "2.0.0", - "nodemon": "3.1.9", + "nodemon": "3.1.10", "execa": "8.0.1", - "tsd": "0.31.2", - "typescript": "5.8.2", - "esbuild": "0.25.2", - "glob": "11.0.1" + "tsd": "0.32.0", + "typescript": "5.8.3", + "esbuild": "0.25.3", + "glob": "11.0.2" }, "files": [ "built" diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 6c66c4beb0..9293a5e950 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -228,9 +228,7 @@ import type { ChatMessagesCreateToUserRequest, ChatMessagesCreateToUserResponse, ChatMessagesDeleteRequest, - ChatMessagesDeleteResponse, ChatMessagesReactRequest, - ChatMessagesReactResponse, ChatMessagesRoomTimelineRequest, ChatMessagesRoomTimelineResponse, ChatMessagesSearchRequest, @@ -238,31 +236,25 @@ import type { ChatMessagesShowRequest, ChatMessagesShowResponse, ChatMessagesUnreactRequest, - ChatMessagesUnreactResponse, ChatMessagesUserTimelineRequest, ChatMessagesUserTimelineResponse, ChatRoomsCreateRequest, ChatRoomsCreateResponse, ChatRoomsDeleteRequest, - ChatRoomsDeleteResponse, ChatRoomsInvitationsCreateRequest, ChatRoomsInvitationsCreateResponse, ChatRoomsInvitationsIgnoreRequest, - ChatRoomsInvitationsIgnoreResponse, ChatRoomsInvitationsInboxRequest, ChatRoomsInvitationsInboxResponse, ChatRoomsInvitationsOutboxRequest, ChatRoomsInvitationsOutboxResponse, ChatRoomsJoinRequest, - ChatRoomsJoinResponse, ChatRoomsJoiningRequest, ChatRoomsJoiningResponse, ChatRoomsLeaveRequest, - ChatRoomsLeaveResponse, ChatRoomsMembersRequest, ChatRoomsMembersResponse, ChatRoomsMuteRequest, - ChatRoomsMuteResponse, ChatRoomsOwnedRequest, ChatRoomsOwnedResponse, ChatRoomsShowRequest, @@ -824,24 +816,24 @@ export type Endpoints = { 'chat/history': { req: ChatHistoryRequest; res: ChatHistoryResponse }; 'chat/messages/create-to-room': { req: ChatMessagesCreateToRoomRequest; res: ChatMessagesCreateToRoomResponse }; 'chat/messages/create-to-user': { req: ChatMessagesCreateToUserRequest; res: ChatMessagesCreateToUserResponse }; - 'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: ChatMessagesDeleteResponse }; - 'chat/messages/react': { req: ChatMessagesReactRequest; res: ChatMessagesReactResponse }; + 'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: EmptyResponse }; + 'chat/messages/react': { req: ChatMessagesReactRequest; res: EmptyResponse }; 'chat/messages/room-timeline': { req: ChatMessagesRoomTimelineRequest; res: ChatMessagesRoomTimelineResponse }; 'chat/messages/search': { req: ChatMessagesSearchRequest; res: ChatMessagesSearchResponse }; 'chat/messages/show': { req: ChatMessagesShowRequest; res: ChatMessagesShowResponse }; - 'chat/messages/unreact': { req: ChatMessagesUnreactRequest; res: ChatMessagesUnreactResponse }; + 'chat/messages/unreact': { req: ChatMessagesUnreactRequest; res: EmptyResponse }; 'chat/messages/user-timeline': { req: ChatMessagesUserTimelineRequest; res: ChatMessagesUserTimelineResponse }; 'chat/rooms/create': { req: ChatRoomsCreateRequest; res: ChatRoomsCreateResponse }; - 'chat/rooms/delete': { req: ChatRoomsDeleteRequest; res: ChatRoomsDeleteResponse }; + 'chat/rooms/delete': { req: ChatRoomsDeleteRequest; res: EmptyResponse }; 'chat/rooms/invitations/create': { req: ChatRoomsInvitationsCreateRequest; res: ChatRoomsInvitationsCreateResponse }; - 'chat/rooms/invitations/ignore': { req: ChatRoomsInvitationsIgnoreRequest; res: ChatRoomsInvitationsIgnoreResponse }; + 'chat/rooms/invitations/ignore': { req: ChatRoomsInvitationsIgnoreRequest; res: EmptyResponse }; 'chat/rooms/invitations/inbox': { req: ChatRoomsInvitationsInboxRequest; res: ChatRoomsInvitationsInboxResponse }; 'chat/rooms/invitations/outbox': { req: ChatRoomsInvitationsOutboxRequest; res: ChatRoomsInvitationsOutboxResponse }; - 'chat/rooms/join': { req: ChatRoomsJoinRequest; res: ChatRoomsJoinResponse }; + 'chat/rooms/join': { req: ChatRoomsJoinRequest; res: EmptyResponse }; 'chat/rooms/joining': { req: ChatRoomsJoiningRequest; res: ChatRoomsJoiningResponse }; - 'chat/rooms/leave': { req: ChatRoomsLeaveRequest; res: ChatRoomsLeaveResponse }; + 'chat/rooms/leave': { req: ChatRoomsLeaveRequest; res: EmptyResponse }; 'chat/rooms/members': { req: ChatRoomsMembersRequest; res: ChatRoomsMembersResponse }; - 'chat/rooms/mute': { req: ChatRoomsMuteRequest; res: ChatRoomsMuteResponse }; + 'chat/rooms/mute': { req: ChatRoomsMuteRequest; res: EmptyResponse }; 'chat/rooms/owned': { req: ChatRoomsOwnedRequest; res: ChatRoomsOwnedResponse }; 'chat/rooms/show': { req: ChatRoomsShowRequest; res: ChatRoomsShowResponse }; 'chat/rooms/update': { req: ChatRoomsUpdateRequest; res: ChatRoomsUpdateResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index 404746b3f2..f71407a6ae 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -231,9 +231,7 @@ export type ChatMessagesCreateToRoomResponse = operations['chat___messages___cre export type ChatMessagesCreateToUserRequest = operations['chat___messages___create-to-user']['requestBody']['content']['application/json']; export type ChatMessagesCreateToUserResponse = operations['chat___messages___create-to-user']['responses']['200']['content']['application/json']; export type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json']; -export type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json']; export type ChatMessagesReactRequest = operations['chat___messages___react']['requestBody']['content']['application/json']; -export type ChatMessagesReactResponse = operations['chat___messages___react']['responses']['200']['content']['application/json']; export type ChatMessagesRoomTimelineRequest = operations['chat___messages___room-timeline']['requestBody']['content']['application/json']; export type ChatMessagesRoomTimelineResponse = operations['chat___messages___room-timeline']['responses']['200']['content']['application/json']; export type ChatMessagesSearchRequest = operations['chat___messages___search']['requestBody']['content']['application/json']; @@ -241,31 +239,25 @@ export type ChatMessagesSearchResponse = operations['chat___messages___search'][ export type ChatMessagesShowRequest = operations['chat___messages___show']['requestBody']['content']['application/json']; export type ChatMessagesShowResponse = operations['chat___messages___show']['responses']['200']['content']['application/json']; export type ChatMessagesUnreactRequest = operations['chat___messages___unreact']['requestBody']['content']['application/json']; -export type ChatMessagesUnreactResponse = operations['chat___messages___unreact']['responses']['200']['content']['application/json']; export type ChatMessagesUserTimelineRequest = operations['chat___messages___user-timeline']['requestBody']['content']['application/json']; export type ChatMessagesUserTimelineResponse = operations['chat___messages___user-timeline']['responses']['200']['content']['application/json']; export type ChatRoomsCreateRequest = operations['chat___rooms___create']['requestBody']['content']['application/json']; export type ChatRoomsCreateResponse = operations['chat___rooms___create']['responses']['200']['content']['application/json']; export type ChatRoomsDeleteRequest = operations['chat___rooms___delete']['requestBody']['content']['application/json']; -export type ChatRoomsDeleteResponse = operations['chat___rooms___delete']['responses']['200']['content']['application/json']; export type ChatRoomsInvitationsCreateRequest = operations['chat___rooms___invitations___create']['requestBody']['content']['application/json']; export type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations___create']['responses']['200']['content']['application/json']; export type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invitations___ignore']['requestBody']['content']['application/json']; -export type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json']; export type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json']; export type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json']; export type ChatRoomsInvitationsOutboxRequest = operations['chat___rooms___invitations___outbox']['requestBody']['content']['application/json']; export type ChatRoomsInvitationsOutboxResponse = operations['chat___rooms___invitations___outbox']['responses']['200']['content']['application/json']; export type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json']; -export type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json']; export type ChatRoomsJoiningRequest = operations['chat___rooms___joining']['requestBody']['content']['application/json']; export type ChatRoomsJoiningResponse = operations['chat___rooms___joining']['responses']['200']['content']['application/json']; export type ChatRoomsLeaveRequest = operations['chat___rooms___leave']['requestBody']['content']['application/json']; -export type ChatRoomsLeaveResponse = operations['chat___rooms___leave']['responses']['200']['content']['application/json']; export type ChatRoomsMembersRequest = operations['chat___rooms___members']['requestBody']['content']['application/json']; export type ChatRoomsMembersResponse = operations['chat___rooms___members']['responses']['200']['content']['application/json']; export type ChatRoomsMuteRequest = operations['chat___rooms___mute']['requestBody']['content']['application/json']; -export type ChatRoomsMuteResponse = operations['chat___rooms___mute']['responses']['200']['content']['application/json']; export type ChatRoomsOwnedRequest = operations['chat___rooms___owned']['requestBody']['content']['application/json']; export type ChatRoomsOwnedResponse = operations['chat___rooms___owned']['responses']['200']['content']['application/json']; export type ChatRoomsShowRequest = operations['chat___rooms___show']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 46b9972bbb..b96cec5e7b 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -5487,6 +5487,7 @@ export type components = { canUseTranslator: boolean; canHideAds: boolean; driveCapacityMb: number; + maxFileSizeMb: number; alwaysMarkNsfw: boolean; canUpdateBioMedia: boolean; pinLimit: number; @@ -15634,11 +15635,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -15689,11 +15688,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -15917,11 +15914,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -16091,11 +16086,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -16207,11 +16200,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -16379,11 +16370,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -16491,11 +16480,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { @@ -16606,11 +16593,9 @@ export type operations = { }; }; responses: { - /** @description OK (with results) */ - 200: { - content: { - 'application/json': unknown; - }; + /** @description OK (without any results) */ + 204: { + content: never; }; /** @description Client error */ 400: { diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json index 7e12bc4a23..b6f30a1320 100644 --- a/packages/misskey-reversi/package.json +++ b/packages/misskey-reversi/package.json @@ -22,14 +22,14 @@ "lint": "pnpm typecheck && pnpm eslint" }, "devDependencies": { - "@types/node": "22.14.0", - "@typescript-eslint/eslint-plugin": "8.29.1", - "@typescript-eslint/parser": "8.29.1", + "@types/node": "22.15.2", + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", "execa": "9.5.2", - "nodemon": "3.1.9", + "nodemon": "3.1.10", "typescript": "5.8.3", - "esbuild": "0.25.2", - "glob": "11.0.1" + "esbuild": "0.25.3", + "glob": "11.0.2" }, "files": [ "built" diff --git a/packages/sw/package.json b/packages/sw/package.json index 1a493e916c..502b05238b 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -9,15 +9,15 @@ "lint": "pnpm typecheck && pnpm eslint" }, "dependencies": { - "esbuild": "0.25.2", + "esbuild": "0.25.3", "idb-keyval": "6.2.1", "misskey-js": "workspace:*" }, "devDependencies": { - "@typescript-eslint/parser": "8.29.1", + "@typescript-eslint/parser": "8.31.0", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74", "eslint-plugin-import": "2.31.0", - "nodemon": "3.1.9", + "nodemon": "3.1.10", "typescript": "5.8.3" }, "type": "module" |