diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-03-09 12:34:08 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-03-09 12:34:08 +0900 |
| commit | d30ddd4c2ebcacc0d0b49c74e8dfe05b5422ba2e (patch) | |
| tree | c0c87a30037d3ffc11784627e67a1965b262c336 /packages/frontend/src/scripts | |
| parent | [skip ci] Update CHANGELOG.md (prepend template) (diff) | |
| download | sharkey-d30ddd4c2ebcacc0d0b49c74e8dfe05b5422ba2e.tar.gz sharkey-d30ddd4c2ebcacc0d0b49c74e8dfe05b5422ba2e.tar.bz2 sharkey-d30ddd4c2ebcacc0d0b49c74e8dfe05b5422ba2e.zip | |
Refine preferences (#15597)
* wip
* wip
* wip
* test
* wip rollup pluginでsearchIndexの情報生成
* wip
* SPDX
* wip: markerIdを自動付与
* rollupでビルド時・devモード時に毎回uuidを生成するように
* 開発サーバーでだけ必要な挙動は開発サーバーのみで
* 条件が逆
* wip: childrenの生成
* update comment
* update comment
* rename auto generated file
* hashをパスと行数から決定
* Update privacy.vue
* Update privacy.vue
* wip
* Update general.vue
* Update general.vue
* wip
* wip
* Update SearchMarker.vue
* wip
* Update profile.vue
* Update mute-block.vue
* Update mute-block.vue
* Update general.vue
* Update general.vue
* childrenがduplicate key errorを吐く問題をいったん解決
* マーカーの形を成形
* loggerを置きかえ
* とりあえず省略記法に対応
* Refactor and Format codes
* wip
* Update settings-search-index.ts
* wip
* wip
* とりあえず不確定要因の仮置きidを削除
* hashの生成を正規化(絶対パスになっていたのを緩和)
* pathの入力を省略可能に
* adminでもパス生成できるように
* Update settings-search-index.ts
* Update privacy.vue
* wip
* build searchIndex
* wip
* build
* Update general.vue
* build
* Update sounds.vue
* build
* build
* Update sounds.vue
* 🎨
* 🎨
* Update privacy.vue
* Update privacy.vue
* Update security.vue
* create-search-indexを多少改善
* build
* Update 2fa.vue
* wip
* 必ずtransformCodeCacheを利用するように, キャッシュの明確な受け渡しを定義
* キャッシュはdevServerでなくても更新
* Revert "wip"
This reverts commit 41bffd3a13f55618bf939dc1c9acb2a77ead4054.
* inlining
* wip
* Update theme.vue
* 🎨
* wip normalize
* Update theme.vue
* キャッシュのパス変換
* build
* wip
* wip
* Update SearchMarker.vue
* i18n.ts['key'] の形式が取り出せない問題のFix
* build
* 仮でpath入れ
* 必ず絶対パスが使われるように
* wip
* 🎨
* storybookビルド時はcreateSearchIndexをしない
* inliningの構造化
* format code
* Update index.vue
* wip
* wip
* 🎨
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* clean up
* wip
* wip
* wip
* Update rollup-plugin-unwind-css-module-class-name.test.ts
* Update navbar.vue
* clean up
* wip
* wip
* wip
* wip
* wip
* Update preferences-backups.vue
* Update common.ts
* Update preferences.ts
* wip
* wip
* wip
* wip
* Update MkPreferenceContainer.vue
* Update MkPreferenceContainer.vue
* Update MkPreferenceContainer.vue
* enhance: 検索で上下矢印を使用することで検索結果を移動できるように
* Update main-boot.ts
* refactor
* wip
* Update sounds.vue
* fix(frontend): PageWindowでSearchMarkerが動作するように
* enhance(frontend): SearchMarkerの点滅を一定時間で止める
* wip
* lint fix
* fix: 子要素監視が抜けていたのを修正
* アニメーションの回数はCSSで制御するように
* refactor
* enhance(frontend): 検索インデックス作成時のログを削減
* revert
* fix
* fix
* Update preferences.ts
* Update preferences.ts
* wip
* Update preferences.ts
* wip
* 🎨
* wip
* Update MkPreferenceContainer.vue
* wip
* Update preferences.ts
* wip
* Update preferences.ts
* Update preferences.ts
* wip
* wip
* Update preferences.ts
* wip
* wip
* Update preferences.ts
* Update CHANGELOG.md
* Update preferences.ts
* Update deck-store.ts
* deckStoreをdefaultStoreに統合
* wip
* defaultStore -> store
* Update profile.ts
* wip
* refactor
* wip: plugin
* plugin
* plugin
* plugin
* Update plugin.ts
* wip
* Update plugin.vue
* Update preferences.ts
* Update main-boot.ts
* wip
* fix test
* Update plugin.vue
* Update plugin.vue
* Update utility.ts
* wip
* wip
* Update utility.ts
* wip
* wip
* clean up
* Update utility.ts
---------
Co-authored-by: tai-cha <dev@taichan.site>
Co-authored-by: taichan <40626578+tai-cha@users.noreply.github.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
Diffstat (limited to 'packages/frontend/src/scripts')
| -rw-r--r-- | packages/frontend/src/scripts/autogen/settings-search-index.ts | 141 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/code-highlighter.ts | 32 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/emoji-picker.ts | 4 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/get-drive-file-menu.ts | 6 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/get-note-menu.ts | 15 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/get-user-menu.ts | 13 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/init-chart.ts | 4 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/install-plugin.ts | 131 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/install-theme.ts | 38 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/reaction-picker.ts | 4 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/select-file.ts | 10 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/sound.ts | 23 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/theme.ts | 32 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/upload.ts | 8 |
14 files changed, 168 insertions, 293 deletions
diff --git a/packages/frontend/src/scripts/autogen/settings-search-index.ts b/packages/frontend/src/scripts/autogen/settings-search-index.ts index c62272b271..983bc07d38 100644 --- a/packages/frontend/src/scripts/autogen/settings-search-index.ts +++ b/packages/frontend/src/scripts/autogen/settings-search-index.ts @@ -57,12 +57,12 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['mute'], }, { - id: 'xy5OOBB4A', + id: 'oALW4ja7U', label: i18n.ts.useSoundOnlyWhenActive, keywords: ['active', 'mute'], }, { - id: '9MxYVIf7k', + id: 'BbJK2SKT2', label: i18n.ts.masterVolume, keywords: ['volume', 'master'], }, @@ -267,15 +267,10 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['remember', 'keep', 'note', 'visibility'], }, { - id: 'rhKwScbVS', + id: '1u5HZuujV', label: i18n.ts.defaultNoteVisibility, keywords: ['default', 'note', 'visibility'], }, - { - id: '3EmXVyevo', - label: i18n.ts.keepCw, - keywords: ['remember', 'keep', 'note', 'cw'], - }, ], label: i18n.ts.privacy, keywords: ['privacy'], @@ -301,50 +296,50 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['post', 'form', 'timeline'], }, { - id: '9ra14w32V', + id: 'snyCQ5oKE', label: i18n.ts.showFixedPostFormInChannel, keywords: ['post', 'form', 'timeline', 'channel'], }, { - id: '84MdeDWL1', + id: '8j36S4Ev6', label: i18n.ts.pinnedList, keywords: ['pinned', 'list'], }, { - id: 'fYdWhBbrN', + id: 'CWpyT9vLK', label: i18n.ts.enableQuickAddMfmFunction, keywords: ['mfm', 'enable', 'show', 'advanced', 'picker', 'form', 'function', 'fn'], }, { - id: '4huRldNp5', + id: 'puIqj1a8b', children: [ { - id: 'puIqj1a8b', + id: '1x3JNXj8N', label: i18n.ts.collapseRenotes, keywords: ['renote', i18n.ts.collapseRenotesDescription], }, { - id: 'wqpOC22Zm', + id: 'c98gbF9c6', label: i18n.ts.showNoteActionsOnlyHover, keywords: ['hover', 'show', 'footer', 'action'], }, { - id: 'cjfAtxMzP', + id: '4LxdiOMNh', label: i18n.ts.showClipButtonInNoteFooter, keywords: ['footer', 'action', 'clip', 'show'], }, { - id: 'khzxoCjtp', + id: '9gTCaLkIf', label: i18n.ts.enableAdvancedMfm, keywords: ['mfm', 'enable', 'show', 'advanced'], }, { - id: 'uJkoVjTmF', + id: '6kMj4HVOg', label: i18n.ts.showReactionsCount, keywords: ['reaction', 'count', 'show'], }, { - id: '9gTCaLkIf', + id: 'dPersnkzh', label: i18n.ts.loadRawImages, keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'quality', 'raw', 'attachment'], }, @@ -353,10 +348,10 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['note'], }, { - id: '5G6O6qdis', + id: '5XhS6ukl8', children: [ { - id: 'sYTvqUbhP', + id: '3GcWIaZf8', label: i18n.ts.useGroupedNotifications, keywords: ['group'], }, @@ -365,55 +360,60 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['notification'], }, { - id: 'c3xhLyXZ5', + id: 'dSGDnj2PA', children: [ { - id: 'FbhoeuRAD', + id: '1LHOhDKGW', label: i18n.ts.openImageInNewTab, keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'], }, { - id: 'qixh85g2N', + id: 'DSzwvTp7i', label: i18n.ts.useReactionPickerForContextMenu, keywords: ['reaction', 'picker', 'contextmenu', 'open'], }, { - id: 'd2H4E5ys6', + id: '5QTUzrpT3', label: i18n.ts.enableInfiniteScroll, keywords: ['load', 'auto', 'more'], }, { - id: 'jC7LtTnmc', + id: '7Uf8ksn3q', label: i18n.ts.disableStreamingTimeline, keywords: ['disable', 'streaming', 'timeline'], }, { - id: '8xazEqlgZ', + id: 'whKYKvaQB', label: i18n.ts.alwaysConfirmFollow, keywords: ['follow', 'confirm', 'always'], }, { - id: 'wZqrDQZar', + id: 'nf4kcPeYw', label: i18n.ts.confirmWhenRevealingSensitiveMedia, keywords: ['sensitive', 'nsfw', 'media', 'image', 'photo', 'picture', 'attachment', 'confirm'], }, { - id: '5QTUzrpT3', + id: 'rRisK1YYQ', label: i18n.ts.confirmOnReact, keywords: ['reaction', 'confirm'], }, { - id: 'nygexkaUk', + id: '6AH0lnjf1', + label: i18n.ts.keepCw, + keywords: ['remember', 'keep', 'note', 'cw'], + }, + { + id: 'uHcTVSGDv', label: i18n.ts.whenServerDisconnected, keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'], }, { - id: 'whKYKvaQB', + id: 'fzPca1Gk9', label: i18n.ts.numberOfPageCache, keywords: ['cache', 'page'], }, { - id: 'lBbtAg0Hm', + id: 'mNU5IBln7', label: i18n.ts.dataSaver, keywords: ['datasaver'], }, @@ -422,20 +422,20 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['behavior'], }, { - id: 'y2v7CV9zs', + id: 'C3psHYdZn', children: [ { - id: 'k1qTdyfzM', + id: 'iCEiAg4Wg', label: i18n.ts.forceShowAds, keywords: ['ad', 'show'], }, { - id: 'e9As4Us48', + id: 'qj9eChQ5B', label: i18n.ts.hemisphere, keywords: [], }, { - id: 'zvM13vl26', + id: 'uItIge5hw', label: i18n.ts.additionalEmojiDictionary, keywords: ['emoji', 'dictionary', 'additional', 'extra'], }, @@ -450,6 +450,13 @@ export const searchIndexes: SearchIndexItem[] = [ icon: 'ti ti-adjustments', }, { + id: 'mwkwtw83Y', + label: i18n.ts.plugins, + keywords: ['plugin'], + path: '/settings/plugin', + icon: 'ti ti-plug', + }, + { id: 'F1uK9ssiY', children: [ { @@ -626,17 +633,17 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['keep', 'original', 'raw', 'upload', i18n.ts.keepOriginalUploadingDescription], }, { - id: 'oqUiI5w0s', + id: 'D8HUTGWE1', label: i18n.ts.keepOriginalFilename, keywords: ['keep', 'original', 'filename', i18n.ts.keepOriginalFilenameDescription], }, { - id: 'Aszkikq9n', + id: '6xAvsWSZi', label: i18n.ts.alwaysMarkSensitive, keywords: ['always', 'default', 'mark', 'nsfw', 'sensitive', 'media', 'file'], }, { - id: 'iGlVjsfVj', + id: 'csNNPF1KX', label: i18n.ts.enableAutoSensitive, keywords: ['auto', 'nsfw', 'sensitive', 'media', 'file', i18n.ts.enableAutoSensitiveDescription], }, @@ -662,80 +669,80 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['blur'], }, { - id: 'vbZvyLDC1', + id: 'C05WQNSIJ', label: i18n.ts.useBlurEffectForModal, keywords: ['blur', 'modal'], }, { - id: '6fLNMTwNt', + id: 'snVKNr7Bw', label: i18n.ts.highlightSensitiveMedia, keywords: ['highlight', 'sensitive', 'nsfw', 'image', 'photo', 'picture', 'media', 'thumbnail'], }, { - id: 'hhvF8Z4pF', + id: 'DsS2CwjYE', label: i18n.ts.squareAvatars, keywords: ['avatar', 'icon', 'square'], }, { - id: 'DsS2CwjYE', + id: 'xCcTDl651', label: i18n.ts.showAvatarDecorations, keywords: ['avatar', 'icon', 'decoration', 'show'], }, { - id: 'pWZ0ypy2g', + id: '3dHw723VD', label: i18n.ts.showGapBetweenNotesInTimeline, keywords: ['note', 'timeline', 'gap'], }, { - id: 'AfRMcC6IM', - label: i18n.ts.useSystemFont, - keywords: ['font', 'system', 'native'], - }, - { - id: 'jD0qbxlzN', + id: 'AWi72xbrl', label: i18n.ts.seasonalScreenEffect, keywords: ['effect', 'show'], }, { - id: 'EdYo3hOK', + id: 'Ces8FsJws', label: i18n.ts.menuStyle, keywords: ['menu', 'style', 'popup', 'drawer'], }, { - id: '9mSlX0EkD', + id: 'wDr9xSXCv', label: i18n.ts.emojiStyle, keywords: ['emoji', 'style', 'native', 'system', 'fluent', 'twemoji'], }, { - id: '44UmMwmUe', + id: 'vFB0pLzck', label: i18n.ts.fontSize, keywords: ['font', 'size'], }, { - id: 'vFB0pLzck', + id: '23BhvYXPC', + label: i18n.ts.useSystemFont, + keywords: ['font', 'system', 'native'], + }, + { + id: 'EeNLndAOa', children: [ { - id: 'pc7IpPEU4', + id: 'rAAPoaodS', label: i18n.ts.reactionsDisplaySize, keywords: ['reaction', 'size', 'scale', 'display'], }, { - id: 'siOW5aSwp', + id: 'qTLAvNWsc', label: i18n.ts.limitWidthOfReaction, keywords: ['reaction', 'size', 'scale', 'display', 'width', 'limit'], }, { - id: 'dDUvhk13F', + id: '2lWgzAm13', label: i18n.ts.mediaListWithOneImageAppearance, keywords: ['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'list', 'size', 'height'], }, { - id: 'CLxNL1Rp0', + id: 'EU7HbxOR5', label: i18n.ts.instanceTicker, keywords: ['ticker', 'information', 'label', 'instance', 'server', 'host', 'federation'], }, { - id: 'dP2KWDYzD', + id: 'AEtM0FAp1', label: i18n.ts.displayOfSensitiveMedia, keywords: ['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'nsfw', 'sensitive', 'display', 'show', 'hide', 'visibility'], }, @@ -744,15 +751,15 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['note', 'display'], }, { - id: 'dVOzi22IW', + id: 'A1FMC2Zon', children: [ { - id: 'aoF4ufUwn', + id: 'CB37G5ZDo', label: i18n.ts.position, keywords: ['position'], }, { - id: 'sKK2XSS69', + id: 'gGS2i19hS', label: i18n.ts.stackAxis, keywords: ['stack', 'axis', 'direction'], }, @@ -775,32 +782,32 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['animation', 'motion', 'reduce'], }, { - id: 'RhYwm8At', + id: 'cXr3tFdpa', label: i18n.ts.disableShowingAnimatedImages, keywords: ['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif'], }, { - id: '5mZxz2cru', + id: 'Ok1UBwtP', label: i18n.ts.enableAnimatedMfm, keywords: ['mfm', 'enable', 'show', 'animated'], }, { - id: 'bgjamYEis', + id: 'yPEpJigqY', label: i18n.ts.enableHorizontalSwipe, keywords: ['swipe', 'horizontal', 'tab'], }, { - id: 'yPEpJigqY', + id: 'h7iZtdTU3', label: i18n.ts.keepScreenOn, keywords: ['keep', 'screen', 'display', 'on'], }, { - id: 'oxwiGKMu0', + id: 'gP1BY3PDy', label: i18n.ts.useNativeUIForVideoAudioPlayer, keywords: ['native', 'system', 'video', 'audio', 'player', 'media'], }, { - id: 'n90tffyiU', + id: 'jnMK3M6rs', label: i18n.ts._contextMenu.title, keywords: ['contextmenu', 'system', 'native'], }, diff --git a/packages/frontend/src/scripts/code-highlighter.ts b/packages/frontend/src/scripts/code-highlighter.ts index 4d57dcd944..4f2aff9d4c 100644 --- a/packages/frontend/src/scripts/code-highlighter.ts +++ b/packages/frontend/src/scripts/code-highlighter.ts @@ -10,18 +10,20 @@ import { bundledThemesInfo } from 'shiki/themes'; import { bundledLanguagesInfo } from 'shiki/langs'; import lightTheme from '@@/themes/_light.json5'; import darkTheme from '@@/themes/_dark.json5'; +import defaultLightTheme from '@@/themes/l-light.json5'; +import defaultDarkTheme from '@@/themes/d-green-lime.json5'; import { unique } from './array.js'; import { deepClone } from './clone.js'; import { deepMerge } from './merge.js'; import type { HighlighterCore, LanguageRegistration, ThemeRegistration, ThemeRegistrationRaw } from 'shiki/core'; -import { ColdDeviceStorage } from '@/store.js'; +import { prefer } from '@/preferences.js'; let _highlighter: HighlighterCore | null = null; export async function getTheme(mode: 'light' | 'dark', getName: true): Promise<string>; export async function getTheme(mode: 'light' | 'dark', getName?: false): Promise<ThemeRegistration | ThemeRegistrationRaw>; export async function getTheme(mode: 'light' | 'dark', getName = false): Promise<ThemeRegistration | ThemeRegistrationRaw | string | null> { - const theme = deepClone(ColdDeviceStorage.get(mode === 'light' ? 'lightTheme' : 'darkTheme')); + const theme = deepClone(mode === 'light' ? prefer.s.lightTheme ?? defaultLightTheme : prefer.s.darkTheme ?? defaultDarkTheme); if (theme.base) { const base = [lightTheme, darkTheme].find(x => x.id === theme.base); @@ -77,19 +79,19 @@ async function initHighlighter() { ], }); - ColdDeviceStorage.watch('lightTheme', async () => { - const newTheme = await getTheme('light'); - if (newTheme.name && !highlighter.getLoadedThemes().includes(newTheme.name)) { - highlighter.loadTheme(newTheme); - } - }); - - ColdDeviceStorage.watch('darkTheme', async () => { - const newTheme = await getTheme('dark'); - if (newTheme.name && !highlighter.getLoadedThemes().includes(newTheme.name)) { - highlighter.loadTheme(newTheme); - } - }); + // TODO + //watch('lightTheme', async () => { + // const newTheme = await getTheme('light'); + // if (newTheme.name && !highlighter.getLoadedThemes().includes(newTheme.name)) { + // highlighter.loadTheme(newTheme); + // } + //}); + //watch('darkTheme', async () => { + // const newTheme = await getTheme('dark'); + // if (newTheme.name && !highlighter.getLoadedThemes().includes(newTheme.name)) { + // highlighter.loadTheme(newTheme); + // } + //}); _highlighter = highlighter; diff --git a/packages/frontend/src/scripts/emoji-picker.ts b/packages/frontend/src/scripts/emoji-picker.ts index e704b5fd6f..7ff5863243 100644 --- a/packages/frontend/src/scripts/emoji-picker.ts +++ b/packages/frontend/src/scripts/emoji-picker.ts @@ -6,7 +6,7 @@ import { defineAsyncComponent, ref } from 'vue'; import type { Ref } from 'vue'; import { popup } from '@/os.js'; -import { defaultStore } from '@/store.js'; +import { store } from '@/store.js'; /** * 絵文字ピッカーを表示する。 @@ -25,7 +25,7 @@ class EmojiPicker { } public async init() { - const emojisRef = defaultStore.reactiveState.pinnedEmojis; + const emojisRef = store.reactiveState.pinnedEmojis; await popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), { src: this.src, pinnedEmojis: emojisRef, diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts index c8ab9238d3..6ac0684170 100644 --- a/packages/frontend/src/scripts/get-drive-file-menu.ts +++ b/packages/frontend/src/scripts/get-drive-file-menu.ts @@ -5,12 +5,12 @@ import * as Misskey from 'misskey-js'; import { defineAsyncComponent } from 'vue'; +import type { MenuItem } from '@/types/menu.js'; import { i18n } from '@/i18n.js'; import { copyToClipboard } from '@/scripts/copy-to-clipboard.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import type { MenuItem } from '@/types/menu.js'; -import { defaultStore } from '@/store.js'; +import { prefer } from '@/preferences.js'; function rename(file: Misskey.entities.DriveFile) { os.inputText({ @@ -148,7 +148,7 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss action: () => deleteFile(file), }); - if (defaultStore.state.devMode) { + if (prefer.s.devMode) { menuItems.push({ type: 'divider' }, { icon: 'ti ti-id', text: i18n.ts.copyFileId, diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 84122c0e60..8ce4a81bd4 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -15,7 +15,7 @@ import { instance } from '@/instance.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { copyToClipboard } from '@/scripts/copy-to-clipboard.js'; -import { defaultStore, noteActions } from '@/store.js'; +import { store, noteActions } from '@/store.js'; import { miLocalStorage } from '@/local-storage.js'; import { getUserMenu } from '@/scripts/get-user-menu.js'; import { clipsCache, favoritedChannelsCache } from '@/cache.js'; @@ -23,6 +23,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { isSupportShare } from '@/scripts/navigator.js'; import { getAppearNote } from '@/scripts/get-appear-note.js'; import { genEmbedCode } from '@/scripts/get-embed-code.js'; +import { prefer } from '@/preferences.js'; export async function getNoteClipMenu(props: { note: Misskey.entities.Note; @@ -507,7 +508,7 @@ export function getNoteMenu(props: { }))); } - if (defaultStore.state.devMode) { + if (prefer.s.devMode) { menuItems.push({ type: 'divider' }, { icon: 'ti ti-id', text: i18n.ts.copyNoteId, @@ -558,7 +559,7 @@ export function getRenoteMenu(props: { icon: 'ti ti-repeat', action: () => { const el = props.renoteButton.value; - if (el && defaultStore.state.animation) { + if (el && prefer.s.animation) { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); @@ -596,7 +597,7 @@ export function getRenoteMenu(props: { icon: 'ti ti-repeat', action: () => { const el = props.renoteButton.value; - if (el && defaultStore.state.animation) { + if (el && prefer.s.animation) { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); @@ -605,8 +606,8 @@ export function getRenoteMenu(props: { }); } - const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility; - const localOnly = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly; + const configuredVisibility = prefer.s.rememberNoteVisibility ? store.state.visibility : prefer.s.defaultNoteVisibility; + const localOnly = prefer.s.rememberNoteVisibility ? store.state.localOnly : prefer.s.defaultNoteLocalOnly; let visibility = appearNote.visibility; visibility = smallerVisibility(visibility, configuredVisibility); @@ -647,7 +648,7 @@ export function getRenoteMenu(props: { text: channel.name, action: () => { const el = props.renoteButton.value; - if (el && defaultStore.state.animation) { + if (el && prefer.s.animation) { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 8f7c3ba3be..6892c3a4e4 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -6,19 +6,20 @@ import { toUnicode } from 'punycode.js'; import { defineAsyncComponent, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; +import { host, url } from '@@/js/config.js'; +import type { IRouter } from '@/nirax.js'; +import type { MenuItem } from '@/types/menu.js'; import { i18n } from '@/i18n.js'; import { copyToClipboard } from '@/scripts/copy-to-clipboard.js'; -import { host, url } from '@@/js/config.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { defaultStore, userActions } from '@/store.js'; +import { userActions } from '@/store.js'; import { $i, iAmModerator } from '@/account.js'; import { notesSearchAvailable, canSearchNonLocalNotes } from '@/scripts/check-permissions.js'; -import type { IRouter } from '@/nirax.js'; import { antennasCache, rolesCache, userListsCache } from '@/cache.js'; import { mainRouter } from '@/router/main.js'; import { genEmbedCode } from '@/scripts/get-embed-code.js'; -import type { MenuItem } from '@/types/menu.js'; +import { prefer } from '@/preferences.js'; export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter = mainRouter) { const meId = $i ? $i.id : null; @@ -251,7 +252,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter listId: list.id, userId: user.id, }).then(() => { - list.userIds?.splice(list.userIds?.indexOf(user.id), 1); + list.userIds?.splice(list.userIds.indexOf(user.id), 1); }); } })); @@ -398,7 +399,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter }); } - if (defaultStore.state.devMode) { + if (prefer.s.devMode) { menuItems.push({ type: 'divider' }, { icon: 'ti ti-id', text: i18n.ts.copyUserId, diff --git a/packages/frontend/src/scripts/init-chart.ts b/packages/frontend/src/scripts/init-chart.ts index 41e1636aa7..037b0d9567 100644 --- a/packages/frontend/src/scripts/init-chart.ts +++ b/packages/frontend/src/scripts/init-chart.ts @@ -24,7 +24,7 @@ import { import gradient from 'chartjs-plugin-gradient'; import zoomPlugin from 'chartjs-plugin-zoom'; import { MatrixController, MatrixElement } from 'chartjs-chart-matrix'; -import { defaultStore } from '@/store.js'; +import { store } from '@/store.js'; import 'chartjs-adapter-date-fns'; export function initChart() { @@ -52,7 +52,7 @@ export function initChart() { // フォントカラー Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-fg'); - Chart.defaults.borderColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; + Chart.defaults.borderColor = store.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; Chart.defaults.animation = false; } diff --git a/packages/frontend/src/scripts/install-plugin.ts b/packages/frontend/src/scripts/install-plugin.ts deleted file mode 100644 index 37f473b6de..0000000000 --- a/packages/frontend/src/scripts/install-plugin.ts +++ /dev/null @@ -1,131 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { defineAsyncComponent } from 'vue'; -import { compareVersions } from 'compare-versions'; -import { v4 as uuid } from 'uuid'; -import { Interpreter, Parser, utils } from '@syuilo/aiscript'; -import type { Plugin } from '@/store.js'; -import { ColdDeviceStorage } from '@/store.js'; -import * as os from '@/os.js'; -import { misskeyApi } from '@/scripts/misskey-api.js'; -import { i18n } from '@/i18n.js'; - -export type AiScriptPluginMeta = { - name: string; - version: string; - author: string; - description?: string; - permissions?: string[]; - config?: Record<string, any>; -}; - -const parser = new Parser(); - -export function savePlugin({ id, meta, src, token }: { - id: string; - meta: AiScriptPluginMeta; - src: string; - token: string; -}) { - ColdDeviceStorage.set('plugins', ColdDeviceStorage.get('plugins').concat({ - ...meta, - id, - active: true, - configData: {}, - token: token, - src: src, - } as Plugin)); -} - -export function isSupportedAiScriptVersion(version: string): boolean { - try { - return (compareVersions(version, '0.12.0') >= 0); - } catch (err) { - return false; - } -} - -export async function parsePluginMeta(code: string): Promise<AiScriptPluginMeta> { - if (!code) { - throw new Error('code is required'); - } - - const lv = utils.getLangVersion(code); - if (lv == null) { - throw new Error('No language version annotation found'); - } else if (!isSupportedAiScriptVersion(lv)) { - throw new Error(`Aiscript version '${lv}' is not supported`); - } - - let ast; - try { - ast = parser.parse(code); - } catch (err) { - throw new Error('Aiscript syntax error'); - } - - const meta = Interpreter.collectMetadata(ast); - if (meta == null) { - throw new Error('Meta block not found'); - } - - const metadata = meta.get(null); - if (metadata == null) { - throw new Error('Metadata not found'); - } - - const { name, version, author, description, permissions, config } = metadata; - if (name == null || version == null || author == null) { - throw new Error('Required property not found'); - } - - return { - name, - version, - author, - description, - permissions, - config, - }; -} - -export async function installPlugin(code: string, meta?: AiScriptPluginMeta) { - if (!code) return; - - let realMeta: AiScriptPluginMeta; - if (!meta) { - realMeta = await parsePluginMeta(code); - } else { - realMeta = meta; - } - - const token = realMeta.permissions == null || realMeta.permissions.length === 0 ? null : await new Promise((res, rej) => { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), { - title: i18n.ts.tokenRequested, - information: i18n.ts.pluginTokenRequestedDescription, - initialName: realMeta.name, - initialPermissions: realMeta.permissions, - }, { - done: async result => { - const { name, permissions } = result; - const { token } = await misskeyApi('miauth/gen-token', { - session: null, - name: name, - permission: permissions, - }); - res(token); - }, - closed: () => dispose(), - }); - }); - - savePlugin({ - id: uuid(), - meta: realMeta, - token, - src: code, - }); -} diff --git a/packages/frontend/src/scripts/install-theme.ts b/packages/frontend/src/scripts/install-theme.ts deleted file mode 100644 index cc32adcc6a..0000000000 --- a/packages/frontend/src/scripts/install-theme.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import JSON5 from 'json5'; -import { addTheme, getThemes } from '@/theme-store.js'; -import { applyTheme, validateTheme } from '@/scripts/theme.js'; -import type { Theme } from '@/scripts/theme.js'; - -export function parseThemeCode(code: string): Theme { - let theme; - - try { - theme = JSON5.parse(code); - } catch (err) { - throw new Error('Failed to parse theme json'); - } - if (!validateTheme(theme)) { - throw new Error('This theme is invaild'); - } - if (getThemes().some(t => t.id === theme.id)) { - throw new Error('This theme is already installed'); - } - - return theme; -} - -export function previewTheme(code: string): void { - const theme = parseThemeCode(code); - if (theme) applyTheme(theme, false); -} - -export async function installTheme(code: string): Promise<void> { - const theme = parseThemeCode(code); - if (!theme) return; - await addTheme(theme); -} diff --git a/packages/frontend/src/scripts/reaction-picker.ts b/packages/frontend/src/scripts/reaction-picker.ts index c142b3ed2a..81f6c02dcf 100644 --- a/packages/frontend/src/scripts/reaction-picker.ts +++ b/packages/frontend/src/scripts/reaction-picker.ts @@ -7,7 +7,7 @@ import * as Misskey from 'misskey-js'; import { defineAsyncComponent, ref } from 'vue'; import type { Ref } from 'vue'; import { popup } from '@/os.js'; -import { defaultStore } from '@/store.js'; +import { store } from '@/store.js'; class ReactionPicker { private src: Ref<HTMLElement | null> = ref(null); @@ -21,7 +21,7 @@ class ReactionPicker { } public async init() { - const reactionsRef = defaultStore.reactiveState.reactions; + const reactionsRef = store.reactiveState.reactions; await popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), { src: this.src, pinnedEmojis: reactionsRef, diff --git a/packages/frontend/src/scripts/select-file.ts b/packages/frontend/src/scripts/select-file.ts index c25b4d73bd..42b34f54f5 100644 --- a/packages/frontend/src/scripts/select-file.ts +++ b/packages/frontend/src/scripts/select-file.ts @@ -9,8 +9,8 @@ import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; -import { defaultStore } from '@/store.js'; import { uploadFile } from '@/scripts/upload.js'; +import { prefer } from '@/preferences.js'; export function chooseFileFromPc( multiple: boolean, @@ -20,8 +20,8 @@ export function chooseFileFromPc( nameConverter?: (file: File) => string | undefined; }, ): Promise<Misskey.entities.DriveFile[]> { - const uploadFolder = options?.uploadFolder ?? defaultStore.state.uploadFolder; - const keepOriginal = options?.keepOriginal ?? defaultStore.state.keepOriginalUploading; + const uploadFolder = options?.uploadFolder ?? prefer.s.uploadFolder; + const keepOriginal = options?.keepOriginal ?? prefer.s.keepOriginalUploading; const nameConverter = options?.nameConverter ?? (() => undefined); return new Promise((res, rej) => { @@ -82,7 +82,7 @@ export function chooseFileFromUrl(): Promise<Misskey.entities.DriveFile> { misskeyApi('drive/files/upload-from-url', { url: url, - folderId: defaultStore.state.uploadFolder, + folderId: prefer.s.uploadFolder, marker, }); @@ -96,7 +96,7 @@ export function chooseFileFromUrl(): Promise<Misskey.entities.DriveFile> { function select(src: HTMLElement | EventTarget | null, label: string | null, multiple: boolean): Promise<Misskey.entities.DriveFile[]> { return new Promise((res, rej) => { - const keepOriginal = ref(defaultStore.state.keepOriginalUploading); + const keepOriginal = ref(prefer.s.keepOriginalUploading); os.popupMenu([label ? { text: label, diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts index 2008afe045..436c2b75f0 100644 --- a/packages/frontend/src/scripts/sound.ts +++ b/packages/frontend/src/scripts/sound.ts @@ -3,8 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import type { SoundStore } from '@/store.js'; -import { defaultStore } from '@/store.js'; +import type { SoundStore } from '@/preferences/def.js'; +import { prefer } from '@/preferences.js'; +import { PREF_DEF } from '@/preferences/def.js'; let ctx: AudioContext; const cache = new Map<string, AudioBuffer>(); @@ -127,11 +128,11 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; }) * @param type スプライトの種類を指定 */ export function playMisskeySfx(operationType: OperationType) { - const sound = defaultStore.state[`sound_${operationType}`]; + const sound = prefer.s[`sound.on.${operationType}`]; playMisskeySfxFile(sound).then((succeed) => { if (!succeed && sound.type === '_driveFile_') { // ドライブファイルが存在しない場合はデフォルトのサウンドを再生する - const soundName = defaultStore.def[`sound_${operationType}`].default.type as Exclude<SoundType, '_driveFile_'>; + const soundName = PREF_DEF[`sound_${operationType}`].default.type as Exclude<SoundType, '_driveFile_'>; if (_DEV_) console.log(`Failed to play sound: ${sound.fileUrl}, so play default sound: ${soundName}`); playMisskeySfxFileInternal({ type: soundName, @@ -166,7 +167,7 @@ async function playMisskeySfxFileInternal(soundStore: SoundStore): Promise<boole if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) { return false; } - const masterVolume = defaultStore.state.sound_masterVolume; + const masterVolume = prefer.s['sound.masterVolume']; if (isMute() || masterVolume === 0 || soundStore.volume === 0) { return true; // ミュート時は成功として扱う } @@ -198,10 +199,10 @@ export function createSourceNode(buffer: AudioBuffer, opts: { pan?: number; playbackRate?: number; }): { - soundSource: AudioBufferSourceNode; - panNode: StereoPannerNode; - gainNode: GainNode; -} { + soundSource: AudioBufferSourceNode; + panNode: StereoPannerNode; + gainNode: GainNode; + } { const panNode = ctx.createStereoPanner(); panNode.pan.value = opts.pan ?? 0; @@ -242,13 +243,13 @@ export async function getSoundDuration(file: string): Promise<number> { * ミュートすべきかどうかを判断する */ export function isMute(): boolean { - if (defaultStore.state.sound_notUseSound) { + if (prefer.s['sound.notUseSound']) { // サウンドを出力しない return true; } // noinspection RedundantIfStatementJS - if (defaultStore.state.sound_useSoundOnlyWhenActive && document.visibilityState === 'hidden') { + if (prefer.s['sound.useSoundOnlyWhenActive'] && document.visibilityState === 'hidden') { // ブラウザがアクティブな時のみサウンドを出力する return true; } diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts index 1a3909c132..851ba41e61 100644 --- a/packages/frontend/src/scripts/theme.ts +++ b/packages/frontend/src/scripts/theme.ts @@ -7,10 +7,12 @@ import { ref } from 'vue'; import tinycolor from 'tinycolor2'; import lightTheme from '@@/themes/_light.json5'; import darkTheme from '@@/themes/_dark.json5'; +import JSON5 from 'json5'; import { deepClone } from './clone.js'; import type { BundledTheme } from 'shiki/themes'; import { globalEvents } from '@/events.js'; import { miLocalStorage } from '@/local-storage.js'; +import { addTheme, getThemes } from '@/theme-store.js'; export type Theme = { id: string; @@ -101,6 +103,7 @@ export function applyTheme(theme: Theme, persist = true) { if (persist) { miLocalStorage.setItem('theme', JSON.stringify(props)); + miLocalStorage.setItem('themeId', theme.id); miLocalStorage.setItem('colorScheme', colorScheme); } @@ -155,3 +158,32 @@ export function validateTheme(theme: Record<string, any>): boolean { if (theme.props == null || typeof theme.props !== 'object') return false; return true; } + +export function parseThemeCode(code: string): Theme { + let theme; + + try { + theme = JSON5.parse(code); + } catch (err) { + throw new Error('Failed to parse theme json'); + } + if (!validateTheme(theme)) { + throw new Error('This theme is invaild'); + } + if (getThemes().some(t => t.id === theme.id)) { + throw new Error('This theme is already installed'); + } + + return theme; +} + +export function previewTheme(code: string): void { + const theme = parseThemeCode(code); + if (theme) applyTheme(theme, false); +} + +export async function installTheme(code: string): Promise<void> { + const theme = parseThemeCode(code); + if (!theme) return; + await addTheme(theme); +} diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts index 713573a377..d105a318a7 100644 --- a/packages/frontend/src/scripts/upload.ts +++ b/packages/frontend/src/scripts/upload.ts @@ -7,13 +7,13 @@ import { reactive, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { v4 as uuid } from 'uuid'; import { readAndCompressImage } from '@misskey-dev/browser-image-resizer'; -import { getCompressionConfig } from './upload/compress-config.js'; -import { defaultStore } from '@/store.js'; import { apiUrl } from '@@/js/config.js'; +import { getCompressionConfig } from './upload/compress-config.js'; import { $i } from '@/account.js'; import { alert } from '@/os.js'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; +import { prefer } from '@/preferences.js'; type Uploading = { id: string; @@ -34,7 +34,7 @@ export function uploadFile( file: File, folder?: string | Misskey.entities.DriveFolder, name?: string, - keepOriginal: boolean = defaultStore.state.keepOriginalUploading, + keepOriginal: boolean = prefer.s.keepOriginalUploading, ): Promise<Misskey.entities.DriveFile> { if ($i == null) throw new Error('Not logged in'); @@ -59,7 +59,7 @@ export function uploadFile( const ctx = reactive<Uploading>({ id, - name: defaultStore.state.keepOriginalFilename ? filename : id + extension, + name: prefer.s.keepOriginalFilename ? filename : id + extension, progressMax: undefined, progressValue: undefined, img: window.URL.createObjectURL(file), |