Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"permissions": {
"allow": [
"Bash(npm run build:mac:*)",
"Bash(npm install:*)",
"Bash(npm run rebuild-native:*)",
"Bash(CXXFLAGS=\"-std=c++17\" npm run rebuild-native:*)",
"Bash(npm run build:*)",
"Bash(npx electron-builder:*)",
"Read(//Users/michaeljosephwork/git/marktext_messed_up/**)",
"Bash(gh pr list:*)",
"Bash(gh pr view:*)",
"Bash(git remote add:*)",
"Bash(git fetch:*)",
"Bash(git cherry-pick:*)",
"Bash(git checkout:*)",
"Bash(git reset:*)",
"Bash(git stash:*)",
"Bash(git log:*)",
"Bash(git merge:*)",
"Bash(git push:*)",
"WebFetch(domain:github.com)",
"Bash(gh pr create:*)",
"Bash(cat:*)",
"Bash(npx patch-package)",
"Bash(ls:*)",
"Bash(awk:*)",
"Bash(npx patch-package:*)",
"Bash(git add:*)",
"Bash(git commit:*)"
],
"deny": [],
"ask": []
}
}
5 changes: 5 additions & 0 deletions src/main/preferences/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -417,5 +417,10 @@
"description": "Watcher--Whether to use polling. Polling may leads to high CPU utilization but is necessary to watch files over a network.",
"type": "boolean",
"default": false
},
"autoReloadUnmodifiedFiles": {
"description": "Experimental--Automatically reload unmodified documents when changed on disk.",
"type": "boolean",
"default": true
}
}
1 change: 1 addition & 0 deletions src/renderer/src/assets/icons/pref_experimental.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 76 additions & 1 deletion src/renderer/src/components/editorWithTabs/notifications.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
>
<div class="msg">
{{ currentNotification.msg }}
<span v-if="currentNotification.showCountdown && countdown > 0" class="countdown">
({{ countdown }}s)
</span>
</div>
<div class="controls">
<div>
Expand All @@ -28,7 +31,7 @@
</template>

<script setup>
import { computed } from 'vue'
import { computed, watch, onUnmounted, ref } from 'vue'
import { useEditorStore } from '@/store/editor'
import { useLayoutStore } from '@/store/layout'
import { storeToRefs } from 'pinia'
Expand All @@ -40,6 +43,10 @@ const layoutStore = useLayoutStore()
const { currentFile } = storeToRefs(editorStore)
const { showSideBar, sideBarWidth } = storeToRefs(layoutStore)

let autoHideTimer = null
let countdownInterval = null
const countdown = ref(0)

