2026-07-14 08:20:29 +00:00
/ *
* 代码块快捷插入 - main . js
* 纯 JavaScript 实现 , 无需编译 , 直接放入插件目录即可运行
*
* 功能 :
* 1. 快捷插入代码块 ` ` ` lang ... ` ` ` , 光标自动定位到块内
* 2. 选中文本时 , 用代码块包裹选中的内容
* 3. 自定义默认语言 + 常用语言列表
* 4. 每个语言注册独立命令
* 5. 「 选择语言 」 命令弹出列表供选择
* 6. 快捷键配置 : 默认一项 ( 默认语言 ) , 用户可自行添加更多
* 7. 设置快捷键后即时检测冲突 , 冲突时显示警告 + 跳转入口
*
* 替代 Templater 的 codepython . md 模板 , 无需 Templater 依赖 。
2026-07-19 11:54:46 +00:00
*
* 双语 UI ( 中文 / 英文 ) , 可在设置面板顶部切换界面语言 。
2026-07-14 08:20:29 +00:00
* /
const { Plugin , Notice , PluginSettingTab , Setting , Modal } = require ( "obsidian" ) ;
const PLUGIN _ID = "quick-codeblock" ;
2026-07-19 11:54:46 +00:00
// ================================================================
// i18n (Bilingual support) 中英双语
// ================================================================
const I18N = {
zh : {
// Commands
cmd _insert _default : "插入代码块({lang}) " ,
cmd _insert _pick : "插入代码块(选择语言)" ,
cmd _insert _lang : "插入代码块({lang}) " ,
// Modal
modal _pick _title : "选择代码块语言" ,
// Settings - title
setting _title : "代码块快捷插入" ,
// Settings - language switcher
setting _language : "界面语言" ,
setting _language _desc : "选择设置面板的显示语言" ,
lang _zh : "中文" ,
lang _en : "English" ,
// Settings - basic
setting _default _lang : "默认语言" ,
setting _default _lang _desc : "主命令「插入代码块」使用的语言" ,
setting _lang _list : "语言列表" ,
setting _lang _list _desc : "每行一个语言,会为每个语言注册独立命令。修改后重新打开设置面板可刷新下方选项。" ,
setting _keep _indent : "保留选中内容缩进" ,
setting _keep _indent _desc : "选中文本包裹成代码块时,是否保留原有缩进" ,
// Settings - hotkeys section
sec _hotkeys : "快捷键设置" ,
hotkeys _desc : "为需要的语言配置快捷键。点击输入框后按下组合键即可设置,按 <code>Backspace</code> 或 <code>Esc</code> 清除。设置后自动检测冲突。" ,
btn _add _hotkey : "+ 添加快捷键配置" ,
hotkey _placeholder : "点击设置…" ,
// Settings - more hotkeys
setting _more _hotkeys : "更多快捷键设置" ,
setting _more _hotkeys _desc : "打开 Obsidian 系统的快捷键设置页面,可查看 / 修改所有命令的快捷键" ,
btn _open _hotkeys : "打开 Obsidian 快捷键设置" ,
notice _hotkeys _opened : "已打开快捷键设置,可在搜索框输入命令名查找" ,
notice _hotkeys _failed : "无法自动打开,请手动进入:设置 → 快捷键" ,
// Settings - tips
tip _title : "使用方法" ,
tip _1 : "1. <code>Ctrl+P</code> 打开命令面板,搜索「插入代码块」" ,
tip _2 : "2. 无选中文本时:插入空代码块,光标自动定位到块内" ,
tip _3 : "3. 有选中文本时:用 <code>```lang</code> 包裹选中的内容" ,
tip _4 : "4. 在上方「快捷键设置」区域,点击「+ 添加」为需要的语言配置快捷键" ,
tip _5 : "5. 修改语言列表后,关闭再打开设置面板即可刷新选项" ,
// Hotkey config item
conflict _label : "⚠️ 冲突:" ,
conflict _link : "前往修改 › " ,
no _conflict : "✓ 无冲突" ,
btn _delete _config : "删除此配置" ,
notice _cleared : "已清除:{lang} 快捷键" ,
notice _set _failed : "设置失败:{msg}" ,
notice _conflict : "⚠️ 「{hotkey}」与「{name}」冲突,点击警告中的「前往修改」处理" ,
notice _set : "已设置:{lang} → {hotkey}" ,
notice _search _conflict : "请在快捷键设置中搜索「{name}」查看并修改" ,
// Settings - reset
setting _reset : "恢复默认设置" ,
setting _reset _desc : "将所有设置恢复为默认值(界面语言选择会被保留)" ,
btn _reset : "重置" ,
notice _reset : "设置已恢复为默认值" ,
} ,
en : {
// Commands
cmd _insert _default : "Insert code block ({lang})" ,
cmd _insert _pick : "Insert code block (pick language)" ,
cmd _insert _lang : "Insert code block ({lang})" ,
// Modal
modal _pick _title : "Choose code block language" ,
// Settings - title
setting _title : "Quick CodeBlock" ,
// Settings - language switcher
setting _language : "UI Language" ,
setting _language _desc : "Select the display language for settings panel" ,
lang _zh : "中文" ,
lang _en : "English" ,
// Settings - basic
setting _default _lang : "Default language" ,
setting _default _lang _desc : "Language used by the main \"Insert code block\" command" ,
setting _lang _list : "Language list" ,
setting _lang _list _desc : "One language per line. An independent command is registered for each. Reopen the settings panel after editing to refresh options below." ,
setting _keep _indent : "Keep selection indentation" ,
setting _keep _indent _desc : "Whether to preserve original indentation when wrapping selected text into a code block" ,
// Settings - hotkeys section
sec _hotkeys : "Hotkey Settings" ,
hotkeys _desc : "Configure hotkeys for the languages you need. Click the input box then press a key combo to set, press <code>Backspace</code> or <code>Esc</code> to clear. Conflicts are detected automatically." ,
btn _add _hotkey : "+ Add hotkey config" ,
hotkey _placeholder : "Click to set…" ,
// Settings - more hotkeys
setting _more _hotkeys : "More hotkey settings" ,
setting _more _hotkeys _desc : "Open Obsidian's built-in hotkey settings to view / modify hotkeys for all commands" ,
btn _open _hotkeys : "Open Obsidian hotkey settings" ,
notice _hotkeys _opened : "Hotkey settings opened. Type a command name in the search box to find it." ,
notice _hotkeys _failed : "Unable to open automatically. Please go to: Settings → Hotkeys" ,
// Settings - tips
tip _title : "Usage" ,
tip _1 : "1. Press <code>Ctrl+P</code> to open the command palette and search for \"Insert code block\"" ,
tip _2 : "2. With no selection: inserts an empty code block and places the cursor inside" ,
tip _3 : "3. With a selection: wraps the selected text with <code>```lang</code>" ,
tip _4 : "4. In the \"Hotkey Settings\" area above, click \"+ Add\" to configure hotkeys for languages you need" ,
tip _5 : "5. After editing the language list, close and reopen the settings panel to refresh options" ,
// Hotkey config item
conflict _label : "⚠️ Conflict: " ,
conflict _link : "Fix › " ,
no _conflict : "✓ No conflict" ,
btn _delete _config : "Delete this config" ,
notice _cleared : "Cleared: {lang} hotkey" ,
notice _set _failed : "Failed to set: {msg}" ,
notice _conflict : "⚠️ \"{hotkey}\" conflicts with \"{name}\". Click \"Fix\" in the warning to resolve." ,
notice _set : "Set: {lang} → {hotkey}" ,
notice _search _conflict : 'Search "{name}" in hotkey settings to view and modify it' ,
// Settings - reset
setting _reset : "Reset to defaults" ,
setting _reset _desc : "Restore all settings to default values (UI language choice is preserved)" ,
btn _reset : "Reset" ,
notice _reset : "Settings reset to defaults" ,
} ,
} ;
2026-07-14 08:20:29 +00:00
const DEFAULT _SETTINGS = {
defaultLanguage : "python" ,
languages : "python\njavascript\nbash\nshell\nsql\njson\nyaml\ngo\nrust\njava\nhtml\ncss" ,
keepIndent : true ,
2026-07-19 11:54:46 +00:00
language : "zh" , // "zh" or "en"
2026-07-14 08:20:29 +00:00
// 快捷键配置列表:用户主动添加,默认只有一项(默认语言)
// 每项: { lang: "python", hotkey: { modifiers, key } | null }
hotkeyConfigs : [ { lang : "python" , hotkey : null } ] ,
} ;
// ================================================================
// 工具函数(快捷键格式化 / 冲突检测 / 跳转)
// ================================================================
function formatHotkey ( hotkey ) {
if ( ! hotkey ) return "" ;
const modMap = { Mod : "Ctrl" , Ctrl : "Ctrl" , Alt : "Alt" , Shift : "Shift" , Meta : "Win" } ;
const mods = ( hotkey . modifiers || [ ] ) . slice ( ) . sort ( ) . map ( ( m ) => modMap [ m ] || m ) ;
return mods . length > 0 ? ` ${ mods . join ( " + " ) } + ${ hotkey . key } ` : hotkey . key ;
}
function getEffectiveHotkeys ( hotkeyManager , commandId ) {
try {
if ( typeof hotkeyManager . getEffectiveHotkeys === "function" ) {
return hotkeyManager . getEffectiveHotkeys ( commandId ) || [ ] ;
}
const custom = hotkeyManager . getHotkeys ? hotkeyManager . getHotkeys ( commandId ) || [ ] : [ ] ;
const baked = hotkeyManager . getBakedHotkeys ? hotkeyManager . getBakedHotkeys ( commandId ) || [ ] : [ ] ;
return custom . length > 0 ? custom : baked ;
} catch ( e ) {
return [ ] ;
}
}
function findConflict ( app , ownCommandId , hotkey ) {
if ( ! hotkey ) return null ;
const targetCombo = formatHotkey ( hotkey ) ;
const hotkeyManager = app . hotkeyManager ;
const commands = app . commands . commands ;
for ( const id of Object . keys ( commands ) ) {
if ( id === ownCommandId ) continue ;
const effective = getEffectiveHotkeys ( hotkeyManager , id ) ;
for ( const h of effective ) {
if ( formatHotkey ( h ) === targetCombo ) {
return { commandId : id , commandName : commands [ id ] . name || id } ;
}
}
}
return null ;
}
function openHotkeysSettings ( app ) {
let opened = false ;
try {
if ( app . setting && typeof app . setting . openTabById === "function" ) {
app . setting . openTabById ( "hotkeys" ) ;
opened = true ;
}
} catch ( e ) { }
if ( ! opened ) {
try {
if ( typeof app . openSettings === "function" ) {
app . openSettings ( ) ;
opened = true ;
}
} catch ( e ) { }
}
if ( ! opened ) {
try {
app . commands . executeCommandById ( "app:open-settings" ) ;
opened = true ;
} catch ( e ) { }
}
if ( opened ) {
setTimeout ( ( ) => {
try {
if ( app . setting && typeof app . setting . openTabById === "function" ) {
app . setting . openTabById ( "hotkeys" ) ;
}
} catch ( e ) { }
} , 200 ) ;
}
return opened ;
}
// ================================================================
// 插件主类
// ================================================================
class QuickCodeBlockPlugin extends Plugin {
2026-07-19 11:54:46 +00:00
// i18n helper —— 支持可选的 {placeholder} 参数替换
t ( key , params ) {
const lang = this . settings ? this . settings . language : "zh" ;
const dict = I18N [ lang ] || I18N . zh ;
let text = dict [ key ] || key ;
if ( params ) {
for ( const k of Object . keys ( params ) ) {
text = text . replace ( new RegExp ( "\\{" + k + "\\}" , "g" ) , params [ k ] ) ;
}
}
return text ;
}
2026-07-14 08:20:29 +00:00
async onload ( ) {
await this . loadSettings ( ) ;
this . registerCommands ( ) ;
this . addSettingTab ( new CodeBlockSettingTab ( this . app , this ) ) ;
}
onunload ( ) {
// 卸载时清除所有快捷键绑定
for ( const config of this . settings . hotkeyConfigs ) {
const fullId = ` ${ PLUGIN _ID } :insert-codeblock- ${ config . lang } ` ;
try {
this . app . hotkeyManager . removeHotkeys ( fullId ) ;
} catch ( e ) { }
}
}
registerCommands ( ) {
const def = this . settings . defaultLanguage ;
// 1. 主命令
this . addCommand ( {
id : "insert-codeblock-default" ,
2026-07-19 11:54:46 +00:00
name : this . t ( "cmd_insert_default" , { lang : def } ) ,
2026-07-14 08:20:29 +00:00
editorCallback : ( editor ) => this . insertCodeBlock ( editor , def ) ,
} ) ;
// 2. 选择语言命令
this . addCommand ( {
id : "insert-codeblock-pick" ,
2026-07-19 11:54:46 +00:00
name : this . t ( "cmd_insert_pick" ) ,
2026-07-14 08:20:29 +00:00
editorCallback : ( editor ) => {
const langs = this . getLanguages ( ) ;
new LanguagePickerModal ( this . app , langs , ( lang ) => {
this . insertCodeBlock ( editor , lang ) ;
} ) . open ( ) ;
} ,
} ) ;
// 3. 为语言列表的每个语言注册命令(命令始终注册,快捷键按需绑定)
for ( const lang of this . getLanguages ( ) ) {
this . addCommand ( {
id : ` insert-codeblock- ${ lang } ` ,
2026-07-19 11:54:46 +00:00
name : this . t ( "cmd_insert_lang" , { lang : lang } ) ,
2026-07-14 08:20:29 +00:00
editorCallback : ( editor ) => this . insertCodeBlock ( editor , lang ) ,
} ) ;
}
// 4. 为 hotkeyConfigs 里的配置绑定快捷键
for ( const config of this . settings . hotkeyConfigs ) {
if ( config . hotkey ) {
const fullId = ` ${ PLUGIN _ID } :insert-codeblock- ${ config . lang } ` ;
try {
this . app . hotkeyManager . setHotkeys ( fullId , [ config . hotkey ] ) ;
} catch ( e ) {
console . error ( ` [quick-codeblock] 绑定 ${ config . lang } 快捷键失败: ` , e ) ;
}
}
}
}
// ================================================================
// 核心:插入代码块
// ================================================================
insertCodeBlock ( editor , lang ) {
const selection = editor . getSelection ( ) ;
if ( selection ) {
const content = this . settings . keepIndent ? selection : selection . trim ( ) ;
const block = "```" + lang + "\n" + content + "\n```" ;
editor . replaceSelection ( block ) ;
return ;
}
const cursor = editor . getCursor ( ) ;
const line = editor . getLine ( cursor . line ) ;
const beforeCursor = line . substring ( 0 , cursor . ch ) ;
const afterCursor = line . substring ( cursor . ch ) ;
let block = "```" + lang + "\n\n```" ;
if ( beforeCursor . trim ( ) !== "" ) block = "\n" + block ;
if ( afterCursor . trim ( ) !== "" ) block = block + "\n" ;
editor . replaceSelection ( block ) ;
const newCursor = editor . getCursor ( ) ;
const targetLine =
afterCursor . trim ( ) !== "" ? newCursor . line - 2 : newCursor . line - 1 ;
editor . setCursor ( { line : targetLine , ch : 0 } ) ;
}
getLanguages ( ) {
return this . settings . languages
. split ( /[\n,, ]/ )
. map ( ( s ) => s . trim ( ) )
. filter ( ( s ) => s . length > 0 ) ;
}
async loadSettings ( ) {
this . settings = Object . assign ( { } , DEFAULT _SETTINGS , await this . loadData ( ) ) ;
// 兼容旧格式 hotkeys 对象 → 迁移到 hotkeyConfigs 数组
if ( ! this . settings . hotkeyConfigs ) {
if ( this . settings . hotkeys && typeof this . settings . hotkeys === "object" ) {
this . settings . hotkeyConfigs = Object . entries ( this . settings . hotkeys )
. filter ( ( [ _ , h ] ) => h )
. map ( ( [ lang , hotkey ] ) => ( { lang , hotkey } ) ) ;
} else {
this . settings . hotkeyConfigs = [ { lang : this . settings . defaultLanguage , hotkey : null } ] ;
}
}
}
async saveSettings ( ) {
await this . saveData ( this . settings ) ;
}
}
// ================================================================
// 语言选择 Modal
// ================================================================
class LanguagePickerModal extends Modal {
constructor ( app , languages , onChoose ) {
super ( app ) ;
this . languages = languages ;
this . onChoose = onChoose ;
}
onOpen ( ) {
const { contentEl } = this ;
contentEl . empty ( ) ;
contentEl . addClass ( "qcb-picker" ) ;
2026-07-19 11:54:46 +00:00
contentEl . createEl ( "h2" , { text : this . app . plugins . plugins [ PLUGIN _ID ] . t ( "modal_pick_title" ) } ) ;
2026-07-14 08:20:29 +00:00
const grid = contentEl . createEl ( "div" , { cls : "qcb-lang-grid" } ) ;
for ( const lang of this . languages ) {
const item = grid . createEl ( "div" , { cls : "qcb-lang-item" , text : lang } ) ;
item . addEventListener ( "click" , ( ) => {
this . onChoose ( lang ) ;
this . close ( ) ;
} ) ;
}
}
onClose ( ) {
this . contentEl . empty ( ) ;
}
}
// ================================================================
// 设置面板
// ================================================================
class CodeBlockSettingTab extends PluginSettingTab {
2026-07-19 11:54:46 +00:00
// i18n helper —— 委托给 plugin
t ( key , params ) {
return this . plugin . t ( key , params ) ;
}
2026-07-14 08:20:29 +00:00
display ( ) {
const { containerEl } = this ;
containerEl . empty ( ) ;
2026-07-19 11:54:46 +00:00
containerEl . createEl ( "h2" , { text : this . t ( "setting_title" ) } ) ;
// ---- 界面语言切换器(顶部)----
new Setting ( containerEl )
. setName ( this . t ( "setting_language" ) )
. setDesc ( this . t ( "setting_language_desc" ) )
. addDropdown ( ( dropdown ) =>
dropdown
. addOption ( "zh" , this . t ( "lang_zh" ) )
. addOption ( "en" , this . t ( "lang_en" ) )
. setValue ( this . plugin . settings . language )
. onChange ( async ( value ) => {
this . plugin . settings . language = value ;
await this . plugin . saveSettings ( ) ;
// 重新注册命令(使命令名跟随语言)
this . plugin . registerCommands ( ) ;
// 重新渲染设置面板
this . display ( ) ;
} )
) ;
containerEl . createEl ( "hr" , { cls : "qcb-divider" } ) ;
2026-07-14 08:20:29 +00:00
// ---- 基础设置 ----
new Setting ( containerEl )
2026-07-19 11:54:46 +00:00
. setName ( this . t ( "setting_default_lang" ) )
. setDesc ( this . t ( "setting_default_lang_desc" ) )
2026-07-14 08:20:29 +00:00
. addText ( ( text ) =>
text
. setPlaceholder ( "python" )
. setValue ( this . plugin . settings . defaultLanguage )
. onChange ( async ( value ) => {
this . plugin . settings . defaultLanguage = value . trim ( ) ;
await this . plugin . saveSettings ( ) ;
} )
) ;
new Setting ( containerEl )
2026-07-19 11:54:46 +00:00
. setName ( this . t ( "setting_lang_list" ) )
. setDesc ( this . t ( "setting_lang_list_desc" ) )
2026-07-14 08:20:29 +00:00
. addTextArea ( ( text ) => {
text
. setPlaceholder ( "python\njavascript\nbash\n..." )
. setValue ( this . plugin . settings . languages )
. onChange ( async ( value ) => {
this . plugin . settings . languages = value ;
await this . plugin . saveSettings ( ) ;
} ) ;
text . inputEl . rows = 8 ;
text . inputEl . cols = 30 ;
} ) ;
new Setting ( containerEl )
2026-07-19 11:54:46 +00:00
. setName ( this . t ( "setting_keep_indent" ) )
. setDesc ( this . t ( "setting_keep_indent_desc" ) )
2026-07-14 08:20:29 +00:00
. addToggle ( ( toggle ) =>
toggle
. setValue ( this . plugin . settings . keepIndent )
. onChange ( async ( value ) => {
this . plugin . settings . keepIndent = value ;
await this . plugin . saveSettings ( ) ;
} )
) ;
// ---- 快捷键设置区域 ----
containerEl . createEl ( "hr" , { cls : "qcb-divider" } ) ;
2026-07-19 11:54:46 +00:00
containerEl . createEl ( "h3" , { text : this . t ( "sec_hotkeys" ) } ) ;
2026-07-14 08:20:29 +00:00
const desc = containerEl . createEl ( "p" , { cls : "qcb-desc" } ) ;
2026-07-19 11:54:46 +00:00
desc . innerHTML = this . t ( "hotkeys_desc" ) ;
2026-07-14 08:20:29 +00:00
// 配置列表
const configs = this . plugin . settings . hotkeyConfigs ;
for ( let i = 0 ; i < configs . length ; i ++ ) {
this . createHotkeyConfigItem ( i ) ;
}
// 添加按钮
new Setting ( containerEl ) . addButton ( ( btn ) =>
btn
2026-07-19 11:54:46 +00:00
. setButtonText ( this . t ( "btn_add_hotkey" ) )
2026-07-14 08:20:29 +00:00
. setCta ( )
. onClick ( async ( ) => {
this . plugin . settings . hotkeyConfigs . push ( {
lang : this . plugin . settings . defaultLanguage ,
hotkey : null ,
} ) ;
await this . plugin . saveSettings ( ) ;
this . display ( ) ;
} )
) ;
// ---- 更多入口 ----
containerEl . createEl ( "hr" , { cls : "qcb-divider" } ) ;
new Setting ( containerEl )
2026-07-19 11:54:46 +00:00
. setName ( this . t ( "setting_more_hotkeys" ) )
. setDesc ( this . t ( "setting_more_hotkeys_desc" ) )
2026-07-14 08:20:29 +00:00
. addButton ( ( btn ) =>
btn
2026-07-19 11:54:46 +00:00
. setButtonText ( this . t ( "btn_open_hotkeys" ) )
2026-07-14 08:20:29 +00:00
. onClick ( ( ) => {
const ok = openHotkeysSettings ( this . app ) ;
if ( ok ) {
2026-07-19 11:54:46 +00:00
new Notice ( this . t ( "notice_hotkeys_opened" ) , 6000 ) ;
2026-07-14 08:20:29 +00:00
} else {
2026-07-19 11:54:46 +00:00
new Notice ( this . t ( "notice_hotkeys_failed" ) , 6000 ) ;
2026-07-14 08:20:29 +00:00
}
} )
) ;
// ---- 使用说明 ----
const tip = containerEl . createEl ( "div" , { cls : "qcb-tip" } ) ;
tip . innerHTML =
2026-07-19 11:54:46 +00:00
"<b>" + this . t ( "tip_title" ) + "</b><br>" +
this . t ( "tip_1" ) + "<br>" +
this . t ( "tip_2" ) + "<br>" +
this . t ( "tip_3" ) + "<br>" +
this . t ( "tip_4" ) + "<br>" +
this . t ( "tip_5" ) ;
// ---- 重置(保留语言选择)----
containerEl . createEl ( "hr" , { cls : "qcb-divider" } ) ;
new Setting ( containerEl )
. setName ( this . t ( "setting_reset" ) )
. setDesc ( this . t ( "setting_reset_desc" ) )
. addButton ( ( btn ) =>
btn
. setButtonText ( this . t ( "btn_reset" ) )
. setWarning ( )
. onClick ( async ( ) => {
const savedLang = this . plugin . settings . language ;
this . plugin . settings = Object . assign ( { } , DEFAULT _SETTINGS ) ;
this . plugin . settings . language = savedLang ;
await this . plugin . saveSettings ( ) ;
// 重新注册命令(命令名跟随语言)
this . plugin . registerCommands ( ) ;
this . display ( ) ;
new Notice ( this . t ( "notice_reset" ) , 2000 ) ;
} )
) ;
2026-07-14 08:20:29 +00:00
}
// 创建单个快捷键配置项:语言选择 + 快捷键输入 + 冲突检测 + 删除
createHotkeyConfigItem ( index ) {
const plugin = this . plugin ;
const config = plugin . settings . hotkeyConfigs [ index ] ;
const fullId = ` ${ PLUGIN _ID } :insert-codeblock- ${ config . lang } ` ;
const setting = new Setting ( this . containerEl ) ;
// 语言选择 dropdown
const select = setting . controlEl . createEl ( "select" , { cls : "qcb-lang-select" } ) ;
const langs = plugin . getLanguages ( ) ;
// 确保当前 lang 在选项里(即使不在列表中)
if ( ! langs . includes ( config . lang ) ) langs . unshift ( config . lang ) ;
for ( const lang of langs ) {
const option = select . createEl ( "option" , { value : lang , text : lang } ) ;
if ( lang === config . lang ) option . selected = true ;
}
// 快捷键输入框
const inputEl = setting . controlEl . createEl ( "input" , {
type : "text" ,
cls : "qcb-hotkey-input" ,
2026-07-19 11:54:46 +00:00
attr : { readonly : true , placeholder : this . t ( "hotkey_placeholder" ) } ,
2026-07-14 08:20:29 +00:00
} ) ;
inputEl . value = formatHotkey ( config . hotkey ) ;
// 冲突提示
const warningEl = setting . descEl . createEl ( "div" , { cls : "qcb-warning" } ) ;
2026-07-19 11:54:46 +00:00
const updateWarning = ( hotkey , lang ) => {
2026-07-14 08:20:29 +00:00
warningEl . empty ( ) ;
warningEl . removeClass ( "has-conflict" ) ;
if ( ! hotkey ) return ;
2026-07-19 11:54:46 +00:00
const currentFullId = ` ${ PLUGIN _ID } :insert-codeblock- ${ lang } ` ;
const conflict = findConflict ( this . app , currentFullId , hotkey ) ;
2026-07-14 08:20:29 +00:00
if ( conflict ) {
warningEl . addClass ( "has-conflict" ) ;
2026-07-19 11:54:46 +00:00
warningEl . createEl ( "span" , { text : this . t ( "conflict_label" ) , cls : "qcb-conflict-label" } ) ;
2026-07-14 08:20:29 +00:00
warningEl . createEl ( "span" , { text : conflict . commandName , cls : "qcb-conflict-name" } ) ;
warningEl . createEl ( "span" , { text : " " } ) ;
const link = warningEl . createEl ( "a" , {
2026-07-19 11:54:46 +00:00
text : this . t ( "conflict_link" ) ,
2026-07-14 08:20:29 +00:00
cls : "qcb-conflict-link" ,
attr : { href : "#" } ,
} ) ;
link . addEventListener ( "click" , ( ev ) => {
ev . preventDefault ( ) ;
openHotkeysSettings ( this . app ) ;
2026-07-19 11:54:46 +00:00
new Notice ( this . t ( "notice_search_conflict" , { name : conflict . commandName } ) , 8000 ) ;
2026-07-14 08:20:29 +00:00
} ) ;
} else {
2026-07-19 11:54:46 +00:00
warningEl . createEl ( "span" , { text : this . t ( "no_conflict" ) , cls : "qcb-ok" } ) ;
2026-07-14 08:20:29 +00:00
}
} ;
2026-07-19 11:54:46 +00:00
updateWarning ( config . hotkey , config . lang ) ;
2026-07-14 08:20:29 +00:00
// 语言变更:清除旧绑定,绑定到新语言
select . addEventListener ( "change" , async ( ) => {
// 清除旧语言的快捷键
if ( config . hotkey ) {
try {
this . app . hotkeyManager . removeHotkeys ( ` ${ PLUGIN _ID } :insert-codeblock- ${ config . lang } ` ) ;
} catch ( e ) { }
}
config . lang = select . value ;
// 绑定到新语言
if ( config . hotkey ) {
const newFullId = ` ${ PLUGIN _ID } :insert-codeblock- ${ config . lang } ` ;
try {
this . app . hotkeyManager . setHotkeys ( newFullId , [ config . hotkey ] ) ;
} catch ( e ) { }
}
await plugin . saveSettings ( ) ;
2026-07-19 11:54:46 +00:00
// 更新冲突检测
updateWarning ( config . hotkey , config . lang ) ;
2026-07-14 08:20:29 +00:00
} ) ;
// 录制状态
inputEl . addEventListener ( "focus" , ( ) => inputEl . addClass ( "recording" ) ) ;
inputEl . addEventListener ( "blur" , ( ) => inputEl . removeClass ( "recording" ) ) ;
// 捕获按键
inputEl . addEventListener ( "keydown" , async ( ev ) => {
ev . preventDefault ( ) ;
ev . stopPropagation ( ) ;
const key = ev . key ;
// 清除
if ( key === "Backspace" || key === "Escape" || key === "Delete" ) {
if ( config . hotkey ) {
try {
this . app . hotkeyManager . removeHotkeys ( ` ${ PLUGIN _ID } :insert-codeblock- ${ config . lang } ` ) ;
} catch ( e ) { }
}
config . hotkey = null ;
inputEl . value = "" ;
await plugin . saveSettings ( ) ;
2026-07-19 11:54:46 +00:00
updateWarning ( null , config . lang ) ;
new Notice ( this . t ( "notice_cleared" , { lang : config . lang } ) , 2000 ) ;
2026-07-14 08:20:29 +00:00
return ;
}
if ( [ "Control" , "Alt" , "Shift" , "Meta" , "Tab" ] . includes ( key ) ) return ;
const modifiers = [ ] ;
if ( ev . ctrlKey ) modifiers . push ( "Mod" ) ;
if ( ev . altKey ) modifiers . push ( "Alt" ) ;
if ( ev . shiftKey ) modifiers . push ( "Shift" ) ;
if ( ev . metaKey ) modifiers . push ( "Meta" ) ;
let displayKey = key ;
if ( key . length === 1 ) displayKey = key . toUpperCase ( ) ;
const hotkey = { modifiers , key : displayKey } ;
config . hotkey = hotkey ;
inputEl . value = formatHotkey ( hotkey ) ;
// 绑定
const bindId = ` ${ PLUGIN _ID } :insert-codeblock- ${ config . lang } ` ;
try {
this . app . hotkeyManager . setHotkeys ( bindId , [ hotkey ] ) ;
} catch ( e ) {
console . error ( ` [quick-codeblock] 设置 ${ config . lang } 快捷键失败: ` , e ) ;
2026-07-19 11:54:46 +00:00
new Notice ( this . t ( "notice_set_failed" , { msg : e . message } ) , 5000 ) ;
2026-07-14 08:20:29 +00:00
return ;
}
await plugin . saveSettings ( ) ;
2026-07-19 11:54:46 +00:00
updateWarning ( hotkey , config . lang ) ;
2026-07-14 08:20:29 +00:00
const conflict = findConflict ( this . app , bindId , hotkey ) ;
if ( conflict ) {
new Notice (
2026-07-19 11:54:46 +00:00
this . t ( "notice_conflict" , { hotkey : formatHotkey ( hotkey ) , name : conflict . commandName } ) ,
2026-07-14 08:20:29 +00:00
8000
) ;
} else {
2026-07-19 11:54:46 +00:00
new Notice ( this . t ( "notice_set" , { lang : config . lang , hotkey : formatHotkey ( hotkey ) } ) , 2000 ) ;
2026-07-14 08:20:29 +00:00
}
} ) ;
// 删除按钮
setting . addExtraButton ( ( btn ) =>
btn
. setIcon ( "trash" )
2026-07-19 11:54:46 +00:00
. setTooltip ( this . t ( "btn_delete_config" ) )
2026-07-14 08:20:29 +00:00
. onClick ( async ( ) => {
if ( config . hotkey ) {
try {
this . app . hotkeyManager . removeHotkeys ( ` ${ PLUGIN _ID } :insert-codeblock- ${ config . lang } ` ) ;
} catch ( e ) { }
}
plugin . settings . hotkeyConfigs . splice ( index , 1 ) ;
await plugin . saveSettings ( ) ;
this . display ( ) ;
} )
) ;
}
}
module . exports = QuickCodeBlockPlugin ;