From bbe4824955d1533c8a466e791a269f30f1586a57 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 10 Jul 2022 15:36:02 +0900 Subject: enhance(client): update themes --- packages/client/src/scripts/theme.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'packages/client/src/scripts/theme.ts') diff --git a/packages/client/src/scripts/theme.ts b/packages/client/src/scripts/theme.ts index dec9fb355c..eb82044b32 100644 --- a/packages/client/src/scripts/theme.ts +++ b/packages/client/src/scripts/theme.ts @@ -1,6 +1,6 @@ import { ref } from 'vue'; -import { globalEvents } from '@/events'; import tinycolor from 'tinycolor2'; +import { globalEvents } from '@/events'; export type Theme = { id: string; @@ -31,11 +31,11 @@ export const getBuiltinThemes = () => Promise.all( 'd-astro', 'd-future', 'd-botanical', + 'd-lime-green', + 'd-orange-green', 'd-cherry', 'd-ice', - 'd-pumpkin', - 'd-black', - ].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default)) + ].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default)), ); export const getBuiltinThemesRef = () => { -- cgit v1.2.3-freya From 05a342009fcd214ab1ad11dbda58a383075dcec2 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 13 Jul 2022 16:33:18 +0900 Subject: :art: --- packages/client/src/scripts/theme.ts | 4 ++-- packages/client/src/themes/d-green-lime.json5 | 24 ++++++++++++++++++++++++ packages/client/src/themes/d-green-orange.json5 | 24 ++++++++++++++++++++++++ packages/client/src/themes/d-lime-green.json5 | 24 ------------------------ packages/client/src/themes/d-orange-green.json5 | 24 ------------------------ 5 files changed, 50 insertions(+), 50 deletions(-) create mode 100644 packages/client/src/themes/d-green-lime.json5 create mode 100644 packages/client/src/themes/d-green-orange.json5 delete mode 100644 packages/client/src/themes/d-lime-green.json5 delete mode 100644 packages/client/src/themes/d-orange-green.json5 (limited to 'packages/client/src/scripts/theme.ts') diff --git a/packages/client/src/scripts/theme.ts b/packages/client/src/scripts/theme.ts index eb82044b32..77d1df0826 100644 --- a/packages/client/src/scripts/theme.ts +++ b/packages/client/src/scripts/theme.ts @@ -31,8 +31,8 @@ export const getBuiltinThemes = () => Promise.all( 'd-astro', 'd-future', 'd-botanical', - 'd-lime-green', - 'd-orange-green', + 'd-green-lime', + 'd-green-orange', 'd-cherry', 'd-ice', ].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default)), diff --git a/packages/client/src/themes/d-green-lime.json5 b/packages/client/src/themes/d-green-lime.json5 new file mode 100644 index 0000000000..a6983b9ac2 --- /dev/null +++ b/packages/client/src/themes/d-green-lime.json5 @@ -0,0 +1,24 @@ +{ + id: '02816013-8107-440f-877e-865083ffe194', + + name: 'Mi Green+Lime Dark', + author: 'syuilo', + + base: 'dark', + + props: { + accent: '#b4e900', + bg: '#0C1210', + fg: '#dee7e4', + fgHighlighted: '#fff', + fgOnAccent: '#192320', + divider: '#e7fffb24', + panel: '#192320', + panelHeaderBg: '@panel', + panelHeaderDivider: '@divider', + popup: '#293330', + renote: '@accent', + mentionMe: '#ffaa00', + link: '#24d7ce', + }, +} diff --git a/packages/client/src/themes/d-green-orange.json5 b/packages/client/src/themes/d-green-orange.json5 new file mode 100644 index 0000000000..63c961565d --- /dev/null +++ b/packages/client/src/themes/d-green-orange.json5 @@ -0,0 +1,24 @@ +{ + id: 'dc489603-27b5-424a-9b25-1ff6aec9824a', + + name: 'Mi Green+Orange Dark', + author: 'syuilo', + + base: 'dark', + + props: { + accent: '#e97f00', + bg: '#0C1210', + fg: '#dee7e4', + fgHighlighted: '#fff', + fgOnAccent: '#192320', + divider: '#e7fffb24', + panel: '#192320', + panelHeaderBg: '@panel', + panelHeaderDivider: '@divider', + popup: '#293330', + renote: '@accent', + mentionMe: '#ffaa00', + link: '#24d7ce', + }, +} diff --git a/packages/client/src/themes/d-lime-green.json5 b/packages/client/src/themes/d-lime-green.json5 deleted file mode 100644 index 17617687b3..0000000000 --- a/packages/client/src/themes/d-lime-green.json5 +++ /dev/null @@ -1,24 +0,0 @@ -{ - id: '02816013-8107-440f-877e-865083ffe194', - - name: 'Mi Lime+Green Dark', - author: 'syuilo', - - base: 'dark', - - props: { - accent: '#b4e900', - bg: '#0C1210', - fg: '#dee7e4', - fgHighlighted: '#fff', - fgOnAccent: '#192320', - divider: '#e7fffb24', - panel: '#192320', - panelHeaderBg: '@panel', - panelHeaderDivider: '@divider', - popup: '#293330', - renote: '@accent', - mentionMe: '#ffaa00', - link: '#2bceff', - }, -} diff --git a/packages/client/src/themes/d-orange-green.json5 b/packages/client/src/themes/d-orange-green.json5 deleted file mode 100644 index f0e15f4763..0000000000 --- a/packages/client/src/themes/d-orange-green.json5 +++ /dev/null @@ -1,24 +0,0 @@ -{ - id: 'dc489603-27b5-424a-9b25-1ff6aec9824a', - - name: 'Mi Orange+Green Dark', - author: 'syuilo', - - base: 'dark', - - props: { - accent: '#e97f00', - bg: '#0C1210', - fg: '#dee7e4', - fgHighlighted: '#fff', - fgOnAccent: '#192320', - divider: '#e7fffb24', - panel: '#192320', - panelHeaderBg: '@panel', - panelHeaderDivider: '@divider', - popup: '#293330', - renote: '@accent', - mentionMe: '#ffaa00', - link: '#2bceff', - }, -} -- cgit v1.2.3-freya From 035ec0a87467529a51b954ed61e82c6048729c6a Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 22 Jul 2022 00:25:56 +0900 Subject: enhance(client): add some themes --- packages/client/src/scripts/theme.ts | 2 + packages/client/src/themes/d-u0.json5 | 88 +++++++++++++++++++++++++++++++++++ packages/client/src/themes/l-u0.json5 | 87 ++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 packages/client/src/themes/d-u0.json5 create mode 100644 packages/client/src/themes/l-u0.json5 (limited to 'packages/client/src/scripts/theme.ts') diff --git a/packages/client/src/scripts/theme.ts b/packages/client/src/scripts/theme.ts index 77d1df0826..02ac77b59d 100644 --- a/packages/client/src/scripts/theme.ts +++ b/packages/client/src/scripts/theme.ts @@ -25,6 +25,7 @@ export const getBuiltinThemes = () => Promise.all( 'l-vivid', 'l-cherry', 'l-sushi', + 'l-u0', 'd-dark', 'd-persimmon', @@ -35,6 +36,7 @@ export const getBuiltinThemes = () => Promise.all( 'd-green-orange', 'd-cherry', 'd-ice', + 'd-u0', ].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default)), ); diff --git a/packages/client/src/themes/d-u0.json5 b/packages/client/src/themes/d-u0.json5 new file mode 100644 index 0000000000..b270f809ac --- /dev/null +++ b/packages/client/src/themes/d-u0.json5 @@ -0,0 +1,88 @@ +{ + id: '7a5bc13b-df8f-4d44-8e94-4452f0c634bb', + base: 'dark', + name: 'Mi U0 Dark', + props: { + X2: ':darken<2<@panel', + X3: 'rgba(255, 255, 255, 0.05)', + X4: 'rgba(255, 255, 255, 0.1)', + X5: 'rgba(255, 255, 255, 0.05)', + X6: 'rgba(255, 255, 255, 0.15)', + X7: 'rgba(255, 255, 255, 0.05)', + X8: ':lighten<5<@accent', + X9: ':darken<5<@accent', + bg: '#172426', + fg: '#dadada', + X10: ':alpha<0.4<@accent', + X11: 'rgba(0, 0, 0, 0.3)', + X12: 'rgba(255, 255, 255, 0.1)', + X13: 'rgba(255, 255, 255, 0.15)', + X14: ':alpha<0.5<@navBg', + X15: ':alpha<0<@panel', + X16: ':alpha<0.7<@panel', + X17: ':alpha<0.8<@bg', + cwBg: '#687390', + cwFg: '#393f4f', + link: '@accent', + warn: '#ecb637', + badge: '#31b1ce', + error: '#ec4137', + focus: ':alpha<0.3<@accent', + navBg: '@panel', + navFg: '@fg', + panel: ':lighten<3<@bg', + popup: ':lighten<3<@panel', + accent: '#00a497', + header: ':alpha<0.7<@panel', + infoBg: '#253142', + infoFg: '#fff', + renote: '@accent', + shadow: 'rgba(0, 0, 0, 0.3)', + divider: 'rgba(255, 255, 255, 0.1)', + hashtag: '#e6b422', + mention: '@accent', + modalBg: 'rgba(0, 0, 0, 0.5)', + success: '#86b300', + buttonBg: 'rgba(255, 255, 255, 0.05)', + switchBg: 'rgba(255, 255, 255, 0.15)', + acrylicBg: ':alpha<0.5<@bg', + cwHoverBg: '#707b97', + indicator: '@accent', + mentionMe: '@mention', + messageBg: '@bg', + navActive: '@accent', + accentedBg: ':alpha<0.15<@accent', + codeNumber: '#cfff9e', + codeString: '#ffb675', + fgOnAccent: '#fff', + infoWarnBg: '#42321c', + infoWarnFg: '#ffbd3e', + navHoverFg: ':lighten<17<@fg', + codeBoolean: '#c59eff', + dateLabelFg: '@fg', + inputBorder: 'rgba(255, 255, 255, 0.1)', + panelBorder: '" solid 1px var(--divider)', + accentDarken: ':darken<10<@accent', + acrylicPanel: ':alpha<0.5<@panel', + navIndicator: '@indicator', + accentLighten: ':lighten<10<@accent', + buttonHoverBg: 'rgba(255, 255, 255, 0.1)', + driveFolderBg: ':alpha<0.3<@accent', + fgHighlighted: ':lighten<3<@fg', + fgTransparent: ':alpha<0.5<@fg', + panelHeaderBg: ':lighten<3<@panel', + panelHeaderFg: '@fg', + buttonGradateA: '@accent', + buttonGradateB: ':hue<20<@accent', + htmlThemeColor: '@bg', + panelHighlight: ':lighten<3<@panel', + listItemHoverBg: 'rgba(255, 255, 255, 0.03)', + scrollbarHandle: 'rgba(255, 255, 255, 0.2)', + inputBorderHover: 'rgba(255, 255, 255, 0.2)', + wallpaperOverlay: 'rgba(0, 0, 0, 0.5)', + fgTransparentWeak: ':alpha<0.75<@fg', + panelHeaderDivider: 'rgba(0, 0, 0, 0)', + scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)', + deckDivider: '#142022', + }, +} diff --git a/packages/client/src/themes/l-u0.json5 b/packages/client/src/themes/l-u0.json5 new file mode 100644 index 0000000000..03b114ba39 --- /dev/null +++ b/packages/client/src/themes/l-u0.json5 @@ -0,0 +1,87 @@ +{ + id: 'e2c940b5-6e9a-4c03-b738-261c720c426d', + base: 'light', + name: 'Mi U0 Light', + props: { + X2: ':darken<2<@panel', + X3: 'rgba(255, 255, 255, 0.05)', + X4: 'rgba(255, 255, 255, 0.1)', + X5: 'rgba(255, 255, 255, 0.05)', + X6: 'rgba(255, 255, 255, 0.15)', + X7: 'rgba(255, 255, 255, 0.05)', + X8: ':lighten<5<@accent', + X9: ':darken<5<@accent', + bg: '#e7e7eb', + fg: '#5f5f5f', + X10: ':alpha<0.4<@accent', + X11: 'rgba(0, 0, 0, 0.3)', + X12: 'rgba(255, 255, 255, 0.1)', + X13: 'rgba(255, 255, 255, 0.15)', + X14: ':alpha<0.5<@navBg', + X15: ':alpha<0<@panel', + X16: ':alpha<0.7<@panel', + X17: ':alpha<0.8<@bg', + cwBg: '#687390', + cwFg: '#393f4f', + link: '@accent', + warn: '#ecb637', + badge: '#31b1ce', + error: '#ec4137', + focus: ':alpha<0.3<@accent', + navBg: '@panel', + navFg: '@fg', + panel: ':lighten<3<@bg', + popup: ':lighten<3<@panel', + accent: '#478384', + header: ':alpha<0.7<@panel', + infoBg: '#253142', + infoFg: '#fff', + renote: '@accent', + shadow: 'rgba(0, 0, 0, 0.3)', + divider: '#4646461a', + hashtag: '#1f3134', + mention: '@accent', + modalBg: 'rgba(0, 0, 0, 0.5)', + success: '#86b300', + buttonBg: '#0000000d', + switchBg: 'rgba(255, 255, 255, 0.15)', + acrylicBg: ':alpha<0.5<@bg', + cwHoverBg: '#707b97', + indicator: '@accent', + mentionMe: '@mention', + messageBg: '@bg', + navActive: '@accent', + accentedBg: ':alpha<0.15<@accent', + codeNumber: '#cfff9e', + codeString: '#ffb675', + fgOnAccent: '#fff', + infoWarnBg: '#42321c', + infoWarnFg: '#ffbd3e', + navHoverFg: ':lighten<17<@fg', + codeBoolean: '#c59eff', + dateLabelFg: '@fg', + inputBorder: 'rgba(255, 255, 255, 0.1)', + panelBorder: '" solid 1px var(--divider)', + accentDarken: ':darken<10<@accent', + acrylicPanel: ':alpha<0.5<@panel', + navIndicator: '@indicator', + accentLighten: ':lighten<10<@accent', + buttonHoverBg: '#0000001a', + driveFolderBg: ':alpha<0.3<@accent', + fgHighlighted: ':lighten<3<@fg', + fgTransparent: ':alpha<0.5<@fg', + panelHeaderBg: ':lighten<3<@panel', + panelHeaderFg: '@fg', + buttonGradateA: '@accent', + buttonGradateB: ':hue<20<@accent', + htmlThemeColor: '@bg', + panelHighlight: ':lighten<3<@panel', + listItemHoverBg: 'rgba(255, 255, 255, 0.03)', + scrollbarHandle: '#74747433', + inputBorderHover: 'rgba(255, 255, 255, 0.2)', + wallpaperOverlay: 'rgba(0, 0, 0, 0.5)', + fgTransparentWeak: ':alpha<0.75<@fg', + panelHeaderDivider: 'rgba(0, 0, 0, 0)', + scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)', + }, +} -- cgit v1.2.3-freya From 773139b737fa5c7a597c78abbb39675964343075 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 27 Aug 2022 00:39:59 +0900 Subject: enhance(client): add html color-schema support --- packages/backend/src/server/web/boot.js | 4 ++++ packages/client/src/scripts/theme.ts | 5 +++++ 2 files changed, 9 insertions(+) (limited to 'packages/client/src/scripts/theme.ts') diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index b0d529ec36..2aef689d3f 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -92,6 +92,10 @@ } } } + const colorSchema = localStorage.getItem('colorSchema'); + if (colorSchema) { + document.documentElement.style.setProperty('color-schema', colorSchema); + } //#endregion const fontSize = localStorage.getItem('fontSize'); diff --git a/packages/client/src/scripts/theme.ts b/packages/client/src/scripts/theme.ts index 02ac77b59d..3f55d9ae86 100644 --- a/packages/client/src/scripts/theme.ts +++ b/packages/client/src/scripts/theme.ts @@ -57,6 +57,8 @@ export function applyTheme(theme: Theme, persist = true) { document.documentElement.classList.remove('_themeChanging_'); }, 1000); + const colorSchema = theme.base === 'dark' ? 'dark' : 'light'; + // Deep copy const _theme = JSON.parse(JSON.stringify(theme)); @@ -78,8 +80,11 @@ export function applyTheme(theme: Theme, persist = true) { document.documentElement.style.setProperty(`--${k}`, v.toString()); } + document.documentElement.style.setProperty('color-schema', colorSchema); + if (persist) { localStorage.setItem('theme', JSON.stringify(props)); + localStorage.setItem('colorSchema', colorSchema); } // 色計算など再度行えるようにクライアント全体に通知 -- cgit v1.2.3-freya From d5aee2ea58a16e0cf65213fab9e46192882feba9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 17 Nov 2022 09:31:07 +0900 Subject: improve performance --- packages/backend/src/core/RelayService.ts | 3 +- .../src/core/entities/DriveFileEntityService.ts | 3 +- packages/backend/src/misc/clone.ts | 18 +++++++++ .../backend/src/server/web/ClientServerService.ts | 3 +- packages/client/src/components/MkNote.vue | 5 ++- packages/client/src/components/MkNoteDetailed.vue | 5 ++- packages/client/src/components/MkPostForm.vue | 3 +- packages/client/src/pages/settings/reaction.vue | 5 ++- .../src/pages/settings/statusbar.statusbar.vue | 7 ++-- packages/client/src/scripts/clone.ts | 18 +++++++++ packages/client/src/scripts/theme.ts | 3 +- packages/client/src/ui/deck/deck-store.ts | 45 ++++++++++------------ packages/client/src/widgets/job-queue.vue | 7 ++-- packages/client/src/widgets/widget.ts | 7 ++-- 14 files changed, 88 insertions(+), 44 deletions(-) create mode 100644 packages/backend/src/misc/clone.ts create mode 100644 packages/client/src/scripts/clone.ts (limited to 'packages/client/src/scripts/theme.ts') diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index 563eeac0f0..3c67e0573f 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -9,6 +9,7 @@ import { QueueService } from '@/core/QueueService.js'; import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; import { DI } from '@/di-symbols.js'; +import { deepClone } from '@/misc/clone.js'; const ACTOR_USERNAME = 'relay.actor' as const; @@ -105,7 +106,7 @@ export class RelayService { })); if (relays.length === 0) return; - const copy = structuredClone(activity); + const copy = deepClone(activity); if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public']; const signed = await this.apRendererService.attachLdSignature(copy, user); diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index d9430e1497..e0aeb70dfc 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -9,6 +9,7 @@ import { awaitAll } from '@/misc/prelude/await-all.js'; import type { User } from '@/models/entities/User.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { appendQuery, query } from '@/misc/prelude/url.js'; +import { deepClone } from '@/misc/clone.js'; import { UtilityService } from '../UtilityService.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFolderEntityService } from './DriveFolderEntityService.js'; @@ -55,7 +56,7 @@ export class DriveFileEntityService { public getPublicProperties(file: DriveFile): DriveFile['properties'] { if (file.properties.orientation != null) { - const properties = structuredClone(file.properties); + const properties = deepClone(file.properties); if (file.properties.orientation >= 5) { [properties.width, properties.height] = [properties.height, properties.width]; } diff --git a/packages/backend/src/misc/clone.ts b/packages/backend/src/misc/clone.ts new file mode 100644 index 0000000000..16fad24129 --- /dev/null +++ b/packages/backend/src/misc/clone.ts @@ -0,0 +1,18 @@ +// structredCloneが遅いため +// SEE: http://var.blog.jp/archives/86038606.html + +type Cloneable = string | number | boolean | null | { [key: string]: Cloneable } | Cloneable[]; + +export function deepClone(x: T): T { + if (typeof x === 'object') { + if (x === null) return x; + if (Array.isArray(x)) return x.map(deepClone) as T; + const obj = {} as Record; + for (const [k, v] of Object.entries(x)) { + obj[k] = deepClone(v); + } + return obj as T; + } else { + return x; + } +} diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 44450245a6..8957a91309 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -26,6 +26,7 @@ import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityServi import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import type { ChannelsRepository, ClipsRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import { deepClone } from '@/misc/clone.js'; import manifest from './manifest.json' assert { type: 'json' }; import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; @@ -86,7 +87,7 @@ export class ClientServerService { } private async manifestHandler(ctx: Koa.Context) { - const res = structuredClone(manifest); + const res = deepClone(manifest); const instance = await this.metaService.fetch(true); diff --git a/packages/client/src/components/MkNote.vue b/packages/client/src/components/MkNote.vue index efe786ba4b..97eadb1945 100644 --- a/packages/client/src/components/MkNote.vue +++ b/packages/client/src/components/MkNote.vue @@ -129,6 +129,7 @@ import { $i } from '@/account'; import { i18n } from '@/i18n'; import { getNoteMenu } from '@/scripts/get-note-menu'; import { useNoteCapture } from '@/scripts/use-note-capture'; +import { deepClone } from '@/scripts/clone'; const props = defineProps<{ note: misskey.entities.Note; @@ -137,12 +138,12 @@ const props = defineProps<{ const inChannel = inject('inChannel', null); -let note = $ref(JSON.parse(JSON.stringify(props.note))); +let note = $ref(deepClone(props.note)); // plugin if (noteViewInterruptors.length > 0) { onMounted(async () => { - let result = JSON.parse(JSON.stringify(note)); + let result = deepClone(note); for (const interruptor of noteViewInterruptors) { result = await interruptor.handler(result); } diff --git a/packages/client/src/components/MkNoteDetailed.vue b/packages/client/src/components/MkNoteDetailed.vue index 0bf8f330ba..82468027fd 100644 --- a/packages/client/src/components/MkNoteDetailed.vue +++ b/packages/client/src/components/MkNoteDetailed.vue @@ -139,6 +139,7 @@ import { $i } from '@/account'; import { i18n } from '@/i18n'; import { getNoteMenu } from '@/scripts/get-note-menu'; import { useNoteCapture } from '@/scripts/use-note-capture'; +import { deepClone } from '@/scripts/clone'; const props = defineProps<{ note: misskey.entities.Note; @@ -147,12 +148,12 @@ const props = defineProps<{ const inChannel = inject('inChannel', null); -let note = $ref(JSON.parse(JSON.stringify(props.note))); +let note = $ref(deepClone(props.note)); // plugin if (noteViewInterruptors.length > 0) { onMounted(async () => { - let result = JSON.parse(JSON.stringify(note)); + let result = deepClone(note); for (const interruptor of noteViewInterruptors) { result = await interruptor.handler(result); } diff --git a/packages/client/src/components/MkPostForm.vue b/packages/client/src/components/MkPostForm.vue index 0c57a5a57a..24f2bfb9e6 100644 --- a/packages/client/src/components/MkPostForm.vue +++ b/packages/client/src/components/MkPostForm.vue @@ -89,6 +89,7 @@ import { i18n } from '@/i18n'; import { instance } from '@/instance'; import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account'; import { uploadFile } from '@/scripts/upload'; +import { deepClone } from '@/scripts/clone'; const modal = inject('modal'); @@ -575,7 +576,7 @@ async function post() { // plugin if (notePostInterruptors.length > 0) { for (const interruptor of notePostInterruptors) { - postData = await interruptor.handler(JSON.parse(JSON.stringify(postData))); + postData = await interruptor.handler(deepClone(postData)); } } diff --git a/packages/client/src/pages/settings/reaction.vue b/packages/client/src/pages/settings/reaction.vue index c23c1c2375..f8d57cbcd5 100644 --- a/packages/client/src/pages/settings/reaction.vue +++ b/packages/client/src/pages/settings/reaction.vue @@ -66,8 +66,9 @@ import * as os from '@/os'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { deepClone } from '@/scripts/clone'; -let reactions = $ref(JSON.parse(JSON.stringify(defaultStore.state.reactions))); +let reactions = $ref(deepClone(defaultStore.state.reactions)); const reactionPickerSize = $computed(defaultStore.makeGetterSetter('reactionPickerSize')); const reactionPickerWidth = $computed(defaultStore.makeGetterSetter('reactionPickerWidth')); @@ -101,7 +102,7 @@ async function setDefault() { }); if (canceled) return; - reactions = JSON.parse(JSON.stringify(defaultStore.def.reactions.default)); + reactions = deepClone(defaultStore.def.reactions.default); } function chooseEmoji(ev: MouseEvent) { diff --git a/packages/client/src/pages/settings/statusbar.statusbar.vue b/packages/client/src/pages/settings/statusbar.statusbar.vue index 98a1825b95..608222386e 100644 --- a/packages/client/src/pages/settings/statusbar.statusbar.vue +++ b/packages/client/src/pages/settings/statusbar.statusbar.vue @@ -91,13 +91,14 @@ import FormRange from '@/components/form/range.vue'; import * as os from '@/os'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; +import { deepClone } from '@/scripts/clone'; const props = defineProps<{ _id: string; userLists: any[] | null; }>(); -const statusbar = reactive(JSON.parse(JSON.stringify(defaultStore.state.statusbars.find(x => x.id === props._id)))); +const statusbar = reactive(deepClone(defaultStore.state.statusbars.find(x => x.id === props._id))); watch(() => statusbar.type, () => { if (statusbar.type === 'rss') { @@ -128,8 +129,8 @@ watch(statusbar, save); async function save() { const i = defaultStore.state.statusbars.findIndex(x => x.id === props._id); - const statusbars = JSON.parse(JSON.stringify(defaultStore.state.statusbars)); - statusbars[i] = JSON.parse(JSON.stringify(statusbar)); + const statusbars = deepClone(defaultStore.state.statusbars); + statusbars[i] = deepClone(statusbar); defaultStore.set('statusbars', statusbars); } diff --git a/packages/client/src/scripts/clone.ts b/packages/client/src/scripts/clone.ts new file mode 100644 index 0000000000..16fad24129 --- /dev/null +++ b/packages/client/src/scripts/clone.ts @@ -0,0 +1,18 @@ +// structredCloneが遅いため +// SEE: http://var.blog.jp/archives/86038606.html + +type Cloneable = string | number | boolean | null | { [key: string]: Cloneable } | Cloneable[]; + +export function deepClone(x: T): T { + if (typeof x === 'object') { + if (x === null) return x; + if (Array.isArray(x)) return x.map(deepClone) as T; + const obj = {} as Record; + for (const [k, v] of Object.entries(x)) { + obj[k] = deepClone(v); + } + return obj as T; + } else { + return x; + } +} diff --git a/packages/client/src/scripts/theme.ts b/packages/client/src/scripts/theme.ts index 3f55d9ae86..62a2b9459a 100644 --- a/packages/client/src/scripts/theme.ts +++ b/packages/client/src/scripts/theme.ts @@ -13,6 +13,7 @@ export type Theme = { import lightTheme from '@/themes/_light.json5'; import darkTheme from '@/themes/_dark.json5'; +import { deepClone } from './clone'; export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X')); @@ -60,7 +61,7 @@ export function applyTheme(theme: Theme, persist = true) { const colorSchema = theme.base === 'dark' ? 'dark' : 'light'; // Deep copy - const _theme = JSON.parse(JSON.stringify(theme)); + const _theme = deepClone(theme); if (_theme.base) { const base = [lightTheme, darkTheme].find(x => x.id === _theme.base); diff --git a/packages/client/src/ui/deck/deck-store.ts b/packages/client/src/ui/deck/deck-store.ts index 67fcff4807..56db7398e5 100644 --- a/packages/client/src/ui/deck/deck-store.ts +++ b/packages/client/src/ui/deck/deck-store.ts @@ -4,6 +4,7 @@ import { notificationTypes } from 'misskey-js'; import { Storage } from '../../pizzax'; import { i18n } from '@/i18n'; import { api } from '@/os'; +import { deepClone } from '@/scripts/clone'; type ColumnWidget = { name: string; @@ -25,10 +26,6 @@ export type Column = { tl?: 'home' | 'local' | 'social' | 'global'; }; -function copy(x: T): T { - return JSON.parse(JSON.stringify(x)); -} - export const deckStore = markRaw(new Storage('deck', { profile: { where: 'deviceAccount', @@ -128,7 +125,7 @@ export function swapColumn(a: Column['id'], b: Column['id']) { const aY = deckStore.state.layout[aX].findIndex(id => id === a); const bX = deckStore.state.layout.findIndex(ids => ids.indexOf(b) !== -1); const bY = deckStore.state.layout[bX].findIndex(id => id === b); - const layout = copy(deckStore.state.layout); + const layout = deepClone(deckStore.state.layout); layout[aX][aY] = b; layout[bX][bY] = a; deckStore.set('layout', layout); @@ -136,7 +133,7 @@ export function swapColumn(a: Column['id'], b: Column['id']) { } export function swapLeftColumn(id: Column['id']) { - const layout = copy(deckStore.state.layout); + const layout = deepClone(deckStore.state.layout); deckStore.state.layout.some((ids, i) => { if (ids.includes(id)) { const left = deckStore.state.layout[i - 1]; @@ -152,7 +149,7 @@ export function swapLeftColumn(id: Column['id']) { } export function swapRightColumn(id: Column['id']) { - const layout = copy(deckStore.state.layout); + const layout = deepClone(deckStore.state.layout); deckStore.state.layout.some((ids, i) => { if (ids.includes(id)) { const right = deckStore.state.layout[i + 1]; @@ -168,9 +165,9 @@ export function swapRightColumn(id: Column['id']) { } export function swapUpColumn(id: Column['id']) { - const layout = copy(deckStore.state.layout); + const layout = deepClone(deckStore.state.layout); const idsIndex = deckStore.state.layout.findIndex(ids => ids.includes(id)); - const ids = copy(deckStore.state.layout[idsIndex]); + const ids = deepClone(deckStore.state.layout[idsIndex]); ids.some((x, i) => { if (x === id) { const up = ids[i - 1]; @@ -188,9 +185,9 @@ export function swapUpColumn(id: Column['id']) { } export function swapDownColumn(id: Column['id']) { - const layout = copy(deckStore.state.layout); + const layout = deepClone(deckStore.state.layout); const idsIndex = deckStore.state.layout.findIndex(ids => ids.includes(id)); - const ids = copy(deckStore.state.layout[idsIndex]); + const ids = deepClone(deckStore.state.layout[idsIndex]); ids.some((x, i) => { if (x === id) { const down = ids[i + 1]; @@ -208,7 +205,7 @@ export function swapDownColumn(id: Column['id']) { } export function stackLeftColumn(id: Column['id']) { - let layout = copy(deckStore.state.layout); + let layout = deepClone(deckStore.state.layout); const i = deckStore.state.layout.findIndex(ids => ids.includes(id)); layout = layout.map(ids => ids.filter(_id => _id !== id)); layout[i - 1].push(id); @@ -218,7 +215,7 @@ export function stackLeftColumn(id: Column['id']) { } export function popRightColumn(id: Column['id']) { - let layout = copy(deckStore.state.layout); + let layout = deepClone(deckStore.state.layout); const i = deckStore.state.layout.findIndex(ids => ids.includes(id)); const affected = layout[i]; layout = layout.map(ids => ids.filter(_id => _id !== id)); @@ -226,7 +223,7 @@ export function popRightColumn(id: Column['id']) { layout = layout.filter(ids => ids.length > 0); deckStore.set('layout', layout); - const columns = copy(deckStore.state.columns); + const columns = deepClone(deckStore.state.columns); for (const column of columns) { if (affected.includes(column.id)) { column.active = true; @@ -238,9 +235,9 @@ export function popRightColumn(id: Column['id']) { } export function addColumnWidget(id: Column['id'], widget: ColumnWidget) { - const columns = copy(deckStore.state.columns); + const columns = deepClone(deckStore.state.columns); const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); - const column = copy(deckStore.state.columns[columnIndex]); + const column = deepClone(deckStore.state.columns[columnIndex]); if (column == null) return; if (column.widgets == null) column.widgets = []; column.widgets.unshift(widget); @@ -250,9 +247,9 @@ export function addColumnWidget(id: Column['id'], widget: ColumnWidget) { } export function removeColumnWidget(id: Column['id'], widget: ColumnWidget) { - const columns = copy(deckStore.state.columns); + const columns = deepClone(deckStore.state.columns); const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); - const column = copy(deckStore.state.columns[columnIndex]); + const column = deepClone(deckStore.state.columns[columnIndex]); if (column == null) return; column.widgets = column.widgets.filter(w => w.id !== widget.id); columns[columnIndex] = column; @@ -261,9 +258,9 @@ export function removeColumnWidget(id: Column['id'], widget: ColumnWidget) { } export function setColumnWidgets(id: Column['id'], widgets: ColumnWidget[]) { - const columns = copy(deckStore.state.columns); + const columns = deepClone(deckStore.state.columns); const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); - const column = copy(deckStore.state.columns[columnIndex]); + const column = deepClone(deckStore.state.columns[columnIndex]); if (column == null) return; column.widgets = widgets; columns[columnIndex] = column; @@ -272,9 +269,9 @@ export function setColumnWidgets(id: Column['id'], widgets: ColumnWidget[]) { } export function updateColumnWidget(id: Column['id'], widgetId: string, widgetData: any) { - const columns = copy(deckStore.state.columns); + const columns = deepClone(deckStore.state.columns); const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); - const column = copy(deckStore.state.columns[columnIndex]); + const column = deepClone(deckStore.state.columns[columnIndex]); if (column == null) return; column.widgets = column.widgets.map(w => w.id === widgetId ? { ...w, @@ -286,9 +283,9 @@ export function updateColumnWidget(id: Column['id'], widgetId: string, widgetDat } export function updateColumn(id: Column['id'], column: Partial) { - const columns = copy(deckStore.state.columns); + const columns = deepClone(deckStore.state.columns); const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); - const currentColumn = copy(deckStore.state.columns[columnIndex]); + const currentColumn = deepClone(deckStore.state.columns[columnIndex]); if (currentColumn == null) return; for (const [k, v] of Object.entries(column)) { currentColumn[k] = v; diff --git a/packages/client/src/widgets/job-queue.vue b/packages/client/src/widgets/job-queue.vue index 8897f240bd..363d1b3ea0 100644 --- a/packages/client/src/widgets/job-queue.vue +++ b/packages/client/src/widgets/job-queue.vue @@ -47,12 +47,13 @@