mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-18 06:07:36 +08:00
✨ Feature(custom): allow user to edit theme file
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
<script setup>
|
||||
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'
|
||||
import { css } from '@codemirror/lang-css'
|
||||
import { javascript } from '@codemirror/lang-javascript'
|
||||
import { json } from '@codemirror/lang-json'
|
||||
import { openSearchPanel, search, searchKeymap } from '@codemirror/search'
|
||||
@@ -11,6 +12,7 @@ import { EditorState } from '@codemirror/state'
|
||||
import { oneDark } from '@codemirror/theme-one-dark'
|
||||
import { EditorView, keymap, lineNumbers } from '@codemirror/view'
|
||||
import { onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: String, default: '' },
|
||||
language: { type: String, default: 'javascript' },
|
||||
@@ -21,7 +23,7 @@ const editorRef = ref(null)
|
||||
const view = shallowRef(null)
|
||||
|
||||
onMounted(() => {
|
||||
const languageExtension = props.language === 'json' ? json() : javascript()
|
||||
const languageExtension = props.language === 'json' ? json() : props.language === 'css' ? css() : javascript()
|
||||
const startState = EditorState.create({
|
||||
doc: props.modelValue,
|
||||
extensions: [
|
||||
|
||||
@@ -906,6 +906,7 @@
|
||||
"downloadThemes": "Download Themes",
|
||||
"downloadThemesFailed": "Failed to download themes",
|
||||
"downloadThemesSuccess": "Themes downloaded successfully",
|
||||
"editTheme": "Edit Theme",
|
||||
"enableAdvancedAnimation": "Enable Advanced Animation",
|
||||
"enableAdvancedAnimationDesc": "Do not enable this option on low-performance devices or when GPU acceleration is disabled",
|
||||
"hideDockHint": "Cannot hide both dock and tray at the same time",
|
||||
@@ -1207,15 +1208,6 @@
|
||||
"title": "Configurations"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"createScript": "Create Script",
|
||||
"deleteScript": "Delete Script",
|
||||
"duplicateScriptNameError": "Script name already exists",
|
||||
"editScripts": "Edit Script",
|
||||
"newScriptTitle": "New Script",
|
||||
"noScriptsFound": "No Scripts Found",
|
||||
"pleaseEnterScriptName": "Please enter script name"
|
||||
},
|
||||
"settings": {
|
||||
"theme": {
|
||||
"auto": "Auto",
|
||||
|
||||
@@ -906,8 +906,10 @@
|
||||
"downloadThemes": "下载主题",
|
||||
"downloadThemesFailed": "下载主题失败",
|
||||
"downloadThemesSuccess": "主题下载成功",
|
||||
"editTheme": "编辑主题",
|
||||
"enableAdvancedAnimation": "启用高级动画效果",
|
||||
"enableAdvancedAnimationDesc": "不要在低性能设备上或关闭GPU加速时启用此选项",
|
||||
"getThemeContentFailed": "获取主题内容失败",
|
||||
"hideDockHint": "不支持同时隐藏 dock 和托盘",
|
||||
"importThemes": "导入主题",
|
||||
"importThemesFailed": "导入主题失败",
|
||||
|
||||
@@ -906,8 +906,10 @@
|
||||
"downloadThemes": "下載主題",
|
||||
"downloadThemesFailed": "下載主題失敗",
|
||||
"downloadThemesSuccess": "主題下載成功",
|
||||
"editTheme": "編輯主題",
|
||||
"enableAdvancedAnimation": "啟用高級動畫效果",
|
||||
"enableAdvancedAnimationDesc": "不要在低性能設備上或關閉GPU加速時啟用此選項",
|
||||
"getThemeContentFailed": "獲取主題內容失敗",
|
||||
"hideDockHint": "不支持同時隱藏 dock 和托盤",
|
||||
"importThemes": "導入主題",
|
||||
"importThemesFailed": "導入主題失敗",
|
||||
@@ -1207,15 +1209,6 @@
|
||||
"title": "配置"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"createScript": "創建腳本",
|
||||
"deleteScript": "刪除腳本",
|
||||
"duplicateScriptNameError": "腳本名稱已存在",
|
||||
"editScripts": "編輯腳本",
|
||||
"newScriptTitle": "新建腳本",
|
||||
"noScriptsFound": "未找到腳本",
|
||||
"pleaseEnterScriptName": "請輸入腳本名稱"
|
||||
},
|
||||
"settings": {
|
||||
"theme": {
|
||||
"auto": "自動",
|
||||
|
||||
@@ -86,36 +86,48 @@
|
||||
</SettingCard>
|
||||
|
||||
<SettingCard>
|
||||
<CustomSelect
|
||||
<SingleSelect
|
||||
v-model="currentTheme"
|
||||
:select-list="themeList"
|
||||
:title="t('pages.settings.system.chooseTheme')"
|
||||
:icon="ImageIcon"
|
||||
/>
|
||||
:fronticon="false"
|
||||
:key-list="themeList.map(item => item.value)"
|
||||
:placeholder="themeList.find(theme => theme.value === currentTheme)?.label || ''"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
{{ themeList.find(theme => theme.value === item)?.label || item }}
|
||||
</template>
|
||||
</SingleSelect>
|
||||
<template #extra>
|
||||
<div class="mt-3 flex gap-4">
|
||||
<CustomButton
|
||||
:disabled="downloadingThemes"
|
||||
:text="
|
||||
downloadingThemes
|
||||
? t('pages.settings.system.downloadingThemes')
|
||||
: t('pages.settings.system.downloadThemes')
|
||||
"
|
||||
:icon-size="14"
|
||||
:icon="Download"
|
||||
type="secondary"
|
||||
@click="handleDownloadThemes"
|
||||
/>
|
||||
<CustomButton
|
||||
:icon="Import"
|
||||
:text="t('pages.settings.system.importThemes')"
|
||||
type="secondary"
|
||||
:icon-size="14"
|
||||
@click="handleImportThemes"
|
||||
/>
|
||||
<CustomButton
|
||||
:icon="Edit2"
|
||||
:text="t('pages.settings.system.editTheme')"
|
||||
type="primary"
|
||||
:icon-size="14"
|
||||
@click="handleEditTheme"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</SettingCard>
|
||||
<template #extra>
|
||||
<div class="mt-3 flex gap-4">
|
||||
<CustomButton
|
||||
:disabled="downloadingThemes"
|
||||
:text="
|
||||
downloadingThemes
|
||||
? t('pages.settings.system.downloadingThemes')
|
||||
: t('pages.settings.system.downloadThemes')
|
||||
"
|
||||
:icon-size="14"
|
||||
:icon="Download"
|
||||
type="secondary"
|
||||
@click="handleDownloadThemes"
|
||||
/>
|
||||
<CustomButton
|
||||
:icon="Import"
|
||||
:text="t('pages.settings.system.importThemes')"
|
||||
type="secondary"
|
||||
:icon-size="14"
|
||||
@click="handleImportThemes"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</SettingSection>
|
||||
|
||||
<!-- Window Behavior Section -->
|
||||
@@ -1219,7 +1231,7 @@
|
||||
</CustomModal>
|
||||
|
||||
<CustomModal v-if="editorVisible" v-model:visible="editorVisible" :title="t('common.edit')">
|
||||
<Editor v-model="editorContent" language="json" />
|
||||
<Editor v-model="editorContent" :language="editorLanguage" />
|
||||
<template #footer>
|
||||
<CustomButton type="secondary" :text="t('common.cancel')" @click="editorVisible = false" />
|
||||
<CustomButton type="primary" :text="t('common.save')" @click="saveEditorContent" />
|
||||
@@ -1236,6 +1248,7 @@ import {
|
||||
CloudUpload,
|
||||
Download,
|
||||
Edit,
|
||||
Edit2,
|
||||
FileText,
|
||||
FolderOpen,
|
||||
GitBranch,
|
||||
@@ -1269,6 +1282,7 @@ import MultiSelect from '@/components/common/MultiSelect.vue'
|
||||
import placeholderTable from '@/components/common/PlaceholderTable.vue'
|
||||
import SettingCard from '@/components/common/SettingCard.vue'
|
||||
import SettingSection from '@/components/common/SettingSection.vue'
|
||||
import SingleSelect from '@/components/common/SingleSelect.vue'
|
||||
import Editor from '@/components/Editor.vue'
|
||||
import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
|
||||
import useConfirm from '@/hooks/useConfirm'
|
||||
@@ -1311,6 +1325,8 @@ const upDownConfigVisible = ref(false)
|
||||
const proxyVisible = ref(false)
|
||||
const editorVisible = ref(false)
|
||||
const editorContent = ref('// 在这里开始编写代码...\nfunction hello() {\n console.log("Hello Electron!");\n}')
|
||||
const editorLanguage = ref('json')
|
||||
const currentEditFile = ref('')
|
||||
|
||||
const latestVersion = ref('')
|
||||
const releaseNotes = ref('')
|
||||
@@ -1430,6 +1446,21 @@ const logLevel = [
|
||||
const syncType = ['github', 'gitee', 'gitea', 'webdav']
|
||||
const version = pkg.version
|
||||
|
||||
const buildInThemesList = [
|
||||
'adwaita.css',
|
||||
'anime.css',
|
||||
'bilibili.css',
|
||||
'Catppucin.css',
|
||||
'CoolApk.css',
|
||||
'Cupertino.css',
|
||||
'default.css',
|
||||
'goldensand.css',
|
||||
'Huorong.css',
|
||||
'purple.css',
|
||||
'wechat.css',
|
||||
'win11.css',
|
||||
]
|
||||
|
||||
const RELEASE_NOTES_CACHE_DURATION = 30 * 60 * 1000
|
||||
|
||||
const shortUrlServerList = [
|
||||
@@ -1747,6 +1778,19 @@ async function handleImportThemes() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleEditTheme() {
|
||||
try {
|
||||
const themeContent = await window.electron.triggerRPC<string>(IRPCActionType.THEME_READ_THEME, currentTheme.value)
|
||||
editorContent.value = themeContent || ''
|
||||
currentEditFile.value = currentTheme.value
|
||||
editorLanguage.value = 'css'
|
||||
editorVisible.value = true
|
||||
} catch (error) {
|
||||
console.error('Failed to open theme folder:', error)
|
||||
message.error(t('pages.settings.system.getThemeContentFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
async function handleThemeChange(theme: string) {
|
||||
try {
|
||||
await window.electron.triggerRPC(IRPCActionType.THEME_APPLY_THEME, theme)
|
||||
@@ -1842,41 +1886,58 @@ async function handleChangeSecondPicBed() {
|
||||
window.electron.sendRPC(IRPCActionType.SHOW_SECOND_UPLOADER_MENU)
|
||||
}
|
||||
|
||||
async function saveEditorContent() {
|
||||
const content = editorContent.value.trim()
|
||||
await saveFile('data.json', content, 'json')
|
||||
editorVisible.value = false
|
||||
}
|
||||
|
||||
async function openFile(file: string) {
|
||||
window.electron.sendRPC(IRPCActionType.PICLIST_OPEN_FILE, file)
|
||||
}
|
||||
|
||||
async function editFile(file: string, mode: 'text' | 'json' = 'text') {
|
||||
async function editFile(file: string) {
|
||||
const content = (await window.electron.triggerRPC<string>(IRPCActionType.READ_FILE_CONTENT, file)) || ''
|
||||
if (mode === 'json') {
|
||||
try {
|
||||
editorContent.value = JSON.stringify(JSON.parse(content), null, 2)
|
||||
} catch (error) {
|
||||
editorContent.value = content
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
editorContent.value = JSON.stringify(JSON.parse(content), null, 2)
|
||||
} catch (error) {
|
||||
editorContent.value = content
|
||||
}
|
||||
currentEditFile.value = file
|
||||
editorLanguage.value = 'json'
|
||||
editorVisible.value = true
|
||||
}
|
||||
|
||||
async function saveFile(file: string, content: string, mode: 'text' | 'json' = 'text') {
|
||||
let dataToSave = content
|
||||
if (mode === 'json') {
|
||||
async function saveEditorContent() {
|
||||
if (currentEditFile.value === 'data.json' || currentEditFile.value === 'manage.json') {
|
||||
const content = editorContent.value.trim()
|
||||
await saveFile(currentEditFile.value, content)
|
||||
} else if (currentEditFile.value.endsWith('.css')) {
|
||||
try {
|
||||
dataToSave = JSON.stringify(JSON.parse(content), null, 2)
|
||||
let themeFileName
|
||||
if (buildInThemesList.includes(currentTheme.value)) {
|
||||
themeFileName = `custom-${currentTheme.value}`
|
||||
} else {
|
||||
themeFileName = currentTheme.value
|
||||
}
|
||||
window.electron.sendRPC(IRPCActionType.THEME_WRITE_THEME, themeFileName, editorContent.value)
|
||||
message.success(t('pages.settings.advanced.saveFileSuccess'))
|
||||
setTimeout(async () => {
|
||||
await loadThemes()
|
||||
await window.electron.triggerRPC(IRPCActionType.THEME_APPLY_THEME, themeFileName)
|
||||
}, 1000)
|
||||
} catch (error) {
|
||||
console.error('Invalid JSON content:', error)
|
||||
message.error(t('pages.settings.advanced.invalidJson'))
|
||||
return
|
||||
console.error('Failed to save theme:', error)
|
||||
message.error(t('pages.settings.advanced.saveFileFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
editorVisible.value = false
|
||||
}
|
||||
|
||||
async function saveFile(file: string, content: string) {
|
||||
let dataToSave = content
|
||||
try {
|
||||
dataToSave = JSON.stringify(JSON.parse(content), null, 2)
|
||||
} catch (error) {
|
||||
console.error('Invalid JSON content:', error)
|
||||
message.error(t('pages.settings.advanced.invalidJson'))
|
||||
return
|
||||
}
|
||||
try {
|
||||
window.electron.sendRPC(IRPCActionType.WRITE_FILE_CONTENT, file, dataToSave)
|
||||
message.success(t('pages.settings.advanced.saveFileSuccess'))
|
||||
|
||||
@@ -163,6 +163,11 @@
|
||||
:title="t('pages.scripts.selectScriptType')"
|
||||
:key-list="supportedScriptCategories.map(cat => cat.type)"
|
||||
:fronticon="false"
|
||||
:placeholder="
|
||||
supportedScriptCategories.find(cat => cat.type === newScriptCategory)
|
||||
? supportedScriptCategories.find(cat => cat.type === newScriptCategory)?.name
|
||||
: newScriptCategory
|
||||
"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
{{
|
||||
|
||||
@@ -23,6 +23,8 @@ export const IRPCActionType = {
|
||||
OPEN_FILE: 'OPEN_FILE',
|
||||
HIDE_DOCK: 'HIDE_DOCK',
|
||||
SET_CURRENT_LANGUAGE: 'SET_CURRENT_LANGUAGE',
|
||||
THEME_READ_THEME: 'THEME_READ_THEME',
|
||||
THEME_WRITE_THEME: 'THEME_WRITE_THEME',
|
||||
THEME_RESOLVE_THEMES: 'THEME_RESOLVE_THEMES',
|
||||
THEME_FETCH_THEMES: 'THEME_FETCH_THEMES',
|
||||
THEME_IMPORT_THEMES: 'THEME_IMPORT_THEMES',
|
||||
|
||||
Reference in New Issue
Block a user