const currentNotification = computed(() => {
const notifications = currentFile.value.notifications
if (!notifications || notifications.length === 0) {
Expand All @@ -55,12 +62,71 @@ const handleClick = (status) => {
return
}

if (autoHideTimer) {
clearTimeout(autoHideTimer)
autoHideTimer = null
}

if (countdownInterval) {
clearInterval(countdownInterval)
countdownInterval = null
}

countdown.value = 0

const item = notifications.shift()
const action = item.action
if (action) {
action(status)
}
}

// Watch for notifications with autoHide enabled
watch(currentNotification, (newNotification, oldNotification) => {
if (autoHideTimer) {
clearTimeout(autoHideTimer)
autoHideTimer = null
}

if (countdownInterval) {
clearInterval(countdownInterval)
countdownInterval = null
}

countdown.value = 0

if (newNotification && newNotification.autoHide) {
const duration = newNotification.autoHideDuration || 3000

// Set up countdown if requested
if (newNotification.showCountdown) {
countdown.value = Math.ceil(duration / 1000)
countdownInterval = setInterval(() => {
countdown.value--
if (countdown.value <= 0) {
clearInterval(countdownInterval)
countdownInterval = null
}
}, 1000)
}

autoHideTimer = setTimeout(() => {
handleClick(false)
autoHideTimer = null
}, duration)
}
}, { deep: true })

onUnmounted(() => {
if (autoHideTimer) {
clearTimeout(autoHideTimer)
autoHideTimer = null
}
if (countdownInterval) {
clearInterval(countdownInterval)
countdownInterval = null
}
})
</script>

<style scoped>
Expand All @@ -83,10 +149,19 @@ const handleClick = (status) => {
background: var(--notificationErrorBg);
color: var(--notificationErrorColor);
}
&.success {
background: #4caf50;
color: #ffffff;
}
}
.msg {
font-size: 13px;
flex: 1;
& .countdown {
margin-left: 8px;
opacity: 0.8;
font-weight: 600;
}
}
.controls {
display: flex;
Expand Down
40 changes: 40 additions & 0 deletions src/renderer/src/prefComponents/experimental/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<div class="pref-experimental">
<h4>{{ t('preferences.experimental.title') }}</h4>
<compound>
<template #head>
<h6 class="title">{{ t('preferences.experimental.fileHandling.title') }}</h6>
</template>
<template #children>
<bool
:description="t('preferences.experimental.fileHandling.autoReloadUnmodified')"
:bool="autoReloadUnmodifiedFiles"
:on-change="(value) => onSelectChange('autoReloadUnmodifiedFiles', value)"
></bool>
</template>
</compound>
</div>
</template>

<script setup>
import { storeToRefs } from 'pinia'
import { useI18n } from 'vue-i18n'
import { usePreferencesStore } from '@/store/preferences'
import Compound from '../common/compound/index.vue'
import Bool from '../common/bool/index.vue'

const { t } = useI18n()
const preferenceStore = usePreferencesStore()

const { autoReloadUnmodifiedFiles } = storeToRefs(preferenceStore)

const onSelectChange = (type, value) => {
preferenceStore.SET_SINGLE_PREFERENCE({ type, value })
}
</script>

<style scoped>
.pref-experimental {
/* Styles can be added here if needed */
}
</style>
9 changes: 8 additions & 1 deletion src/renderer/src/prefComponents/sideBar/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ThemeIcon from '@/assets/icons/pref_theme.svg'
import ImageIcon from '@/assets/icons/pref_image.svg'
import SpellIcon from '@/assets/icons/pref_spellcheck.svg'
import KeyBindingIcon from '@/assets/icons/pref_key_binding.svg'
import ExperimentalIcon from '@/assets/icons/pref_experimental.svg'

import preferences from '../../../../main/preferences/schema.json'
import { t } from '../../i18n'
Expand Down Expand Up @@ -51,6 +52,12 @@ export const getCategory = () => [
label: 'keybindings',
icon: KeyBindingIcon,
path: '/preference/keybindings'
},
{
name: t('preferences.categories.experimental'),
label: 'experimental',
icon: ExperimentalIcon,
path: '/preference/experimental'
}
]

Expand Down Expand Up @@ -93,7 +100,7 @@ export const getTranslatedSearchContent = () => {

// 计算用于路由跳转的分类(仅允许已存在的路由,否则回退到 general)
let routeCategory = mappedCategory
const validRoutes = ['general', 'editor', 'markdown', 'spelling', 'theme', 'image', 'keybindings']
const validRoutes = ['general', 'editor', 'markdown', 'spelling', 'theme', 'image', 'keybindings', 'experimental']
if (!validRoutes.includes(routeCategory)) routeCategory = 'general'

// 尝试翻译分类和项目
Expand Down
6 changes: 6 additions & 0 deletions src/renderer/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import SpellChecker from '@/prefComponents/spellchecker'
import Theme from '@/prefComponents/theme'
import Image from '@/prefComponents/image'
import Keybindings from '@/prefComponents/keybindings'
import Experimental from '@/prefComponents/experimental'

const parseSettingsPage = (type) => {
let pageUrl = '/preference'
Expand Down Expand Up @@ -67,6 +68,11 @@ const routes = (type) => [
path: 'keybindings',
component: Keybindings,
name: 'keybindings'
},
{
path: 'experimental',
component: Experimental,
name: 'experimental'
}
]
}
Expand Down
27 changes: 25 additions & 2 deletions src/renderer/src/store/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ export const useEditorStore = defineStore('editor', {
const style = data.style || 'info'
// Whether only one notification should exist.
const exclusiveType = data.exclusiveType || ''
const autoHide = data.autoHide || false
const autoHideDuration = data.autoHideDuration || 3000
const showCountdown = data.showCountdown || false

const tab = this.tabs.find((t) => t.id === tabId)
if (!tab) {
Expand All @@ -95,7 +98,10 @@ export const useEditorStore = defineStore('editor', {
showConfirm,
style,
exclusiveType,
action
action,
autoHide,
autoHideDuration,
showCountdown
})
},

Expand Down Expand Up @@ -1231,7 +1237,24 @@ export const useEditorStore = defineStore('editor', {
}
case 'add':
case 'change': {
const { autoSave } = preferencesStore
const { autoSave, autoReloadUnmodifiedFiles } = preferencesStore

// Check if auto-reload unmodified files is enabled
if (autoReloadUnmodifiedFiles && isSaved) {
this.loadChange(change)
this.pushTabNotification({
tabId: id,
msg: i18n.global.t('store.editor.documentAutoReloaded'),
style: 'success',
showConfirm: false,
exclusiveType: 'file_changed',
autoHide: true,
autoHideDuration: 5000,
showCountdown: true
})
return
}

if (autoSave) {
if (autoSaveTimers.has(id)) {
const timer = autoSaveTimers.get(id)
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/src/store/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ export const usePreferencesStore = defineStore('preferences', {

watcherUsePolling: false,

autoReloadUnmodifiedFiles: true,

// --------------------------------------------------------------------------

// Edit modes of the current window (not part of persistent settings)
Expand Down
13 changes: 11 additions & 2 deletions static/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,8 @@
"spelling": "Spelling",
"theme": "Theme",
"image": "Image",
"keybindings": "Keybindings"
"keybindings": "Keybindings",
"experimental": "Experimental"
},
"general": {
"title": "General",
Expand Down Expand Up @@ -854,6 +855,13 @@
"unbind": "Unbind"
}
},
"experimental": {
"title": "Experimental",
"fileHandling": {
"title": "File Handling",
"autoReloadUnmodified": "Automatically reload unmodified documents when changed on disk"
}
},
"selectFont": "Select Font",
"spellchecker": {
"autoDetectDescription": "Automatically detect document language",
Expand Down Expand Up @@ -1102,7 +1110,8 @@
"exportSuccessTitle": "Exported successfully",
"exportSuccessMessage": "Exported \"{name}\" successfully!",
"fileRemovedOnDisk": "\"{name}\" has been removed on disk.",
"fileChangedOnDisk": "\"{name}\" has been changed on disk. Do you want to reload it?"
"fileChangedOnDisk": "\"{name}\" has been changed on disk. Do you want to reload it?",
"documentAutoReloaded": "Document automatically reloaded from disk"
},
"help": {
"lineEndingAssertionError": "Line ending assertion error"
Expand Down
4 changes: 3 additions & 1 deletion static/preference.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,7 @@
"searchNoIgnore": false,
"searchFollowSymlinks": true,

"watcherUsePolling": false
"watcherUsePolling": false,

"autoReloadUnmodifiedFiles": true
}