From 386fcedf3525eb18ed2297bd93d45d1d78a36f84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?= <46447427+samunohito@users.noreply.github.com> Date: Thu, 14 Dec 2023 20:16:02 +0900 Subject: [PATCH 1/3] =?UTF-8?q?(dev-mode)=20dev=E3=83=A2=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=81=AE=E6=94=B9=E5=96=84=20(#12639)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix dev-mode * fix dev-mode * fix dev-mode * fix dev-mode --------- Co-authored-by: syuilo --- package.json | 2 +- packages/backend/package.json | 4 +- packages/frontend/@types/global.d.ts | 3 + packages/frontend/src/_dev_boot_.ts | 84 ++++++++++++++++++++++ packages/frontend/vite.config.local-dev.ts | 6 ++ packages/misskey-js/package.json | 6 +- packages/sw/package.json | 3 +- pnpm-lock.yaml | 9 +++ scripts/build-assets.mjs | 1 + scripts/build-pre.js | 23 +++++- scripts/dev.mjs | 42 ++++------- 11 files changed, 146 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index bb44951e8a..3057c5d804 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "check:connect": "cd packages/backend && pnpm check:connect", "migrateandstart": "pnpm migrate && pnpm start", "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 17113a5e0d..504cc882ff 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", @@ -226,6 +227,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/frontend/@types/global.d.ts b/packages/frontend/@types/global.d.ts index 390f63990b..7d9335cc52 100644 --- a/packages/frontend/@types/global.d.ts +++ b/packages/frontend/@types/global.d.ts @@ -13,3 +13,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 2e95a03576..d01a957048 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/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/misskey-js/package.json b/packages/misskey-js/package.json index 6a7712af90..0b54c6a59c 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-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/sw/package.json b/packages/sw/package.json index 039a6887aa..990e991966 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 5afc583343..7c0bc470b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -643,6 +643,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 @@ -1041,6 +1044,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 @@ -1105,6 +1111,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 f8f09ec2fb..2e1268130c 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 bf3e355b5b..ed75aa6553 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 }), '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 }), '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 1d06aa541f..30dbebcf0f 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', 'misskey-js', 'watch'], { + cwd: _dirname + '/../', + stdout: process.stdout, + stderr: process.stderr, +}); From 239507d7d6d0a8574bb641adee66d9da96537bb9 Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 14 Dec 2023 20:21:57 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20dev=E3=83=A2=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=81=AE=E7=89=B9=E5=AE=9A=E6=9D=A1=E4=BB=B6=E4=B8=8B=E3=81=A7?= =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=81=AA=E3=81=8F=E3=81=AA=E3=82=8B=E5=95=8F=E9=A1=8C?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12653)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: devモードでキャッシュクリアするとページが表示されなくなる問題を修正 * fix: localeがnullの場合も最新のlocaleを取得するように --- packages/frontend/src/boot/common.ts | 4 ++-- packages/frontend/src/scripts/clear-cache.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index 60f2781fdf..728f39962b 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, ui, lang, updateLocale } from '@/config.js'; +import { version, 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'; @@ -88,7 +88,7 @@ export async function common(createVue: () => App) { //#region Detect language & fetch translations const localeVersion = miLocalStorage.getItem('localeVersion'); - const localeOutdated = (localeVersion == null || localeVersion !== version); + const localeOutdated = (localeVersion == null || localeVersion !== version || locale == null); if (localeOutdated) { const res = await window.fetch(`/assets/locales/${lang}.${version}.json`); if (res.status === 200) { 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'); From 417852779f871197f6aec2fec0fd56a2380836e4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 14 Dec 2023 20:58:08 +0900 Subject: [PATCH 3/3] =?UTF-8?q?enhance:=20=E3=82=A2=E3=82=A4=E3=82=B3?= =?UTF-8?q?=E3=83=B3=E3=83=87=E3=82=B3=E3=83=AC=E3=83=BC=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=81=AE=E4=BD=8D=E7=BD=AE=E3=82=92=E5=BE=AE=E8=AA=BF?= =?UTF-8?q?=E6=95=B4=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../src/core/entities/UserEntityService.ts | 2 ++ packages/backend/src/models/User.ts | 6 ++++-- .../src/server/api/endpoints/i/update.ts | 4 ++++ .../src/components/global/MkAvatar.vue | 7 +++++++ .../profile.avatar-decoration.decoration.vue | 4 +++- .../profile.avatar-decoration.dialog.vue | 18 ++++++++++++++++++ .../settings/profile.avatar-decoration.vue | 6 ++++++ 8 files changed, 45 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0581466d7c..599bd463fd 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/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 917f4e06d0..fb7aa0c244 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -362,6 +362,8 @@ export class UserEntityService implements OnModuleInit { id: ud.id, angle: ud.angle || undefined, flipH: ud.flipH || undefined, + offsetX: ud.offsetX || undefined, + offsetY: ud.offsetY || undefined, url: decorations.find(d => d.id === ud.id)!.url, }))) : [], isBot: user.isBot, diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index c3762fcd3e..219497a125 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -143,8 +143,10 @@ export class MiUser { }) public avatarDecorations: { id: string; - angle: number; - flipH: boolean; + angle?: number; + flipH?: boolean; + offsetX?: number; + offsetY?: number; }[]; @Index() diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 399e6b88cb..a56f50115b 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -143,6 +143,8 @@ 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 }, }, required: ['id'], } }, @@ -341,6 +343,8 @@ 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, })); } diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 9d13c03290..af5b6e44f5 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -31,6 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only :style="{ rotate: getDecorationAngle(decoration), scale: getDecorationScale(decoration), + translate: getDecorationOffset(decoration), }" alt="" > @@ -99,6 +100,12 @@ function getDecorationScale(decoration: Omit) { + const offsetX = decoration.offsetX ?? 0; + const offsetY = decoration.offsetY ?? 0; + return offsetX === 0 && offsetY === 0 ? undefined : `${offsetX * 100}% ${offsetY * 100}%`; +} + const color = ref(); watch(() => props.user.avatarBlurhash, () => { 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 c113868238..9c95b5547e 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,8 @@ const props = defineProps<{ }; angle?: number; flipH?: boolean; + offsetX?: number; + offsetY?: 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 26cacf3c37..77e6b28fad 100644 --- a/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue @@ -23,6 +23,12 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + @@ -64,10 +70,14 @@ const emit = defineEmits<{ (ev: 'attach', payload: { angle: number; flipH: boolean; + offsetX: number; + offsetY: number; }): void; (ev: 'update', payload: { angle: number; flipH: boolean; + offsetX: number; + offsetY: number; }): void; (ev: 'detach'): void; }>(); @@ -76,6 +86,8 @@ 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 decorationsForPreview = computed(() => { const decoration = { @@ -83,6 +95,8 @@ const decorationsForPreview = computed(() => { url: props.decoration.url, angle: angle.value, flipH: flipH.value, + offsetX: offsetX.value, + offsetY: offsetY.value, }; const decorations = [...$i.avatarDecorations]; if (props.usingIndex != null) { @@ -101,6 +115,8 @@ async function update() { emit('update', { angle: angle.value, flipH: flipH.value, + offsetX: offsetX.value, + offsetY: offsetY.value, }); dialog.value.close(); } @@ -109,6 +125,8 @@ async function attach() { emit('attach', { angle: angle.value, flipH: flipH.value, + offsetX: offsetX.value, + offsetY: offsetY.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 bfef6e0325..8579acfed8 100644 --- a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue @@ -16,6 +16,8 @@ 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" :active="true" @click="openDecoration(avatarDecoration, i)" /> @@ -66,6 +68,8 @@ function openDecoration(avatarDecoration, index?: number) { id: avatarDecoration.id, angle: payload.angle, flipH: payload.flipH, + offsetX: payload.offsetX, + offsetY: payload.offsetY, }; const update = [...$i.avatarDecorations, decoration]; await os.apiWithDialog('i/update', { @@ -78,6 +82,8 @@ function openDecoration(avatarDecoration, index?: number) { id: avatarDecoration.id, angle: payload.angle, flipH: payload.flipH, + offsetX: payload.offsetX, + offsetY: payload.offsetY, }; const update = [...$i.avatarDecorations]; update[index] = decoration;