diff --git a/locales/index.d.ts b/locales/index.d.ts index 39a867b8c484..67e228bd665d 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -10683,6 +10683,115 @@ export interface Locale extends ILocale { */ readonly "caption": string; }; + readonly "_backupAndSyncingCustomCss": { + readonly "_backup": { + /** + * カスタムCSSのバックアップ + */ + readonly "title": string; + /** + * 現在のカスタムCSSをバックアップとしてサーバーに保存することが可能です。 + */ + readonly "description": string; + /** + * バックアップがありません。「新規作成」で現在適用されているカスタムCSSのバックアップを作成できます。 + */ + readonly "notfound": string; + /** + * 作成日時: {datetime} + */ + readonly "createdAt": ParameterizedString<"datetime">; + /** + * バックアップの読み込みに失敗しました。 + */ + readonly "cannnotLoad": string; + /** + * バックアップファイルはjson形式である必要があります。 + */ + readonly "invalidFile": string; + /** + * バックアップ名が不正です。 + */ + readonly "invalidName": string; + /** + * バックアップのカスタムCSSが不正です。 + */ + readonly "invalidCustomCss": string; + /** + * カスタムCSSが設定されていません + */ + readonly "noCustomCss": string; + /** + * カスタムCSSを設定されていない状態ではバックアップを作成できません。 + */ + readonly "noCustomCssDescription": string; + /** + * バックアップ名を入力 + */ + readonly "inputBackupName": string; + /** + * カスタムCSSのバックアップを適用 + */ + readonly "applyBackup": string; + /** + * カスタムCSSのバックアップ「{name}」を適用しますか? + */ + readonly "applyBackupDescription": ParameterizedString<"name">; + /** + * カスタムCSSを設定されていない状態では上書き保存できません。 + * カスタムCSSのバックアップを削除する場合はメニューから「削除」を選択してください。 + */ + readonly "cannotOverrideEmpty": string; + /** + * カスタムCSSのバックアップを上書き保存 + */ + readonly "overrideBackup": string; + /** + * カスタムCSSのバックアップ「{name}」を上書き保存しますか? + * この操作は取り消せません。 + */ + readonly "overrideBackupDescription": ParameterizedString<"name">; + /** + * カスタムCSSのバックアップを削除 + */ + readonly "deleteBackup": string; + /** + * カスタムCSSのバックアップ「{name}」を削除しますか? + * この操作は取り消せません。 + */ + readonly "deleteBackupDescription": ParameterizedString<"name">; + /** + * 上書き保存 + */ + readonly "override": string; + /** + * バックアップ内容を見る + */ + readonly "preview": string; + }; + readonly "_syncing": { + /** + * カスタムCSSの同期 + */ + readonly "title": string; + /** + * カスタムCSSの同期を有効化すると、カスタムCSSの変更がリアルタイムに同期されます。 + */ + readonly "description": string; + /** + * カスタムCSSの同期を有効化 + */ + readonly "enable": string; + /** + * 同期するカスタムCSSを選択 + */ + readonly "select": string; + /** + * 任意のカスタムCSSのバックアップを他のデバイスとの間で同期することができます。 + */ + readonly "selectDescription": string; + }; + }; }; readonly "_admin": { /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index c4a3f2a983c7..9171b5910b2d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2848,6 +2848,34 @@ _tms: _preventLongPressContextMenu: label: "長押しによるコンテキストメニューイベントの発行を防ぐ" caption: "長押しを含む操作が中断される問題を解消します。" + _backupAndSyncingCustomCss: + _backup: + title: "カスタムCSSのバックアップ" + description: "現在のカスタムCSSをバックアップとしてサーバーに保存することが可能です。" + notfound: "バックアップがありません。「新規作成」で現在適用されているカスタムCSSのバックアップを作成できます。" + createdAt: "作成日時: {datetime}" + cannnotLoad: "バックアップの読み込みに失敗しました。" + invalidFile: "バックアップファイルはjson形式である必要があります。" + invalidName: "バックアップ名が不正です。" + invalidCustomCss: "バックアップのカスタムCSSが不正です。" + noCustomCss: "カスタムCSSが設定されていません" + noCustomCssDescription: "カスタムCSSを設定されていない状態ではバックアップを作成できません。" + inputBackupName: "バックアップ名を入力" + applyBackup: "カスタムCSSのバックアップを適用" + applyBackupDescription: "カスタムCSSのバックアップ「{name}」を適用しますか?" + cannotOverrideEmpty: "カスタムCSSを設定されていない状態では上書き保存できません。\nカスタムCSSのバックアップを削除する場合はメニューから「削除」を選択してください。" + overrideBackup: "カスタムCSSのバックアップを上書き保存" + overrideBackupDescription: "カスタムCSSのバックアップ「{name}」を上書き保存しますか?\nこの操作は取り消せません。" + deleteBackup: "カスタムCSSのバックアップを削除" + deleteBackupDescription: "カスタムCSSのバックアップ「{name}」を削除しますか?\nこの操作は取り消せません。" + override: "上書き保存" + preview: "バックアップ内容を見る" + _syncing: + title: "カスタムCSSの同期" + description: "カスタムCSSの同期を有効化すると、カスタムCSSの変更がリアルタイムに同期されます。" + enable: "カスタムCSSの同期を有効化" + select: "同期するカスタムCSSを選択" + selectDescription: "任意のカスタムCSSのバックアップを他のデバイスとの間で同期することができます。" _admin: repositoryUrlDescription: "ソースコードが公開されているリポジトリがある場合、そのURLを記入します。taiymeを現状のまま(ソースコードにいかなる変更も加えずに)使用している場合は https://github.com/taiyme/misskey と記入します。" _search: diff --git a/packages/backend/src/server/web/boot.embed.js b/packages/backend/src/server/web/boot.embed.js index 173f7b0222df..905ccb8cf75f 100644 --- a/packages/backend/src/server/web/boot.embed.js +++ b/packages/backend/src/server/web/boot.embed.js @@ -105,10 +105,10 @@ } //#endregion - async function addStyle(styleText) { - let css = document.createElement('style'); - css.appendChild(document.createTextNode(styleText)); - document.head.appendChild(css); + function addStyle(styleText) { + const styleTag = document.createElement('style'); + styleTag.textContent = styleText; + document.head.appendChild(styleTag); } async function renderError(code) { diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index 259a2b2eeea9..0b6031e74dd5 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -136,15 +136,16 @@ const customCss = localStorage.getItem('customCss'); if (customCss && customCss.length > 0) { - const style = document.createElement('style'); - style.innerHTML = customCss; - document.head.appendChild(style); + const styleTag = document.createElement('style'); + styleTag.textContent = customCss; + styleTag.id = 'custom_css'; + document.head.appendChild(styleTag); } - async function addStyle(styleText) { - let css = document.createElement('style'); - css.appendChild(document.createTextNode(styleText)); - document.head.appendChild(css); + function addStyle(styleText) { + const styleTag = document.createElement('style'); + styleTag.textContent = styleText; + document.head.appendChild(styleTag); } async function renderError(code, details) { diff --git a/packages/frontend/src/_dev_boot_.ts b/packages/frontend/src/_dev_boot_.ts index 1601f247d759..9b1b812a8c8a 100644 --- a/packages/frontend/src/_dev_boot_.ts +++ b/packages/frontend/src/_dev_boot_.ts @@ -79,9 +79,10 @@ async function main() { const customCss = localStorage.getItem('customCss'); if (customCss && customCss.length > 0) { - const style = document.createElement('style'); - style.innerHTML = customCss; - document.head.appendChild(style); + const styleTag = document.createElement('style'); + styleTag.textContent = customCss; + styleTag.id = 'custom_css'; + document.head.appendChild(styleTag); } } diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index 0747678caf44..343aa39419c5 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -6,6 +6,7 @@ import { computed, watch, version as vueVersion, App } from 'vue'; import { compareVersions } from 'compare-versions'; import { version, lang, updateLocale, locale, commitHash } from '@@/js/config.js'; +import type { CustomCSSBackup } from '@/pages/tms/backup-and-syncing-custom-css/backup.vue'; import widgets from '@/widgets/index.js'; import directives from '@/directives/index.js'; import components from '@/components/index.js'; @@ -26,6 +27,7 @@ import { setupRouter } from '@/router/main.js'; import { createMainRouter } from '@/router/definition.js'; import { tmsFlaskStore } from '@/tms/flask-store.js'; import { tmsStore } from '@/tms/store.js'; +import { useStream } from '@/stream.js'; import { preventLongPressContextMenu } from '@/scripts/tms/prevent-longpress-contextmenu.js'; export async function common(createVue: () => App) { @@ -120,10 +122,10 @@ export async function common(createVue: () => App) { html.setAttribute('lang', lang); //#endregion - await defaultStore.ready; - await deckStore.ready; - await tmsStore.ready; - await tmsFlaskStore.ready; + await defaultStore.loaded; + await deckStore.loaded; + await tmsStore.loaded; + await tmsFlaskStore.loaded; if (tmsFlaskStore.state.preventLongPressContextMenu) { preventLongPressContextMenu(); @@ -213,6 +215,31 @@ export async function common(createVue: () => App) { } }, { immediate: true }); + // tms custom css syncing + const enabledCustomCssSyncing = tmsFlaskStore.state.enabledCustomCssSyncing; + if (enabledCustomCssSyncing) { + const syncingCustomCssId = tmsFlaskStore.state.syncingCustomCssId; + const connection = useStream().useChannel('main'); + const scope = ['tms', 'customCssBackups'] as const satisfies string[]; + + connection.on('registryUpdated', ({ scope: recievedScope, key, value }) => { + if (scope.join('/') !== recievedScope?.join('/')) return; + if (key !== syncingCustomCssId) return; + + const { customCss } = value as unknown as CustomCSSBackup; + miLocalStorage.setItem('customCss', customCss); + + let styleTag = document.getElementById('custom_css'); + if (styleTag == null) { + styleTag = document.createElement('style'); + styleTag.id = 'custom_css'; + document.head.appendChild(styleTag); + } + + styleTag.textContent = customCss; + }); + } + // Keep screen on const onVisibilityChange = () => document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { diff --git a/packages/frontend/src/components/TmsCodePreviewDialog.vue b/packages/frontend/src/components/TmsCodePreviewDialog.vue new file mode 100644 index 000000000000..22590014ef05 --- /dev/null +++ b/packages/frontend/src/components/TmsCodePreviewDialog.vue @@ -0,0 +1,81 @@ + + + + + + + + + diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue index c35bd62ec9e5..4f96e605c3ed 100644 --- a/packages/frontend/src/pages/settings/custom-css.vue +++ b/packages/frontend/src/pages/settings/custom-css.vue @@ -20,6 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only + + diff --git a/packages/frontend/src/pages/tms/backup-and-syncing-custom-css/main.vue b/packages/frontend/src/pages/tms/backup-and-syncing-custom-css/main.vue new file mode 100644 index 000000000000..4cab1cedd4ae --- /dev/null +++ b/packages/frontend/src/pages/tms/backup-and-syncing-custom-css/main.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/packages/frontend/src/pages/tms/backup-and-syncing-custom-css/sync.vue b/packages/frontend/src/pages/tms/backup-and-syncing-custom-css/sync.vue new file mode 100644 index 000000000000..a515010f5157 --- /dev/null +++ b/packages/frontend/src/pages/tms/backup-and-syncing-custom-css/sync.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/packages/frontend/src/pages/tms/flags/index.main.vue b/packages/frontend/src/pages/tms/flags/index.main.vue index a0778252c5b2..238bca90a800 100644 --- a/packages/frontend/src/pages/tms/flags/index.main.vue +++ b/packages/frontend/src/pages/tms/flags/index.main.vue @@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only