diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f389e5906..43b6f045db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83) - Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加 - Enhance: アイコンデコレーションを複数設定できるように +- Enhance: アイコンデコレーションの位置を微調整できるように - Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正 ### Client diff --git a/locales/en-US.yml b/locales/en-US.yml index b9c4d60506..bcf6b8b04d 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1,8 +1,6 @@ --- _lang_: "English" cannotBeUsedFunc: "This feature is currently unavailable." -Xcoordinate: "X-Coordinate" -Ycoordinate: "Y-Coordinate" scale: "Scale" opacity: "Opacity" noteUpdatedAt: "Edited: {date} {time}" diff --git a/locales/index.d.ts b/locales/index.d.ts index 08ec619162..fbcc18b0be 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -4,8 +4,6 @@ export interface Locale { "_lang_": string; "cannotBeUsedFunc": string; - "Xcoordinate": string; - "Ycoordinate": string; "scale": string; "opacity": string; "noteUpdatedAt": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ecb147398c..c43039006f 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1,8 +1,6 @@ _lang_: "日本語" cannotBeUsedFunc: "この機能は現在使用できません。" -Xcoordinate: "X座標" -Ycoordinate: "Y座標" scale: "大きさ" opacity: "不透明度" noteUpdatedAt: "編集済み: {date} {time}" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 2f3d2987b4..4f62d6ec23 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1,8 +1,6 @@ --- _lang_: "한국어" cannotBeUsedFunc: "이 기능은 현재 사용할 수 없어요." -Xcoordinate: "X 좌표" -Ycoordinate: "Y 좌표" scale: "크기" opacity: "불투명도" noteUpdatedAt: "편집됨: {date} {time}" diff --git a/package.json b/package.json index 5e44b34fc4..8caf0026eb 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "migrateandstart": "pnpm migrate && pnpm start", "migrateandstart:docker": "pnpm migrate && exec pnpm start:docker", "watch": "pnpm dev", - "dev": "pnpm -r dev", + "dev": "node scripts/dev.mjs", "lint": "pnpm -r lint", "cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts", "cy:run": "pnpm cypress run", diff --git a/packages/backend/package.json b/packages/backend/package.json index ffc57c6ca7..bf106eb135 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -16,7 +16,8 @@ "watch:swc": "swc src -d built -D -w", "build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json", "watch": "node watch.mjs", - "dev": "node ./built/boot/entry.js", + "restart": "pnpm build && pnpm start", + "dev": "nodemon -w src -e ts,js,mjs,cjs,json --exec \"cross-env NODE_ENV=development pnpm run restart\"", "typecheck": "tsc --noEmit", "eslint": "eslint --quiet \"src/**/*.ts\"", "lint": "pnpm typecheck && pnpm eslint", @@ -231,6 +232,7 @@ "execa": "8.0.1", "jest": "29.7.0", "jest-mock": "29.7.0", + "nodemon": "3.0.2", "simple-oauth2": "5.0.0" } } diff --git a/packages/backend/src/core/AvatarDecorationService.ts b/packages/backend/src/core/AvatarDecorationService.ts index b84f070dba..06849ede1a 100644 --- a/packages/backend/src/core/AvatarDecorationService.ts +++ b/packages/backend/src/core/AvatarDecorationService.ts @@ -189,9 +189,9 @@ export class AvatarDecorationService implements OnApplicationShutdown { id: findDecoration?.id ?? '', angle: avatarDecorations.angle ?? 0, flipH: avatarDecorations.flipH ?? false, + offsetX: avatarDecorations.offsetX ?? 0, + offsetY: avatarDecorations.offsetY ?? 0, scale: avatarDecorations.scale ?? 1, - moveX: avatarDecorations.moveX ?? 0, - moveY: avatarDecorations.moveY ?? 0, opacity: avatarDecorations.opacity ?? 1, }]; await this.usersRepository.update({ id: user.id }, updates); diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 6e265dba32..aa271293df 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -398,9 +398,9 @@ export class UserEntityService implements OnModuleInit { id: ud.id, angle: ud.angle || undefined, flipH: ud.flipH || undefined, + offsetX: ud.offsetX || undefined, + offsetY: ud.offsetY || undefined, scale: ud.scale || undefined, - moveX: ud.moveX || undefined, - moveY: ud.moveY || undefined, opacity: ud.opacity || undefined, url: decorations.find(d => d.id === ud.id)!.url, }))) : [], diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index 0a5b199df2..4736f48ab1 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -143,12 +143,12 @@ export class MiUser { }) public avatarDecorations: { id: string; - angle: number; - flipH: boolean; - scale: number; - moveX: number; - moveY: number; - opacity: number; + angle?: number; + flipH?: boolean; + offsetX?: number; + offsetY?: number; + scale?: number; + opacity?: number; }[]; @Index() diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 66fd5039aa..6180c580c9 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -69,18 +69,18 @@ export const packedUserLiteSchema = { type: 'boolean', nullable: false, optional: true, }, + offsetX: { + type: 'number', + nullable: false, optional: true, + }, + offsetY: { + type: 'number', + nullable: false, optional: true, + }, scale: { type: 'number', nullable: false, optional: true, }, - moveX: { - type: 'number', - nullable: false, optional: true, - }, - moveY: { - type: 'number', - nullable: false, optional: true, - }, opacity: { type: 'number', nullable: false, optional: true, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 6d4c7cf7a3..65d512fac6 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -143,9 +143,9 @@ export const paramDef = { id: { type: 'string', format: 'misskey:id' }, angle: { type: 'number', nullable: true, maximum: 0.5, minimum: -0.5 }, flipH: { type: 'boolean', nullable: true }, + offsetX: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 }, + offsetY: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 }, scale: { type: 'number', nullable: true, maximum: 1.5, minimum: 0.5 }, - moveX: { type: 'number', nullable: true, maximum: 25, minimum: -25 }, - moveY: { type: 'number', nullable: true, maximum: 25, minimum: -25 }, opacity: { type: 'number', nullable: true, maximum: 1, minimum: 0.1 }, }, required: ['id'], @@ -345,9 +345,9 @@ export default class extends Endpoint { // eslint- id: d.id, angle: d.angle ?? 0, flipH: d.flipH ?? false, + offsetX: d.offsetX ?? 0, + offsetY: d.offsetY ?? 0, scale: d.scale ?? 1, - moveX: d.moveX ?? 0, - moveY: d.moveY ?? 0, opacity: d.opacity ?? 1, })); } diff --git a/packages/cherrypick-js/package.json b/packages/cherrypick-js/package.json index f8fbff0280..43ed955a50 100644 --- a/packages/cherrypick-js/package.json +++ b/packages/cherrypick-js/package.json @@ -6,6 +6,7 @@ "types": "./built/index.d.ts", "scripts": { "build": "tsc", + "watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run build\"", "tsd": "tsd", "api": "pnpm api-extractor run --local --verbose", "api-prod": "pnpm api-extractor run --verbose", @@ -32,9 +33,10 @@ "jest-fetch-mock": "3.0.3", "jest-websocket-mock": "2.5.0", "mock-socket": "9.3.1", + "ncp": "2.0.0", + "nodemon": "3.0.2", "tsd": "0.29.0", - "typescript": "5.3.3", - "ncp": "2.0.0" + "typescript": "5.3.3" }, "files": [ "built" diff --git a/packages/cherrypick-js/src/autogen/apiClientJSDoc.ts b/packages/cherrypick-js/src/autogen/apiClientJSDoc.ts index 006d923942..031a87439b 100644 --- a/packages/cherrypick-js/src/autogen/apiClientJSDoc.ts +++ b/packages/cherrypick-js/src/autogen/apiClientJSDoc.ts @@ -1,7 +1,7 @@ /* * version: 4.6.0-beta.4 * basedMisskeyVersion: 2023.12.0-beta.4 - * generatedAt: 2023-12-14T16:00:54.542Z + * generatedAt: 2023-12-14T17:47:05.660Z */ import type { SwitchCaseResponseType } from '../api.js'; diff --git a/packages/cherrypick-js/src/autogen/endpoint.ts b/packages/cherrypick-js/src/autogen/endpoint.ts index a1b658dccc..0fe4710966 100644 --- a/packages/cherrypick-js/src/autogen/endpoint.ts +++ b/packages/cherrypick-js/src/autogen/endpoint.ts @@ -1,7 +1,7 @@ /* * version: 4.6.0-beta.4 * basedMisskeyVersion: 2023.12.0-beta.4 - * generatedAt: 2023-12-14T16:00:54.540Z + * generatedAt: 2023-12-14T17:47:05.658Z */ import type { diff --git a/packages/cherrypick-js/src/autogen/entities.ts b/packages/cherrypick-js/src/autogen/entities.ts index 842d6cd00c..28f3b27688 100644 --- a/packages/cherrypick-js/src/autogen/entities.ts +++ b/packages/cherrypick-js/src/autogen/entities.ts @@ -1,7 +1,7 @@ /* * version: 4.6.0-beta.4 * basedMisskeyVersion: 2023.12.0-beta.4 - * generatedAt: 2023-12-14T16:00:54.539Z + * generatedAt: 2023-12-14T17:47:05.657Z */ import { operations } from './types.js'; diff --git a/packages/cherrypick-js/src/autogen/models.ts b/packages/cherrypick-js/src/autogen/models.ts index 6c5693cbf2..ea8bde45be 100644 --- a/packages/cherrypick-js/src/autogen/models.ts +++ b/packages/cherrypick-js/src/autogen/models.ts @@ -1,7 +1,7 @@ /* * version: 4.6.0-beta.4 * basedMisskeyVersion: 2023.12.0-beta.4 - * generatedAt: 2023-12-14T16:00:54.537Z + * generatedAt: 2023-12-14T17:47:05.656Z */ import { components } from './types.js'; diff --git a/packages/cherrypick-js/src/autogen/types.ts b/packages/cherrypick-js/src/autogen/types.ts index 6c3d6aa6f2..4587445c62 100644 --- a/packages/cherrypick-js/src/autogen/types.ts +++ b/packages/cherrypick-js/src/autogen/types.ts @@ -4,7 +4,7 @@ /* * version: 4.6.0-beta.4 * basedMisskeyVersion: 2023.12.0-beta.4 - * generatedAt: 2023-12-14T16:00:54.457Z + * generatedAt: 2023-12-14T17:47:05.577Z */ /** @@ -3757,9 +3757,9 @@ export type components = { id: string; angle?: number; flipH?: boolean; + offsetX?: number; + offsetY?: number; scale?: number; - moveX?: number; - moveY?: number; opacity?: number; /** Format: url */ url: string; @@ -18674,9 +18674,9 @@ export type operations = { id: string; angle?: number | null; flipH?: boolean | null; + offsetX?: number | null; + offsetY?: number | null; scale?: number | null; - moveX?: number | null; - moveY?: number | null; opacity?: number | null; })[]; /** Format: misskey:id */ diff --git a/packages/frontend/@types/global.d.ts b/packages/frontend/@types/global.d.ts index 7f72bcc935..937faade36 100644 --- a/packages/frontend/@types/global.d.ts +++ b/packages/frontend/@types/global.d.ts @@ -14,3 +14,6 @@ declare const _PERF_PREFIX_: string; declare const _DATA_TRANSFER_DRIVE_FILE_: string; declare const _DATA_TRANSFER_DRIVE_FOLDER_: string; declare const _DATA_TRANSFER_DECK_COLUMN_: string; + +// for dev-mode +declare const _LANGS_FULL_: string[][]; diff --git a/packages/frontend/src/_dev_boot_.ts b/packages/frontend/src/_dev_boot_.ts index a98dc2054d..2a8836e4bf 100644 --- a/packages/frontend/src/_dev_boot_.ts +++ b/packages/frontend/src/_dev_boot_.ts @@ -8,4 +8,88 @@ // (pnpm start時はpugファイルの中で静的リソースとして読み込むようになっており、この問題は起こっていない) import '@tabler/icons-webfont/tabler-icons.scss'; +await main(); + import('@/_boot_.js'); + +/** + * backend/src/server/web/boot.jsで差し込まれている起動処理のうち、最低限必要なものを模倣するための処理 + */ +async function main() { + const forceError = localStorage.getItem('forceError'); + if (forceError != null) { + renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.'); + } + + //#region Detect language & fetch translations + + // dev-modeの場合は常に取り直す + const supportedLangs = _LANGS_.map(it => it[0]); + let lang: string | null | undefined = localStorage.getItem('lang'); + if (lang == null || !supportedLangs.includes(lang)) { + if (supportedLangs.includes(navigator.language)) { + lang = navigator.language; + } else { + lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); + + // Fallback + if (lang == null) lang = 'en-US'; + } + } + + // TODO:今のままだと言語ファイル変更後はpnpm devをリスタートする必要があるので、chokidarを使ったり等で対応できるようにする + const locale = _LANGS_FULL_.find(it => it[0] === lang); + localStorage.setItem('lang', lang); + localStorage.setItem('locale', JSON.stringify(locale[1])); + localStorage.setItem('localeVersion', _VERSION_); + //#endregion + + //#region Theme + const theme = localStorage.getItem('theme'); + if (theme) { + for (const [k, v] of Object.entries(JSON.parse(theme))) { + document.documentElement.style.setProperty(`--${k}`, v.toString()); + + // HTMLの theme-color 適用 + if (k === 'htmlThemeColor') { + for (const tag of document.head.children) { + if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') { + tag.setAttribute('content', v); + break; + } + } + } + } + } + const colorScheme = localStorage.getItem('colorScheme'); + if (colorScheme) { + document.documentElement.style.setProperty('color-scheme', colorScheme); + } + //#endregion + + const fontSize = localStorage.getItem('fontSize'); + if (fontSize) { + document.documentElement.classList.add('f-' + fontSize); + } + + const useSystemFont = localStorage.getItem('useSystemFont'); + if (useSystemFont) { + document.documentElement.classList.add('useSystemFont'); + } + + const wallpaper = localStorage.getItem('wallpaper'); + if (wallpaper) { + document.documentElement.style.backgroundImage = `url(${wallpaper})`; + } + + const customCss = localStorage.getItem('customCss'); + if (customCss && customCss.length > 0) { + const style = document.createElement('style'); + style.innerHTML = customCss; + document.head.appendChild(style); + } +} + +function renderError(code: string, details?: string) { + console.log(code, details); +} diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index ae4f887ff6..c328506d49 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -8,7 +8,7 @@ import { compareVersions } from 'compare-versions'; import widgets from '@/widgets/index.js'; import directives from '@/directives/index.js'; import components from '@/components/index.js'; -import { version, basedMisskeyVersion, ui, lang, updateLocale } from '@/config.js'; +import { version, basedMisskeyVersion, ui, lang, updateLocale, locale } from '@/config.js'; import { applyTheme } from '@/scripts/theme.js'; import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js'; import { i18n, updateI18n } from '@/i18n.js'; @@ -101,7 +101,7 @@ export async function common(createVue: () => App) { //#region Detect language & fetch translations const localeVersion = miLocalStorage.getItem('localeVersion'); - const localeOutdated = (localeVersion == null || localeVersion !== version || lastBasedMisskeyVersion !== basedMisskeyVersion); + const localeOutdated = (localeVersion == null || localeVersion !== version || lastBasedMisskeyVersion !== basedMisskeyVersion || locale == null); if (localeOutdated) { const res = await window.fetch(`/assets/locales/${lang}.${version}.json`); if (res.status === 200) { diff --git a/packages/frontend/src/components/global/CPAvatar-Friendly.vue b/packages/frontend/src/components/global/CPAvatar-Friendly.vue index 8e768afb7f..56d13164dd 100644 --- a/packages/frontend/src/components/global/CPAvatar-Friendly.vue +++ b/packages/frontend/src/components/global/CPAvatar-Friendly.vue @@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only :style="{ rotate: getDecorationAngle(decoration), scale: getDecorationScale(decoration), + translate: getDecorationOffset(decoration), transform: getDecorationTransform(decoration), opacity: getDecorationOpacity(decoration), }" @@ -91,11 +92,15 @@ function getDecorationScale(decoration: Omit) { + const offsetX = decoration.offsetX ?? 0; + const offsetY = decoration.offsetY ?? 0; + return offsetX === 0 && offsetY === 0 ? undefined : `${offsetX * 100}% ${offsetY * 100}%`; +} + function getDecorationTransform(decoration: Omit) { const scale = decoration.scale ?? 1; - const moveX = decoration.moveX ?? 0; - const moveY = decoration.moveY ?? 0; - return `${scale === 1 ? '' : `scale(${scale})`} ${moveX === 0 && moveY === 0 ? '' : `translate(${moveX}%, ${moveY}%)`}`; + return `${scale === 1 ? '' : `scale(${scale})`}`; } function getDecorationOpacity(decoration: Omit) { diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 9681566b1e..c26155b460 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only :style="{ rotate: getDecorationAngle(decoration), scale: getDecorationScale(decoration), + translate: getDecorationOffset(decoration), transform: getDecorationTransform(decoration), opacity: getDecorationOpacity(decoration), }" @@ -63,7 +64,6 @@ import { defaultStore } from '@/store.js'; const animation = ref(defaultStore.state.animation); const squareAvatars = ref(defaultStore.state.squareAvatars); -const useBlurEffect = ref(defaultStore.state.useBlurEffect); const props = withDefaults(defineProps<{ user: Misskey.entities.User; @@ -114,11 +114,15 @@ function getDecorationScale(decoration: Omit) { + const offsetX = decoration.offsetX ?? 0; + const offsetY = decoration.offsetY ?? 0; + return offsetX === 0 && offsetY === 0 ? undefined : `${offsetX * 100}% ${offsetY * 100}%`; +} + function getDecorationTransform(decoration: Omit) { const scale = decoration.scale ?? 1; - const moveX = decoration.moveX ?? 0; - const moveY = decoration.moveY ?? 0; - return `${scale === 1 ? '' : `scale(${scale})`} ${moveX === 0 && moveY === 0 ? '' : `translate(${moveX}%, ${moveY}%)`}`; + return `${scale === 1 ? '' : `scale(${scale})`}`; } function getDecorationOpacity(decoration: Omit) { diff --git a/packages/frontend/src/components/global/ToastAvatar.vue b/packages/frontend/src/components/global/ToastAvatar.vue index 510be77394..768e484d0b 100644 --- a/packages/frontend/src/components/global/ToastAvatar.vue +++ b/packages/frontend/src/components/global/ToastAvatar.vue @@ -24,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only :style="{ rotate: getDecorationAngle(decoration), scale: getDecorationScale(decoration), + translate: getDecorationOffset(decoration), transform: getDecorationTransform(decoration), opacity: getDecorationOpacity(decoration), }" @@ -85,6 +86,12 @@ function getDecorationAngle(decoration: Omit) { + const offsetX = decoration.offsetX ?? 0; + const offsetY = decoration.offsetY ?? 0; + return offsetX === 0 && offsetY === 0 ? undefined : `${offsetX * 100}% ${offsetY * 100}%`; +} + function getDecorationScale(decoration: Omit) { const scaleX = decoration.flipH ? -1 : 1; return scaleX === 1 ? undefined : `${scaleX} 1`; @@ -92,9 +99,7 @@ function getDecorationScale(decoration: Omit) { const scale = decoration.scale ?? 1; - const moveX = decoration.moveX ?? 0; - const moveY = decoration.moveY ?? 0; - return `${scale === 1 ? '' : `scale(${scale})`} ${moveX === 0 && moveY === 0 ? '' : `translate(${moveX}%, ${moveY}%)`}`; + return `${scale === 1 ? '' : `scale(${scale})`}`; } function getDecorationOpacity(decoration: Omit) { diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue index a1bd65d07c..8ebf7cdee8 100644 --- a/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only @click="emit('click')" >
{{ decoration.name }}
- + @@ -28,6 +28,10 @@ const props = defineProps<{ }; angle?: number; flipH?: boolean; + offsetX?: number; + offsetY?: number; + scale?: number; + opacity?: number; }>(); const emit = defineEmits<{ diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue index 63b41fa605..ba952dd357 100644 --- a/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -23,18 +23,18 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + - - - - - - @@ -73,17 +73,17 @@ const emit = defineEmits<{ (ev: 'attach', payload: { angle: number; flipH: boolean; + offsetX: number; + offsetY: number; scale: number; - moveX: number; - moveY: number; opacity: number; }): void; (ev: 'update', payload: { angle: number; flipH: boolean; + offsetX: number; + offsetY: number; scale: number; - moveX: number; - moveY: number; opacity: number; }): void; (ev: 'detach'): void; @@ -93,9 +93,9 @@ const dialog = shallowRef>(); const exceeded = computed(() => ($i.policies.avatarDecorationLimit - $i.avatarDecorations.length) <= 0); const angle = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].angle : null) ?? 0); const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false); +const offsetX = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetX : null) ?? 0); +const offsetY = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetY : null) ?? 0); const scale = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].scale : null) ?? 1); -const moveX = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].moveX : null) ?? 0); -const moveY = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].moveY : null) ?? 0); const opacity = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].opacity : null) ?? 1); const decorationsForPreview = computed(() => { @@ -104,9 +104,9 @@ const decorationsForPreview = computed(() => { url: props.decoration.url, angle: angle.value, flipH: flipH.value, + offsetX: offsetX.value, + offsetY: offsetY.value, scale: scale.value, - moveX: moveX.value, - moveY: moveY.value, opacity: opacity.value, }; const decorations = [...$i.avatarDecorations]; @@ -126,9 +126,9 @@ async function update() { emit('update', { angle: angle.value, flipH: flipH.value, + offsetX: offsetX.value, + offsetY: offsetY.value, scale: scale.value, - moveX: moveX.value, - moveY: moveY.value, opacity: opacity.value, }); dialog.value.close(); @@ -138,9 +138,9 @@ async function attach() { emit('attach', { angle: angle.value, flipH: flipH.value, + offsetX: offsetX.value, + offsetY: offsetY.value, scale: scale.value, - moveX: moveX.value, - moveY: moveY.value, opacity: opacity.value, }); dialog.value.close(); diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue index cf1dfee324..4a5b5a4e15 100644 --- a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue @@ -16,6 +16,10 @@ SPDX-License-Identifier: AGPL-3.0-only :decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)" :angle="avatarDecoration.angle" :flipH="avatarDecoration.flipH" + :offsetX="avatarDecoration.offsetX" + :offsetY="avatarDecoration.offsetY" + :scale="avatarDecoration.scale" + :opacity="avatarDecoration.opacity" :active="true" @click="openDecoration(avatarDecoration, i)" /> @@ -66,6 +70,10 @@ function openDecoration(avatarDecoration, index?: number) { id: avatarDecoration.id, angle: payload.angle, flipH: payload.flipH, + offsetX: payload.offsetX, + offsetY: payload.offsetY, + scale: payload.scale, + opacity: payload.opacity, }; const update = [...$i.avatarDecorations, decoration]; await os.apiWithDialog('i/update', { @@ -78,6 +86,10 @@ function openDecoration(avatarDecoration, index?: number) { id: avatarDecoration.id, angle: payload.angle, flipH: payload.flipH, + offsetX: payload.offsetX, + offsetY: payload.offsetY, + scale: payload.scale, + opacity: payload.opacity, }; const update = [...$i.avatarDecorations]; update[index] = decoration; diff --git a/packages/frontend/src/scripts/clear-cache.ts b/packages/frontend/src/scripts/clear-cache.ts index 5f27254b8a..f2db87c4fb 100644 --- a/packages/frontend/src/scripts/clear-cache.ts +++ b/packages/frontend/src/scripts/clear-cache.ts @@ -6,6 +6,7 @@ import { fetchCustomEmojis } from '@/custom-emojis.js'; export async function clearCache() { os.waiting(); miLocalStorage.removeItem('locale'); + miLocalStorage.removeItem('localeVersion'); miLocalStorage.removeItem('theme'); miLocalStorage.removeItem('emojis'); miLocalStorage.removeItem('lastEmojisFetchedAt'); diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts index 6b6394a722..aea46f4231 100644 --- a/packages/frontend/vite.config.local-dev.ts +++ b/packages/frontend/vite.config.local-dev.ts @@ -1,5 +1,6 @@ import dns from 'dns'; import { defineConfig } from 'vite'; +import locales from '../../locales'; import { getConfig } from './vite.config.js'; dns.setDefaultResultOrder('ipv4first'); @@ -49,6 +50,11 @@ const devConfig = { input: 'index.html', }, }, + + define: { + ...defaultConfig.define, + _LANGS_FULL_: JSON.stringify(Object.entries(locales)), + }, }; export default defineConfig(({ command, mode }) => devConfig); diff --git a/packages/sw/package.json b/packages/sw/package.json index 59ffc51d70..0ab388f260 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -2,7 +2,7 @@ "name": "sw", "private": true, "scripts": { - "watch": "node build.js watch", + "watch": "nodemon -w ../../package.json -e json --exec \"node build.js watch\"", "build": "node build.js", "typecheck": "tsc --noEmit", "eslint": "eslint --quiet src/**/*.ts", @@ -18,6 +18,7 @@ "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67", "eslint": "8.55.0", "eslint-plugin-import": "2.29.0", + "nodemon": "3.0.2", "typescript": "5.3.3" }, "type": "module" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 695dff173a..5be2862511 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -655,6 +655,9 @@ importers: jest-mock: specifier: 29.7.0 version: 29.7.0 + nodemon: + specifier: 3.0.2 + version: 3.0.2 simple-oauth2: specifier: 5.0.0 version: 5.0.0 @@ -710,6 +713,9 @@ importers: ncp: specifier: 2.0.0 version: 2.0.0 + nodemon: + specifier: 3.0.2 + version: 3.0.2 tsd: specifier: 0.29.0 version: 0.29.0 @@ -1150,6 +1156,9 @@ importers: eslint-plugin-import: specifier: 2.29.0 version: 2.29.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0) + nodemon: + specifier: 3.0.2 + version: 3.0.2 typescript: specifier: 5.3.3 version: 5.3.3 diff --git a/scripts/build-assets.mjs b/scripts/build-assets.mjs index 921c8b4d75..a795500bf3 100644 --- a/scripts/build-assets.mjs +++ b/scripts/build-assets.mjs @@ -95,6 +95,7 @@ if (process.argv.includes("--watch")) { for await (const event of watcher) { const filename = event.filename?.replaceAll('\\', '/'); if (/^[a-z]+-[A-Z]+\.yml/.test(filename)) { + console.log(`update ${filename} ...`) locales = buildLocales(); await copyFrontendLocales() } diff --git a/scripts/build-pre.js b/scripts/build-pre.js index 55d99a5a1d..036f0632e7 100644 --- a/scripts/build-pre.js +++ b/scripts/build-pre.js @@ -4,7 +4,24 @@ */ const fs = require('fs'); -const meta = require('../package.json'); +const packageJsonPath = __dirname + '/../package.json' -fs.mkdirSync(__dirname + '/../built', { recursive: true }); -fs.writeFileSync(__dirname + '/../built/meta.json', JSON.stringify({ version: meta.version, basedMisskeyVersion: meta.basedMisskeyVersion }), 'utf-8'); +function build() { + try { + const json = fs.readFileSync(packageJsonPath, 'utf-8') + const meta = JSON.parse(json); + fs.mkdirSync(__dirname + '/../built', { recursive: true }); + fs.writeFileSync(__dirname + '/../built/meta.json', JSON.stringify({ version: meta.version, basedMisskeyVersion: meta.basedMisskeyVersion }), 'utf-8'); + } catch (e) { + console.error(e) + } +} + +build(); + +if (process.argv.includes("--watch")) { + fs.watch(packageJsonPath, (event, filename) => { + console.log(`update ${filename} ...`) + build() + }) +} diff --git a/scripts/dev.mjs b/scripts/dev.mjs index 5b235dab4f..42667acd67 100644 --- a/scripts/dev.mjs +++ b/scripts/dev.mjs @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import fs from 'node:fs'; import { dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import { execa } from 'execa'; @@ -11,8 +10,6 @@ import { execa } from 'execa'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); -const vitePort = process.env.VITE_PORT ? ["--strictPort", "--port", process.env.VITE_PORT] : ["--strictPort"]; - await execa('pnpm', ['clean'], { cwd: _dirname + '/../', stdout: process.stdout, @@ -31,19 +28,25 @@ await execa('pnpm', ['build-assets'], { stderr: process.stderr, }); +execa('pnpm', ['build-pre', '--watch'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +}); + execa('pnpm', ['build-assets', '--watch'], { cwd: _dirname + '/../', stdout: process.stdout, stderr: process.stderr, }); -execa('pnpm', ['--filter', 'backend', 'watch'], { +execa('pnpm', ['--filter', 'backend', 'dev'], { cwd: _dirname + '/../', stdout: process.stdout, stderr: process.stderr, }); -execa('pnpm', ['--filter', 'frontend', 'watch', ...vitePort], { +execa('pnpm', ['--filter', 'frontend', 'dev'], { cwd: _dirname + '/../', stdout: process.stdout, stderr: process.stderr, @@ -55,27 +58,8 @@ execa('pnpm', ['--filter', 'sw', 'watch'], { stderr: process.stderr, }); -const start = async () => { - try { - const stat = fs.statSync(_dirname + '/../packages/backend/built/boot/entry.js'); - if (!stat) throw new Error('not exist yet'); - if (stat.size === 0) throw new Error('not built yet'); - - const subprocess = await execa('pnpm', ['start'], { - cwd: _dirname + '/../', - stdout: process.stdout, - stderr: process.stderr, - }); - - // なぜかworkerだけが終了してmasterが残るのでその対策 - process.on('SIGINT', () => { - subprocess.kill('SIGINT'); - process.exit(0); - }); - } catch (e) { - await new Promise(resolve => setTimeout(resolve, 3000)); - start(); - } -}; - -start(); +execa('pnpm', ['--filter', 'cherrypick-js', 'watch'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +});