diff --git a/.forgejo/ISSUE_TEMPLATE/02_feature-request.md b/.forgejo/ISSUE_TEMPLATE/02_feature-request.md new file mode 100644 index 0000000000..db73f0dc7f --- /dev/null +++ b/.forgejo/ISSUE_TEMPLATE/02_feature-request.md @@ -0,0 +1,10 @@ +--- +name: "Feature Request" +about: "Suggest an idea for this project" +title: "feat: " +--- +## Summary + + +## Purpose + \ No newline at end of file diff --git a/.forgejo/pull_request_template.md b/.forgejo/pull_request_template.md new file mode 100644 index 0000000000..63eb2ab623 --- /dev/null +++ b/.forgejo/pull_request_template.md @@ -0,0 +1,24 @@ + + +## What + + + +## Why + + + +## Additional info (optional) + + + +## Checklist +- [ ] Read the [contribution guide](https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md) +- [ ] Test working in a local environment +- [ ] (If needed) Add story of storybook +- [ ] (If needed) Update CHANGELOG.md +- [ ] (If possible) Add tests \ No newline at end of file diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index fa4a58c3a9..88e2aceaed 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -11,6 +11,6 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v4 + - uses: actions/labeler@v5 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index d10d0fcafd..1f9238780f 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -34,7 +34,7 @@ jobs: node-version: ${{ matrix.node-version }} - name: Setup Python - uses: actions/setup-python@v4.7.1 + uses: actions/setup-python@v5.0.0 with: python-version: ${{ matrix.python-version }} diff --git a/Dockerfile b/Dockerfile index 66018a01c8..440c04e2df 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,101 +1,62 @@ # syntax = docker/dockerfile:1.4 -ARG NODE_VERSION=20.5.1-bullseye +ARG NODE_VERSION=21.4.0-alpine3.18 -# build assets & compile TypeScript - -FROM --platform=$BUILDPLATFORM node:${NODE_VERSION} AS native-builder - -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt,sharing=locked \ - rm -f /etc/apt/apt.conf.d/docker-clean \ - ; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache \ - && apt-get update \ - && apt-get install -yqq --no-install-recommends \ - build-essential curl ca-certificates - -ARG TARGETARCH - -RUN curl -L https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-$TARGETARCH-static.tar.xz -o /ffmpeg.tar.xz \ - && tar xvf /ffmpeg.tar.xz -C / --strip-components 1 --wildcards 'ffmpeg-*-static/ffmpeg' 'ffmpeg-*-static/ffprobe' +FROM node:${NODE_VERSION} as build RUN corepack enable WORKDIR /sharkey -COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] -COPY --link ["scripts", "./scripts"] -COPY --link ["packages/megalodon/package.json", "./packages/megalodon/"] -COPY --link ["packages/backend/package.json", "./packages/backend/"] -COPY --link ["packages/frontend/package.json", "./packages/frontend/"] -COPY --link ["packages/sw/package.json", "./packages/sw/"] -COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"] - -RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ - pnpm i --frozen-lockfile --aggregate-output +RUN apk add git linux-headers build-base -COPY --link . ./ +ENV PYTHONUNBUFFERED=1 +RUN apk add --update python3 && ln -sf python3 /usr/bin/python +RUN python3 -m ensurepip +RUN pip3 install --no-cache --upgrade pip setuptools -ARG NODE_ENV=production +COPY . ./ -RUN git submodule update --init +RUN git submodule update --init --recursive +RUN pnpm config set fetch-retries 5 +RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ + pnpm i RUN pnpm build -RUN rm -rf .git/ - -# build native dependencies for target platform - -FROM --platform=$TARGETPLATFORM node:${NODE_VERSION} AS target-builder - -RUN apt-get update \ - && apt-get install -yqq --no-install-recommends \ - build-essential - -RUN corepack enable - -WORKDIR /sharkey - -COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] -COPY --link ["scripts", "./scripts"] -COPY --link ["packages/megalodon/package.json", "./packages/megalodon/"] -COPY --link ["packages/backend/package.json", "./packages/backend/"] - +RUN node scripts/trim-deps.mjs +RUN mv packages/frontend/assets sharkey-assets +RUN rm -r node_modules packages/frontend packages/sw RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ - pnpm i --frozen-lockfile --aggregate-output - -FROM --platform=$TARGETPLATFORM node:${NODE_VERSION}-slim AS runner + pnpm i --prod +RUN rm -rf .git -ARG UID="991" -ARG GID="991" +FROM node:${NODE_VERSION} -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - tini curl libjemalloc-dev libjemalloc2 \ - && ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so \ - && corepack enable \ - && groupadd -g "${GID}" sharkey \ - && useradd -l -u "${UID}" -g "${GID}" -m -d /sharkey sharkey \ - && find / -type d -path /proc -prune -o -type f -perm /u+s -ignore_readdir_race -exec chmod u-s {} \; \ - && find / -type d -path /proc -prune -o -type f -perm /g+s -ignore_readdir_race -exec chmod g-s {} \; \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists - -USER sharkey WORKDIR /sharkey -COPY --chown=sharkey:sharkey --from=target-builder /sharkey/node_modules ./node_modules -COPY --chown=sharkey:sharkey --from=target-builder /sharkey/packages/megalodon/node_modules ./packages/megalodon/node_modules -COPY --chown=sharkey:sharkey --from=target-builder /sharkey/packages/backend/node_modules ./packages/backend/node_modules -COPY --chown=sharkey:sharkey --from=native-builder /ffmpeg /usr/local/bin/ -COPY --chown=sharkey:sharkey --from=native-builder /ffprobe /usr/local/bin/ -COPY --chown=sharkey:sharkey --from=native-builder /sharkey/built ./built -COPY --chown=sharkey:sharkey --from=native-builder /sharkey/packages/megalodon/lib ./packages/megalodon/lib -COPY --chown=sharkey:sharkey --from=native-builder /sharkey/packages/backend/built ./packages/backend/built -COPY --chown=sharkey:sharkey --from=native-builder /sharkey/fluent-emojis /sharkey/fluent-emojis -COPY --chown=sharkey:sharkey . ./ +RUN apk add ffmpeg tini + +COPY --from=build /sharkey/built ./built +COPY --from=build /sharkey/node_modules ./node_modules +COPY --from=build /sharkey/packages/backend/built ./packages/backend/built +COPY --from=build /sharkey/packages/backend/node_modules ./packages/backend/node_modules +COPY --from=build /sharkey/packages/megalodon/lib ./packages/megalodon/lib +COPY --from=build /sharkey/packages/megalodon/node_modules ./packages/megalodon/node_modules +COPY --from=build /sharkey/packages/misskey-js/built ./packages/misskey-js/built +COPY --from=build /sharkey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules +COPY --from=build /sharkey/fluent-emojis ./fluent-emojis +COPY --from=build /sharkey/sharkey-assets ./packages/frontend/assets + +COPY package.json ./package.json +COPY pnpm-workspace.yaml ./pnpm-workspace.yaml +COPY packages/backend/package.json ./packages/backend/package.json +COPY packages/backend/check_connect.js ./packages/backend/check_connect.js +COPY packages/backend/ormconfig.js ./packages/backend/ormconfig.js +COPY packages/backend/migration ./packages/backend/migration +COPY packages/backend/assets ./packages/backend/assets +COPY packages/megalodon/package.json ./packages/megalodon/package.json +COPY packages/misskey-js/package.json ./packages/misskey-js/package.json -ENV LD_PRELOAD=/usr/local/lib/libjemalloc.so ENV NODE_ENV=production -VOLUME "/sharkey/files" -HEALTHCHECK --interval=5s --retries=20 CMD ["/bin/bash", "/sharkey/healthcheck.sh"] -ENTRYPOINT ["/usr/bin/tini", "--"] +RUN corepack enable +ENTRYPOINT ["/sbin/tini", "--"] CMD ["pnpm", "run", "migrateandstart"] diff --git a/locales/en-US.yml b/locales/en-US.yml index 6a8227511e..b9017317ed 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -944,6 +944,8 @@ approvalStatus: "Approval Status" document: "Documentation" numberOfPageCache: "Number of cached pages" numberOfPageCacheDescription: "Increasing this number will improve convenience for but cause more load as more memory usage on the user's device." +numberOfReplies: "Number of replies in a thread" +numberOfRepliesDescription: "Increasing this number will display more replies. Setting this too high can cause replies to be cramped and unreadable." logoutConfirm: "Really log out?" lastActiveDate: "Last used at" statusbar: "Status bar" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 0fff937d84..fe7aaecdec 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -1871,6 +1871,7 @@ _poll: remainingHours: "Quedan {h} horas y {m} minutos para que finalice" remainingMinutes: "Quedan {m} minutos y {s} segundos para que finalice" remainingSeconds: "Quedan {s} segundos para que finalice" + multiple: "Opciones múltiples" _visibility: public: "Público" publicDescription: "Visible para todos los usuarios" diff --git a/locales/index.d.ts b/locales/index.d.ts index 98ab44dd41..6181e08b56 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -947,6 +947,8 @@ export interface Locale { "document": string; "numberOfPageCache": string; "numberOfPageCacheDescription": string; + "numberOfReplies": string; + "numberOfRepliesDescription": string; "logoutConfirm": string; "lastActiveDate": string; "statusbar": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0af37e041f..6006487a9c 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -944,6 +944,8 @@ approvalStatus: "承認状況" document: "ドキュメント" numberOfPageCache: "ページキャッシュ数" numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。" +numberOfReplies: "スレッド内の返信数" +numberOfRepliesDescription: "この数値を大きくすると、より多くの返信が表示されます。この値を大きくしすぎると、返信が窮屈になり、読めなくなることがあります。" logoutConfirm: "ログアウトしますか?" lastActiveDate: "最終利用日時" statusbar: "ステータスバー" diff --git a/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.svg b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.svg new file mode 100644 index 0000000000..9d21137072 --- /dev/null +++ b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.ttf b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.ttf new file mode 100644 index 0000000000..a2601e0f1b Binary files /dev/null and b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.ttf differ diff --git a/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.woff b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.woff new file mode 100644 index 0000000000..d9f471fa35 Binary files /dev/null and b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.woff differ diff --git a/packages/backend/assets/fonts/sharkey-icons/style.css b/packages/backend/assets/fonts/sharkey-icons/style.css new file mode 100644 index 0000000000..7fb0f94504 --- /dev/null +++ b/packages/backend/assets/fonts/sharkey-icons/style.css @@ -0,0 +1,31 @@ +@charset "UTF-8"; + +@font-face { + font-family: "custom-sharkey-icons"; + src: url("./custom-sharkey-icons.woff") format("woff"), + url("./custom-sharkey-icons.ttf") format("truetype"), + url("./custom-sharkey-icons.svg#custom-sharkey-icons") format("svg"); + font-weight: normal; + font-style: normal; + font-display: block; +} + +.sk-icons { + font-family: "custom-sharkey-icons" !important; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.sk-icons.sk-shark:before { + content: "\61"; +} + +.sk-icons.sk-misskey:before { + content: "\62"; +} diff --git a/packages/backend/migration/1701809447000-NSFW-Instance.js b/packages/backend/migration/1701809447000-NSFW-Instance.js new file mode 100644 index 0000000000..882aa9865d --- /dev/null +++ b/packages/backend/migration/1701809447000-NSFW-Instance.js @@ -0,0 +1,11 @@ +export class NSFWInstance1701809447000 { + name = 'NSFWInstance1701809447000' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" ADD "isNSFW" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "isNSFW"`); + } +} diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts index a4cd533892..b7b8acd661 100644 --- a/packages/backend/src/core/activitypub/models/ApImageService.ts +++ b/packages/backend/src/core/activitypub/models/ApImageService.ts @@ -5,7 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { DriveFilesRepository } from '@/models/_.js'; +import type { DriveFilesRepository, InstancesRepository } from '@/models/_.js'; import type { MiRemoteUser } from '@/models/User.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import { MetaService } from '@/core/MetaService.js'; @@ -18,6 +18,7 @@ import { checkHttps } from '@/misc/check-https.js'; import { ApResolverService } from '../ApResolverService.js'; import { ApLoggerService } from '../ApLoggerService.js'; import type { IObject } from '../type.js'; +import { UtilityService } from '@/core/UtilityService.js'; @Injectable() export class ApImageService { @@ -27,10 +28,14 @@ export class ApImageService { @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, + @Inject(DI.instancesRepository) + private instancesRepository: InstancesRepository, + private metaService: MetaService, private apResolverService: ApResolverService, private driveService: DriveService, private apLoggerService: ApLoggerService, + private utilityService: UtilityService, ) { this.logger = this.apLoggerService.logger; } @@ -68,6 +73,12 @@ export class ApImageService { // 2. or the image is not sensitive const shouldBeCached = instance.cacheRemoteFiles && (instance.cacheRemoteSensitiveFiles || !image.sensitive); + const shouldBeSensitive = await this.instancesRepository.findOneBy({ host: this.utilityService.toPuny(actor.host), isNSFW: true }); + + if (shouldBeSensitive) { + image.sensitive = true; + } + const file = await this.driveService.uploadFromUrl({ url: image.url, user: actor, diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index 7d16a7a80e..515b356dee 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -48,6 +48,7 @@ export class InstanceEntityService { themeColor: instance.themeColor, infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null, latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null, + isNSFW: instance.isNSFW, }; } diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts index b225d918d6..4200b1b461 100644 --- a/packages/backend/src/models/Instance.ts +++ b/packages/backend/src/models/Instance.ts @@ -144,4 +144,9 @@ export class MiInstance { nullable: true, }) public infoUpdatedAt: Date | null; + + @Column('boolean', { + default: false, + }) + public isNSFW: boolean; } diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts index 442e1076f2..ac4b37fb57 100644 --- a/packages/backend/src/models/json-schema/federation-instance.ts +++ b/packages/backend/src/models/json-schema/federation-instance.ts @@ -108,5 +108,10 @@ export const packedFederationInstanceSchema = { optional: false, nullable: true, format: 'date-time', }, + isNSFW: { + type: 'boolean', + optional: false, + nullable: false, + }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index 357bf83e87..4db52b1052 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -23,8 +23,9 @@ export const paramDef = { properties: { host: { type: 'string' }, isSuspended: { type: 'boolean' }, + isNSFW: { type: 'boolean' }, }, - required: ['host', 'isSuspended'], + required: ['host'], } as const; @Injectable() @@ -44,23 +45,31 @@ export default class extends Endpoint { // eslint- throw new Error('instance not found'); } - await this.federatedInstanceService.update(instance.id, { - isSuspended: ps.isSuspended, - }); + if (ps.isSuspended != null) { + await this.federatedInstanceService.update(instance.id, { + isSuspended: ps.isSuspended, + }); - if (instance.isSuspended !== ps.isSuspended) { - if (ps.isSuspended) { - this.moderationLogService.log(me, 'suspendRemoteInstance', { - id: instance.id, - host: instance.host, - }); - } else { - this.moderationLogService.log(me, 'unsuspendRemoteInstance', { - id: instance.id, - host: instance.host, - }); + if (instance.isSuspended !== ps.isSuspended) { + if (ps.isSuspended) { + this.moderationLogService.log(me, 'suspendRemoteInstance', { + id: instance.id, + host: instance.host, + }); + } else { + this.moderationLogService.log(me, 'unsuspendRemoteInstance', { + id: instance.id, + host: instance.host, + }); + } } } + + if (ps.isNSFW != null) { + await this.federatedInstanceService.update(instance.id, { + isNSFW: ps.isNSFW, + }); + } }); } } diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index c8beefa9c7..e143dcfe89 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -40,6 +40,7 @@ export const paramDef = { federating: { type: 'boolean', nullable: true }, subscribing: { type: 'boolean', nullable: true }, publishing: { type: 'boolean', nullable: true }, + nsfw: { type: 'boolean', nullable: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, offset: { type: 'integer', default: 0 }, sort: { type: 'string' }, @@ -103,6 +104,14 @@ export default class extends Endpoint { // eslint- } } + if (typeof ps.nsfw === 'boolean') { + if (ps.nsfw) { + query.andWhere('instance.isNSFW = TRUE'); + } else { + query.andWhere('instance.isNSFW = FALSE'); + } + } + if (typeof ps.silenced === "boolean") { const meta = await this.metaService.fetch(true); diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts index 0652c82a9d..c5e3a5a5f7 100644 --- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts @@ -79,9 +79,7 @@ export default class extends Endpoint { // eslint- ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') - .andWhere( - `(note.userHost = ANY ('{"${instance.bubbleInstances.join('","')}"}'))`, - ) + .andWhere('note.userHost IN (:...hosts)', { hosts: instance.bubbleInstances }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') diff --git a/packages/backend/src/server/api/endpoints/notes/edit.ts b/packages/backend/src/server/api/endpoints/notes/edit.ts index d5592c4726..cfbc207853 100644 --- a/packages/backend/src/server/api/endpoints/notes/edit.ts +++ b/packages/backend/src/server/api/endpoints/notes/edit.ts @@ -123,6 +123,18 @@ export const meta = { code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL', id: '33510210-8452-094c-6227-4a6c05d99f00', }, + + cannotQuoteaQuoteOfCurrentPost: { + message: 'Cannot quote a quote of edited note.', + code: 'CANNOT_QUOTE_A_QUOTE_OF_EDITED_NOTE', + id: '33510210-8452-094c-6227-4a6c05d99f01', + }, + + cannotQuoteCurrentPost: { + message: 'Cannot quote the current note.', + code: 'CANNOT_QUOTE_THE_CURRENT_NOTE', + id: '33510210-8452-094c-6227-4a6c05d99f02', + }, }, } as const; @@ -268,6 +280,11 @@ export default class extends Endpoint { // eslint- } let renote: MiNote | null = null; + + if (ps.renoteId === ps.editId) { + throw new ApiError(meta.errors.cannotQuoteCurrentPost); + } + if (ps.renoteId != null) { // Fetch renote to note renote = await this.notesRepository.findOneBy({ id: ps.renoteId }); @@ -278,6 +295,10 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.cannotReRenote); } + if (renote.renoteId === ps.editId) { + throw new ApiError(meta.errors.cannotQuoteaQuoteOfCurrentPost); + } + // Check blocking if (renote.userId !== me.id) { const blockExist = await this.blockingsRepository.exist({ diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index 243d18202f..018b0bed16 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -41,6 +41,7 @@ html link(rel='prefetch' href=notFoundImageUrl) //- https://github.com/misskey-dev/misskey/issues/9842 link(rel='stylesheet' href='/assets/phosphor-icons/bold/style.css') + link(rel='stylesheet' href='/static-assets/fonts/sharkey-icons/style.css') link(rel='modulepreload' href=`/vite/${clientEntry.file}`) script(src='/client-assets/libopenmpt.js') diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index 384a85e546..5b1e1af308 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only -