2025-04-22 05:59:28 +00:00
import { App , ButtonComponent , DropdownComponent , Notice , Plugin , PluginSettingTab , Setting , TextAreaComponent , TextComponent , ToggleComponent } from 'obsidian' ;
import { AutoGenerateMode , DEFAULT_SETTINGS , DEFAULT_TEMPLATE_EN , DEFAULT_TEMPLATE_ZH , DailyTaskSettings , Language } from '../models/settings' ;
import { getTranslation , setCurrentLanguage } from '../i18n/i18n' ;
import { renderTemplate } from '../utils/templateEngine' ;
import { TaskGenerator } from '../taskGenerator' ;
2025-04-26 01:55:26 +00:00
import { setTextContentByLines , createIconButton , createTextElement } from '../utils/domUtils' ;
2025-04-27 07:00:37 +00:00
// 导入主插件类型定义
import DailyTaskPlugin from '../main' ;
2025-04-22 05:59:28 +00:00
2025-04-23 05:06:41 +00:00
// CSS 相关常量( class名称)
2025-04-22 05:59:28 +00:00
const SettingsSectionCSS = "daily-task-settings-section" ;
const ButtonCSS = "daily-task-button" ;
const PreviewButtonCSS = "daily-task-preview-button" ;
const ResetButtonCSS = "daily-task-reset-button" ;
const EditorCSS = "daily-task-editor" ;
const VerticalStackCSS = "daily-task-vertical-stack" ;
const TextRightCSS = "daily-task-text-right" ;
const TextCenterCSS = "daily-task-text-center" ;
const ScrollbarSlimCSS = "daily-task-slim-scrollbar" ;
2025-04-23 05:06:41 +00:00
const SaveIndicatorCSS = "daily-task-save-indicator" ;
const SuccessIconCSS = "daily-task-success-icon" ;
const SettingTopSpaceCSS = "daily-task-setting-top-space" ;
const InputContainerCSS = "daily-task-input-container" ;
const InputCSS = "daily-task-input" ;
2025-04-22 05:59:28 +00:00
/ * *
* 添 加 插 件 自 定 义 样 式
2025-04-23 05:06:41 +00:00
* 注 意 : 样 式 内 容 现 在 已 移 至 外 部 CSS文件
2025-04-22 05:59:28 +00:00
* /
function addCustomStyles() {
2025-04-23 05:06:41 +00:00
// 样式已移至src/styles.css, 无需在此处添加内联样式
// 插件加载时会自动加载styles.css文件
2025-04-22 05:59:28 +00:00
}
/ * *
* 插 件 设 置 标 签 页
* /
export class DailyTaskSettingTab extends PluginSettingTab {
plugin : Plugin ;
settingsManager : SettingsManager ;
taskGenerator : TaskGenerator ;
previewEl : HTMLElement | null = null ;
addTaskButton : ButtonComponent | null = null ;
// 目录输入框
rootDirInput : TextComponent | null = null ;
// 标记设置是否已修改但未保存
dirtySettings : boolean = false ;
// 自动保存目录的方法
autoSaveRootDir : ( value : string ) = > Promise < void > ;
constructor ( app : App , plugin : Plugin ) {
super ( app , plugin ) ;
this . plugin = plugin ;
2025-04-27 07:00:37 +00:00
// 获取父插件中的设置管理器引用 - 使用类型断言为DailyTaskPlugin而不是any
this . settingsManager = ( plugin as DailyTaskPlugin ) . settingsManager ;
2025-04-22 05:59:28 +00:00
// 创建任务生成器
this . taskGenerator = new TaskGenerator ( app , this . settingsManager ) ;
}
display ( ) : void {
const { containerEl } = this ;
containerEl . empty ( ) ;
containerEl . classList . add ( 'daily-task-setting-tab' ) ;
const settings = this . settingsManager . getSettings ( ) ;
// 添加顶部间距(增加间距以改善界面美观)
2025-04-26 01:55:26 +00:00
const topSpacing = document . createElement ( 'div' ) ;
2025-04-23 05:06:41 +00:00
topSpacing . classList . add ( SettingTopSpaceCSS ) ;
2025-04-26 01:55:26 +00:00
containerEl . appendChild ( topSpacing ) ;
2025-04-22 05:59:28 +00:00
// 根目录设置
const rootDirSetting = new Setting ( containerEl )
. setName ( getTranslation ( 'settings.rootDir' ) )
. setDesc ( getTranslation ( 'settings.rootDir.desc' ) ) ;
// 创建输入框容器,使其可以包含额外元素
const inputContainer = document . createElement ( 'div' ) ;
2025-04-23 05:06:41 +00:00
inputContainer . classList . add ( InputContainerCSS ) ;
2025-04-22 05:59:28 +00:00
rootDirSetting . controlEl . appendChild ( inputContainer ) ;
this . rootDirInput = new TextComponent ( inputContainer )
. setValue ( settings . rootDir )
. onChange ( async ( value ) = > {
if ( value . trim ( ) !== '' ) {
// 设置已更改,准备自动保存
this . dirtySettings = true ;
// 启动自动保存定时器
this . autoSaveRootDir ( value ) ;
}
} ) ;
2025-04-23 05:06:41 +00:00
// 给input元素设置类和placeholder属性
2025-04-22 05:59:28 +00:00
if ( this . rootDirInput && this . rootDirInput . inputEl ) {
2025-04-23 05:06:41 +00:00
this . rootDirInput . inputEl . classList . add ( InputCSS ) ;
2025-04-22 05:59:28 +00:00
this . rootDirInput . inputEl . placeholder = 'DailyTasks' ;
}
// 添加自动保存指示器
const saveIndicator = document . createElement ( 'div' ) ;
2025-04-23 05:06:41 +00:00
saveIndicator . classList . add ( SaveIndicatorCSS ) ;
2025-04-22 05:59:28 +00:00
inputContainer . appendChild ( saveIndicator ) ;
// 创建保存成功图标
2025-04-26 01:55:26 +00:00
const saveSuccessIcon = document . createElement ( 'span' ) ;
saveSuccessIcon . classList . add ( 'svg-icon' , 'lucide-check' , SuccessIconCSS ) ;
2025-04-22 05:59:28 +00:00
saveIndicator . appendChild ( saveSuccessIcon ) ;
// 记录自动保存定时器
2025-06-03 04:38:07 +00:00
let autoSaveTimer : number | null = null ;
2025-04-22 05:59:28 +00:00
// 自动保存方法
this . autoSaveRootDir = async ( value : string ) = > {
// 清除之前的定时器
if ( autoSaveTimer !== null ) {
2025-06-03 04:38:07 +00:00
window . clearTimeout ( autoSaveTimer ) ;
2025-04-22 05:59:28 +00:00
}
// 设置新的定时器, 延迟800ms保存( 在用户停止输入后)
2025-06-03 04:38:07 +00:00
autoSaveTimer = window . setTimeout ( async ( ) = > {
2025-04-22 05:59:28 +00:00
let pathToSave = value . trim ( ) ;
if ( pathToSave === '' ) {
pathToSave = 'DailyTasks' ; // 默认存放目录
}
2025-04-23 05:06:41 +00:00
// 实际保存设置
2025-04-22 05:59:28 +00:00
await this . settingsManager . updateSettings ( { rootDir : pathToSave } ) ;
this . dirtySettings = false ;
2025-04-23 05:06:41 +00:00
// 显示保存成功的视觉反馈
2025-04-27 07:00:37 +00:00
saveIndicator . classList . add ( 'save-indicator-visible' ) ;
saveIndicator . classList . remove ( 'save-indicator-hidden' ) ;
2025-06-03 04:38:07 +00:00
window . setTimeout ( ( ) = > {
2025-04-27 07:00:37 +00:00
saveIndicator . classList . remove ( 'save-indicator-visible' ) ;
saveIndicator . classList . add ( 'save-indicator-hidden' ) ;
2025-04-23 05:06:41 +00:00
} , 1500 ) ;
} , 800 ) ;
2025-04-22 05:59:28 +00:00
} ;
// 自动生成模式
const autoGenSetting = new Setting ( containerEl )
. setName ( getTranslation ( 'settings.autoGenerate' ) )
. setDesc ( getTranslation ( 'settings.autoGenerate.desc' ) ) ;
// 自定义三选滑块
const toggleContainer = document . createElement ( 'div' ) ;
toggleContainer . classList . add ( 'mode-toggle-container' ) ;
2025-04-26 01:55:26 +00:00
autoGenSetting . controlEl . appendChild ( toggleContainer ) ;
2025-04-22 05:59:28 +00:00
const modes = [
{ value : AutoGenerateMode.NONE , label : getTranslation ( 'settings.mode.none' ) } ,
{ value : AutoGenerateMode.DAILY , label : getTranslation ( 'settings.mode.daily' ) } ,
{ value : AutoGenerateMode.WORKDAY , label : getTranslation ( 'settings.mode.workday' ) }
] ;
// 滑块指示器
const slider = document . createElement ( 'div' ) ;
slider . classList . add ( 'mode-toggle-slider' ) ;
toggleContainer . appendChild ( slider ) ;
// 更新滑块位置
const updateSlider = ( index : number ) = > {
2025-04-27 07:00:37 +00:00
// 移除之前的位置类
slider . classList . remove ( 'slider-position-0' , 'slider-position-1' , 'slider-position-2' ) ;
// 添加新的位置类
slider . classList . add ( ` slider-position- ${ index } ` ) ;
2025-04-22 05:59:28 +00:00
} ;
// 设置默认模式为仅工作日生成( WorkDay)
let currentModeIndex = modes . findIndex ( mode = > mode . value === settings . autoGenerateMode ) ;
if ( currentModeIndex === - 1 ) {
// 如果没有找到有效的模式,设置为工作日模式
currentModeIndex = modes . findIndex ( mode = > mode . value === AutoGenerateMode . WORKDAY ) ;
// 更新设置到工作日模式
this . settingsManager . updateSettings ( { autoGenerateMode : AutoGenerateMode.WORKDAY } ) ;
}
updateSlider ( currentModeIndex ) ;
// 创建选项
modes . forEach ( ( mode , index ) = > {
const option = document . createElement ( 'div' ) ;
option . classList . add ( 'mode-toggle-option' ) ;
option . textContent = mode . label ;
if ( mode . value === settings . autoGenerateMode ) {
option . classList . add ( 'active' ) ;
}
option . addEventListener ( 'click' , async ( ) = > {
// 更新视觉状态
toggleContainer . querySelectorAll ( '.mode-toggle-option' ) . forEach ( el = > {
el . classList . remove ( 'active' ) ;
} ) ;
option . classList . add ( 'active' ) ;
// 更新滑块位置
updateSlider ( index ) ;
// 保存设置
await this . settingsManager . updateSettings ( { autoGenerateMode : mode.value } ) ;
} ) ;
toggleContainer . appendChild ( option ) ;
} ) ;
// 语言设置
2025-06-03 04:38:07 +00:00
// TODO: 应该使用Obsidian API的app.i18n.locale获取系统语言设置
// 需要在manifest.json中设置minAppVersion: "1.8.0"或更高
// 可以移除此设置并修改SettingsManager.getCurrentLanguage()方法
2025-04-22 05:59:28 +00:00
new Setting ( containerEl )
. setName ( getTranslation ( 'settings.language' ) )
. setDesc ( getTranslation ( 'settings.language.desc' ) )
. addDropdown ( dropdown = > {
dropdown
. addOption ( Language . AUTO , getTranslation ( 'settings.language.auto' ) )
. addOption ( Language . ZH , getTranslation ( 'settings.language.zh' ) )
. addOption ( Language . EN , getTranslation ( 'settings.language.en' ) )
. setValue ( settings . language )
. onChange ( async ( value ) = > {
await this . settingsManager . updateSettings ( { language : value as Language } ) ;
// 需要重新加载设置页面以更新翻译
this . display ( ) ;
} ) ;
} ) ;
// 通知显示时间
new Setting ( containerEl )
. setName ( getTranslation ( 'settings.notificationDuration' ) )
. setDesc ( getTranslation ( 'settings.notificationDuration.desc' ) )
. addText ( text = > {
const component = text
. setValue ( settings . successNotificationDuration . toString ( ) )
. onChange ( async ( value ) = > {
const duration = parseInt ( value ) ;
if ( ! isNaN ( duration ) && duration > 0 ) {
await this . settingsManager . updateSettings ( { successNotificationDuration : duration } ) ;
}
} ) ;
// 直接设置placeholder属性
component . inputEl . placeholder = '3000' ;
return component ;
} ) ;
// 任务统计功能开关
new Setting ( containerEl )
. setName ( getTranslation ( 'settings.taskStatistics' ) )
. setDesc ( getTranslation ( 'settings.taskStatistics.desc' ) )
. addToggle ( ( toggle ) = > {
// 设置开关样式
const toggleEl = toggle
. setValue ( settings . taskStatistics )
. onChange ( async ( value ) = > {
await this . settingsManager . updateSettings ( { taskStatistics : value } ) ;
} ) ;
// 自定义开关样式 - 使用DOM元素访问
// @ts-ignore - 添加这行来忽略TypeScript警告
const toggleControl = toggle . toggleEl || toggle . containerEl . querySelector ( '.checkbox-container' ) ;
if ( toggleControl ) {
toggleControl . classList . add ( 'task-statistics-toggle' ) ;
// 添加图标 - 使用DOM父元素访问
const toggleContainer = toggleControl . parentElement ;
if ( toggleContainer ) {
2025-04-26 01:55:26 +00:00
const iconEl = document . createElement ( 'span' ) ;
iconEl . classList . add ( 'svg-icon' , 'lucide-bar-chart-2' ) ;
2025-04-22 05:59:28 +00:00
toggleContainer . prepend ( iconEl ) ;
}
}
return toggleEl ;
} ) ;
2025-06-03 04:38:07 +00:00
// 模板设置 - 使用常规方式创建标题
// TODO: 如果Obsidian API支持, 应该使用Setting.setHeading()方法创建标题
// 例如: new Setting(containerEl).setName(getTranslation('settings.template')).setHeading();
containerEl . createEl ( 'h3' , { text : getTranslation ( 'settings.template' ) } ) ;
2025-04-22 05:59:28 +00:00
// 添加模板使用逻辑说明
2025-04-26 01:55:26 +00:00
const templateDescription = document . createElement ( 'p' ) ;
templateDescription . classList . add ( 'template-description' ) ;
templateDescription . textContent = this . settingsManager . getCurrentLanguage ( ) === 'zh' ?
'注意:默认模板会根据当前语言环境自动选择对应语言的内容。如果您自定义模板,将在所有语言环境中使用您的自定义内容。' :
'Note: Default template automatically adapts to your language environment. If you customize the template, your content will be used in all language environments.' ;
containerEl . appendChild ( templateDescription ) ;
2025-04-22 05:59:28 +00:00
// 添加模板变量说明
2025-04-26 01:55:26 +00:00
const templateVariablesEl = document . createElement ( 'div' ) ;
templateVariablesEl . classList . add ( 'template-variables' ) ;
2025-04-27 07:00:37 +00:00
// 创建变量内容
2025-04-26 01:55:26 +00:00
const variablesContent = this . settingsManager . getCurrentLanguage ( ) === 'zh' ?
2025-04-22 05:59:28 +00:00
'<strong>可用变量:</strong> {{date}} - 日期, {{dateWithIcon}} - 带图标的日期, {{weekday}} - 星期几, {{yearProgress}} - 年进度, {{monthProgress}} - 月进度, {{time}} - 当前时间' :
'<strong>Available variables:</strong> {{date}} - Date, {{dateWithIcon}} - Date with icon, {{weekday}} - Day of week, {{yearProgress}} - Year progress, {{monthProgress}} - Month progress, {{time}} - Current time' ;
2025-04-27 07:00:37 +00:00
// 使用DOMParser安全地解析HTML内容
const parser = new DOMParser ( ) ;
const doc = parser . parseFromString ( variablesContent , 'text/html' ) ;
// 安全地添加解析后的内容
Array . from ( doc . body . childNodes ) . forEach ( node = > {
templateVariablesEl . appendChild ( document . importNode ( node , true ) ) ;
} ) ;
2025-04-26 01:55:26 +00:00
containerEl . appendChild ( templateVariablesEl ) ;
2025-04-22 05:59:28 +00:00
// 单一模板设置
const templateSetting = new Setting ( containerEl )
. setName ( getTranslation ( 'settings.template' ) )
. setClass ( 'template-setting' ) ;
const templateContainer = document . createElement ( 'div' ) ;
2025-04-27 07:00:37 +00:00
templateContainer . classList . add ( 'template-container' , 'full-width-container' ) ;
2025-04-22 05:59:28 +00:00
// 获取当前模板内容
const currentTemplate = this . settingsManager . hasCustomTemplate ( ) ?
this . settingsManager . getSettings ( ) . customTemplate :
this . settingsManager . getTemplateByLanguage ( ) ;
const textarea = new TextAreaComponent ( templateContainer )
. setValue ( currentTemplate )
. setPlaceholder ( this . settingsManager . getCurrentLanguage ( ) === 'zh' ?
'在此处输入任务模板...' :
'Enter task template here...' )
. onChange ( async ( value ) = > {
// 更新为自定义模板
await this . settingsManager . updateSettings ( {
customTemplate : value ,
hasCustomTemplate : true
} ) ;
this . updatePreview ( this . previewEl , value ) ;
} ) ;
2025-04-26 01:55:26 +00:00
// 添加样式类
2025-04-22 05:59:28 +00:00
textarea . inputEl . classList . add ( 'template-editor' ) ;
// 预览标题, 使用flex布局居中
const previewHeader = document . createElement ( 'div' ) ;
previewHeader . classList . add ( 'template-preview-header' ) ;
// 预览按钮容器 - 左侧
const previewBtnContainer = document . createElement ( 'div' ) ;
2025-04-26 01:55:26 +00:00
previewBtnContainer . classList . add ( 'button-container' ) ;
2025-04-22 05:59:28 +00:00
// 重置按钮容器 - 右侧
const resetBtnContainer = document . createElement ( 'div' ) ;
2025-04-26 01:55:26 +00:00
resetBtnContainer . classList . add ( 'button-container' ) ;
2025-04-22 05:59:28 +00:00
// 预览按钮 - 改进样式
const toggleButton = new ButtonComponent ( previewBtnContainer )
. setButtonText ( getTranslation ( 'settings.template.preview' ) ) ;
// 添加样式类
toggleButton . buttonEl . addClass ( TextCenterCSS ) ;
2025-04-26 01:55:26 +00:00
toggleButton . buttonEl . addClass ( 'daily-task-button-common' ) ;
toggleButton . buttonEl . addClass ( 'daily-task-button-md' ) ;
2025-04-22 05:59:28 +00:00
// 手动添加眼睛图标
2025-04-26 01:55:26 +00:00
const eyeIcon = document . createElement ( 'span' ) ;
eyeIcon . classList . add ( 'svg-icon' , 'lucide-eye' ) ;
2025-04-22 05:59:28 +00:00
toggleButton . buttonEl . prepend ( eyeIcon ) ;
// 预览区域
this . previewEl = document . createElement ( 'div' ) ;
this . previewEl . classList . add ( 'template-preview' ) ;
this . updatePreview ( this . previewEl , currentTemplate ) ;
templateContainer . appendChild ( this . previewEl ) ;
toggleButton . onClick ( ( ) = > {
this . togglePreview ( this . previewEl ) ;
// 切换图标和按钮文本
if ( this . previewEl && this . previewEl . classList . contains ( 'visible' ) ) {
eyeIcon . className = 'svg-icon lucide-eye-off' ;
toggleButton . setButtonText ( getTranslation ( 'settings.template.hide' ) ) ;
2025-04-26 01:55:26 +00:00
toggleButton . buttonEl . classList . add ( 'success-button' ) ;
2025-04-22 05:59:28 +00:00
} else {
eyeIcon . className = 'svg-icon lucide-eye' ;
toggleButton . setButtonText ( getTranslation ( 'settings.template.preview' ) ) ;
2025-04-26 01:55:26 +00:00
toggleButton . buttonEl . classList . remove ( 'success-button' ) ;
2025-04-22 05:59:28 +00:00
}
} ) ;
// 重置按钮 - 改进样式
const resetBtn = new ButtonComponent ( resetBtnContainer )
. setButtonText ( getTranslation ( 'settings.resetDefault' ) ) ;
// 添加样式类
resetBtn . buttonEl . addClass ( TextCenterCSS ) ;
2025-04-26 01:55:26 +00:00
resetBtn . buttonEl . addClass ( 'daily-task-button-common' ) ;
resetBtn . buttonEl . addClass ( 'daily-task-button-lg' ) ;
2025-04-22 05:59:28 +00:00
// 添加重置图标
2025-04-26 01:55:26 +00:00
const resetIcon = document . createElement ( 'span' ) ;
resetIcon . classList . add ( 'svg-icon' , 'lucide-refresh-cw' ) ;
2025-04-22 05:59:28 +00:00
resetBtn . buttonEl . prepend ( resetIcon ) ;
// 添加重置事件
resetBtn . onClick ( async ( ) = > {
// 将自定义模板设置为空,回到使用默认模板
await this . settingsManager . updateSettings ( {
customTemplate : '' ,
hasCustomTemplate : false
} ) ;
// 获取当前语言的默认模板
const defaultTemplate = this . settingsManager . getTemplateByLanguage ( ) ;
// 更新输入框和预览
textarea . setValue ( defaultTemplate ) ;
this . updatePreview ( this . previewEl , defaultTemplate ) ;
// 显示成功提示动画
2025-04-26 01:55:26 +00:00
resetBtn . buttonEl . classList . add ( 'success-button' ) ;
2025-06-03 04:38:07 +00:00
window . setTimeout ( ( ) = > {
2025-04-26 01:55:26 +00:00
resetBtn . buttonEl . classList . remove ( 'success-button' ) ;
2025-04-22 05:59:28 +00:00
} , 1000 ) ;
} ) ;
// 将按钮添加到各自的容器
previewHeader . appendChild ( previewBtnContainer ) ;
previewHeader . appendChild ( resetBtnContainer ) ;
templateContainer . appendChild ( previewHeader ) ;
templateSetting . controlEl . appendChild ( templateContainer ) ;
// 恢复默认设置 - 创建容器让按钮右对齐
const resetContainer = document . createElement ( 'div' ) ;
2025-04-26 01:55:26 +00:00
resetContainer . classList . add ( 'button-container' ) ;
2025-04-22 05:59:28 +00:00
containerEl . appendChild ( resetContainer ) ;
// 恢复默认设置按钮
const resetDefaultBtn = new ButtonComponent ( resetContainer )
. setButtonText ( getTranslation ( 'settings.resetToDefault' ) ) ;
// 添加样式类
resetDefaultBtn . buttonEl . addClass ( TextCenterCSS ) ;
resetDefaultBtn . buttonEl . addClass ( 'danger-button' ) ;
2025-04-26 01:55:26 +00:00
resetDefaultBtn . buttonEl . addClass ( 'daily-task-button-common' ) ;
resetDefaultBtn . buttonEl . addClass ( 'daily-task-button-lg' ) ;
2025-04-22 05:59:28 +00:00
2025-04-26 01:55:26 +00:00
// 添加重置图标
const resetIcon2 = document . createElement ( 'span' ) ;
resetIcon2 . classList . add ( 'svg-icon' , 'lucide-refresh-cw' ) ;
resetDefaultBtn . buttonEl . prepend ( resetIcon2 ) ;
2025-04-22 05:59:28 +00:00
// 为全局重置按钮添加事件处理
resetDefaultBtn . onClick ( async ( ) = > {
await this . settingsManager . resetToDefaults ( ) ;
this . display ( ) ;
} ) ;
// 手动添加今日任务按钮 - 右对齐显示
const addTaskContainer = document . createElement ( 'div' ) ;
2025-04-26 01:55:26 +00:00
addTaskContainer . classList . add ( 'button-container' ) ;
2025-04-22 05:59:28 +00:00
containerEl . appendChild ( addTaskContainer ) ;
this . addTaskButton = new ButtonComponent ( addTaskContainer )
. setButtonText ( getTranslation ( 'settings.addTaskButton' ) )
. setCta ( ) ;
// 添加样式类 - 确保按钮文字居中
if ( this . addTaskButton && this . addTaskButton . buttonEl ) {
this . addTaskButton . buttonEl . addClass ( TextCenterCSS ) ;
2025-04-26 01:55:26 +00:00
this . addTaskButton . buttonEl . addClass ( 'daily-task-button-common' ) ;
2025-04-22 05:59:28 +00:00
}
// 手动添加任务按钮事件处理
this . addTaskButton . onClick ( async ( ) = > {
// 检查目录设置
const rootDir = this . settingsManager . getSettings ( ) . rootDir ;
// 添加loading状态
if ( this . addTaskButton && this . addTaskButton . buttonEl ) {
this . addTaskButton . buttonEl . classList . add ( 'loading' ) ;
}
this . addTaskButton ? . setDisabled ( true ) ;
try {
// 添加任务
await this . taskGenerator . addTaskManually ( ) ;
} catch ( e ) {
new Notice ( ` 添加任务失败: ${ e . message || e } ` ) ;
} finally {
// 移除loading状态
2025-06-03 04:38:07 +00:00
window . setTimeout ( ( ) = > {
2025-04-22 05:59:28 +00:00
if ( this . addTaskButton && this . addTaskButton . buttonEl ) {
this . addTaskButton . buttonEl . classList . remove ( 'loading' ) ;
}
this . addTaskButton ? . setDisabled ( false ) ;
} , 500 ) ;
}
} ) ;
// 添加图标
2025-04-26 01:55:26 +00:00
const calendarIcon = document . createElement ( 'span' ) ;
calendarIcon . classList . add ( 'svg-icon' , 'lucide-calendar-plus' ) ;
this . addTaskButton . buttonEl . prepend ( calendarIcon ) ;
2025-04-22 05:59:28 +00:00
}
/ * *
* 更 新 模 板 预 览
* /
private updatePreview ( previewEl : HTMLElement | null , template : string ) : void {
if ( ! previewEl ) return ;
const renderedContent = renderTemplate ( template ) ;
2025-04-26 01:55:26 +00:00
// 使用domUtils中的工具函数安全地设置内容
setTextContentByLines ( previewEl , renderedContent ) ;
2025-04-22 05:59:28 +00:00
}
/ * *
* 切 换 预 览 的 显 示 / 隐 藏
* /
private togglePreview ( previewEl : HTMLElement | null ) : void {
if ( ! previewEl ) return ;
2025-04-27 07:00:37 +00:00
if ( previewEl . classList . contains ( 'hidden-element' ) ) {
previewEl . classList . remove ( 'hidden-element' ) ;
previewEl . classList . add ( 'visible-element' , 'visible' ) ;
2025-04-22 05:59:28 +00:00
} else {
2025-04-27 07:00:37 +00:00
previewEl . classList . add ( 'hidden-element' ) ;
previewEl . classList . remove ( 'visible-element' , 'visible' ) ;
2025-04-22 05:59:28 +00:00
}
}
}
/ * *
* 设 置 管 理 器
* 负 责 加 载 、 保 存 和 提 供 设 置 访 问 接 口
* /
export class SettingsManager {
private plugin : Plugin ;
private settings : DailyTaskSettings ;
constructor ( plugin : Plugin ) {
this . plugin = plugin ;
this . settings = Object . assign ( { } , DEFAULT_SETTINGS ) ;
}
/ * *
* 获 取 当 前 设 置
* /
getSettings ( ) : DailyTaskSettings {
return this . settings ;
}
/ * *
* 更 新 设 置 并 保 存
* @param settings 要 更 新 的 设 置
* /
async updateSettings ( settings : Partial < DailyTaskSettings > ) : Promise < void > {
this . settings = {
. . . this . settings ,
. . . settings
} ;
await this . saveSettings ( ) ;
// 更新当前语言
this . updateCurrentLanguage ( ) ;
}
/ * *
* 保 存 设 置 到 数 据 存 储
* /
async saveSettings ( ) : Promise < void > {
2025-04-27 07:00:37 +00:00
await ( this . plugin as DailyTaskPlugin ) . saveData ( this . settings ) ;
2025-04-22 05:59:28 +00:00
}
/ * *
* 加 载 设 置
* /
async loadSettings ( ) : Promise < void > {
2025-04-27 07:00:37 +00:00
const loadedData = await ( this . plugin as DailyTaskPlugin ) . loadData ( ) ;
2025-04-22 05:59:28 +00:00
if ( loadedData ) {
// 合并默认设置和已保存的设置
this . settings = {
. . . DEFAULT_SETTINGS ,
. . . loadedData
} ;
// 确保在升级插件后,新增的设置项也有默认值
this . ensureSettingsCompleteness ( ) ;
} else {
// 如果没有加载到数据,使用默认设置但将自动生成模式改为工作日
this . settings = {
. . . DEFAULT_SETTINGS ,
autoGenerateMode : AutoGenerateMode.WORKDAY
} ;
}
// 更新当前语言
this . updateCurrentLanguage ( ) ;
}
/ * *
* 确 保 设 置 完 整 性 , 为 新 增 的 设 置 项 提 供 默 认 值
* /
private ensureSettingsCompleteness ( ) : void {
const defaultKeys = Object . keys ( DEFAULT_SETTINGS ) ;
defaultKeys . forEach ( key = > {
// 如果当前设置中缺少某个默认设置项,添加默认值
if ( ! ( key in this . settings ) ) {
2025-04-27 07:00:37 +00:00
// 先将对象转换为unknown, 再转换为Record<string, unknown>
( ( this . settings as unknown ) as Record < string , unknown > ) [ key ] =
( ( DEFAULT_SETTINGS as unknown ) as Record < string , unknown > ) [ key ] ;
2025-04-22 05:59:28 +00:00
}
} ) ;
}
/ * *
* 恢 复 默 认 设 置
* /
async resetToDefaults ( ) : Promise < void > {
this . settings = Object . assign ( { } , DEFAULT_SETTINGS ) ;
await this . saveSettings ( ) ;
// 更新当前语言
this . updateCurrentLanguage ( ) ;
}
/ * *
* 根 据 语 言 获 取 当 前 使 用 的 模 板
* 如 果 当 前 模 板 不 是 默 认 模 板 , 则 不 再 区 分 语 言
* /
getCurrentTemplate ( ) : string {
const language = this . getCurrentLanguage ( ) ;
// 中文环境
if ( language === 'zh' ) {
// 如果中文模板已被修改(不等于默认模板),使用中文模板
if ( this . settings . templateZh !== DEFAULT_TEMPLATE_ZH ) {
return this . settings . templateZh ;
}
// 如果英文模板已被修改,使用英文模板
if ( this . settings . templateEn !== DEFAULT_TEMPLATE_EN ) {
return this . settings . templateEn ;
}
// 如果都是默认模板,使用中文默认模板
return this . settings . templateZh ;
}
// 英文环境
else {
// 如果英文模板已被修改(不等于默认模板),使用英文模板
if ( this . settings . templateEn !== DEFAULT_TEMPLATE_EN ) {
return this . settings . templateEn ;
}
// 如果中文模板已被修改,使用中文模板
if ( this . settings . templateZh !== DEFAULT_TEMPLATE_ZH ) {
return this . settings . templateZh ;
}
// 如果都是默认模板,使用英文默认模板
return this . settings . templateEn ;
}
}
/ * *
* 获 取 当 前 语 言 设 置
* /
getCurrentLanguage ( ) : string {
2025-06-03 04:38:07 +00:00
// TODO: 应该使用Obsidian API的app.i18n.locale获取系统语言设置
// 例如: return this.plugin.app.i18n.locale.startsWith('zh') ? 'zh' : 'en';
2025-04-22 05:59:28 +00:00
if ( this . settings . language === Language . AUTO ) {
// 自动检测系统语言
const systemLanguage = window . navigator . language . toLowerCase ( ) ;
return systemLanguage . startsWith ( 'zh' ) ? 'zh' : 'en' ;
}
return this . settings . language ;
}
/ * *
* 更 新 当 前 语 言
* /
private updateCurrentLanguage ( ) : void {
const language = this . getCurrentLanguage ( ) ;
setCurrentLanguage ( language ) ;
}
/ * *
* 获 取 当 前 语 言 的 模 板
* /
getTemplateByLanguage ( ) : string {
const language = this . getCurrentLanguage ( ) ;
// 中文环境
if ( language === 'zh' ) {
// 如果中文模板已被修改(不等于默认模板),使用中文模板
if ( this . settings . templateZh !== DEFAULT_TEMPLATE_ZH ) {
return this . settings . templateZh ;
}
// 如果英文模板已被修改,使用英文模板
if ( this . settings . templateEn !== DEFAULT_TEMPLATE_EN ) {
return this . settings . templateEn ;
}
// 如果都是默认模板,使用中文默认模板
return this . settings . templateZh ;
}
// 英文环境
else {
// 如果英文模板已被修改(不等于默认模板),使用英文模板
if ( this . settings . templateEn !== DEFAULT_TEMPLATE_EN ) {
return this . settings . templateEn ;
}
// 如果中文模板已被修改,使用中文模板
if ( this . settings . templateZh !== DEFAULT_TEMPLATE_ZH ) {
return this . settings . templateZh ;
}
// 如果都是默认模板,使用英文默认模板
return this . settings . templateEn ;
}
}
/ * *
* 检 查 是 否 存 在 自 定 义 模 板
* /
hasCustomTemplate ( ) : boolean {
return ! ! this . settings . customTemplate ;
}
}