!function () { // 暗黑模式 if (utools.isDarkColors()) { !$('#darkmode').length && $('head').append(` `) } else { $('#darkmode').length && $('#darkmode, #darkswal').remove() } // 禁用危险函数 let utoolsFull = utools if (!isDev()) utools = getuToolsLite() // 数据库函数封装 let getDB = id => { var db = utoolsFull.db.get(id), dbData = db ? db.data : {}; return dbData; } let putDB = (key, value, id) => { var db = utoolsFull.db.get(id); if (db) { var rev = db._rev var data = db.data data[key] = value; utoolsFull.db.put({ _id: id, data: data, _rev: rev }); } else { var data = {}; data[key] = value; utoolsFull.db.put({ _id: id, data: data }); } } // 进入插件 utools.onPluginEnter(async ({ code, type, payload }) => { if (isRunningAtFirstTime()) { showChangeLog() importDefaultCommands() oldVersionFix() } var handleEnter utools.onPluginOut(() => { // 暂存 codeRunner 的内容 if (code == "code") { var cmd = window.editor.getValue(); var program = $('#program').val(), scptarg = $('#scptarg').val(), customoptions; if (program == 'custom') customoptions = { custombin: $('#custombin').val(), customarg: $('#customarg').val(), customext: $('#customext').val(), customcodec: $('#customcodec').val() } putDB('history', { cmd: cmd, program: program, scptarg: scptarg, customoptions: customoptions }, 'codeHistory') } // 初始化 $("#options, #out").empty() $('body').children(':not(#wrapper)').remove() $('body').css({overflow: 'hidden'}) if (handleEnter) document.removeEventListener('keydown', handleEnter) }) // 配置页面 if (code == 'options') { utools.setExpendHeight(600); // $("#options").show(); showOptions(); } else if (code == 'code') { var file = "" // utools.setExpendHeight(600); if (type == 'files') file = payload[0].path showCodeEditor(file) } else { // console.log(new Date().getTime() - window.startTime); $('body').css({overflow: 'auto'}) utools.setExpendHeight(0); $("#options").hide(); var db = getDB('customFts')[code], cmd = db.cmd; if (db.program == "custom") { option = db.customOptions; } else if(db.program == "quickcommand"){ option = { mode: "quickcommand", payload: payload }; }else{ option = programs[db.program]; } option.scptarg = db.scptarg cmd = special(cmd); // 正则 if (type == 'regex') cmd = cmd.replace(/\{\{input\}\}/mg, payload); // 文件 if (type == 'files' && cmd.includes('{{MatchedFiles')) { let MatchedFiles = payload let Matched = cmd.match(/\{\{MatchedFiles(\[\d+\]){0,1}(\.\w{1,11}){0,1}\}\}/g) Matched && Matched.forEach(m => { repl = eval(m.slice(2, -2)) typeof repl == 'object' && (repl = JSON.stringify(repl)) cmd = cmd.replace(m, repl) }) } // 窗口 var repl if (type == 'window') { // 获取选中的文件 if (cmd.includes('{{SelectFile}}')) { repl = await getSelectFile(payload.id); cmd = cmd.replace(/\{\{SelectFile\}\}/mg, repl) } // 获取资源管理器或访达当前目录 if (cmd.includes('{{pwd}}')) { repl = getCurrentFolderPathFix(); cmd = cmd.replace(/\{\{pwd\}\}/mg, repl) } // 获取窗口信息 if (cmd.includes('{{WindowInfo')) { let WindowInfo = payload let Matched = cmd.match(/\{\{WindowInfo(\.\w{1,7}){0,1}\}\}/g) Matched && Matched.forEach(m => { repl = eval(m.slice(2, -2)) typeof repl == 'object' && (repl = JSON.stringify(repl)) cmd = cmd.replace(m, repl) }) } } // 无输出的批处理 // if (db.output == 'ignore' && option.ext == 'bat') option.bin = 'explorer'; if (db.hasSubInput) { // 启动子命令输入 // 清空输出 // $("#out").empty(); var regex = new RegExp(String.raw`\{\{subinput(:.+?){0,1}\}\}`) var matched = cmd.match(regex) var placeholder = matched[1] || ':请输入' var subinput = ''; var setSubInput = () => { utools.setSubInput(({text}) => { subinput = text; }, placeholder.slice(1)); } handleEnter = (event) => { if (event.keyCode == 13) { $("#out").append(`

>> ${new Date()}

`); var execmd = cmd.replace(matched[0], subinput); runQuickCommand(execmd, option, db.output, true); } }; setSubInput(); document.addEventListener('keydown', handleEnter); } else { runQuickCommand(cmd, option, db.output, false); } } }); let runQuickCommand = (cmd, option, output, autoScroll = false, autoHeight = true) => { // 不需要输出的,提前关闭窗口 if (['ignore', 'clip', 'send', 'notice', 'terminal'].indexOf(output) !== -1) { utools.hideMainWindow(); setTimeout(() => { utools.outPlugin(); }, 500); } var outputOpts = { type: output, autoScroll: autoScroll, autoHeight: autoHeight } if (option.mode) { // 内置环境 runCodeInVm(cmd, (stdout, stderr) => { if (cmd.includes("utools.setExpendHeight")) outputOpts.autoHeight = false switchQuickCommandResult(stdout, stderr, outputOpts) }, option.payload) } else { var terminal = output == 'terminal' ? true : false outputOpts.scriptPath = getQuickCommandScriptFile(option.ext) // 执行脚本 runCodeFile(cmd, option, terminal, (stdout, stderr) => { switchQuickCommandResult(stdout, stderr, outputOpts) }) } } let switchQuickCommandResult = (stdout, stderr, outputOpts) => { var output = outputOpts.type, autoScroll = outputOpts.autoScroll, autoHeight = outputOpts.autoHeight; var outputAutoFix = (autoScroll, autoHeight) => { var outputHeight = $("#out").outerHeight() if (outputHeight > 600) outputHeight = 600 if (autoHeight && $('#options').is(':hidden')) utools.setExpendHeight(outputHeight); if (outputHeight == 600 && autoScroll) $(document).scrollTop($(document).height()); } if (stderr) { $("#out").addClass('error') // 报错 if (output == 'text' || output == 'html') { $("#out").append(stderr) outputAutoFix(autoScroll, autoHeight) } else { var index = utools.showMessageBox({ type: 'error', title: '啊嘞?!', message: stderr, buttons: outputOpts.scriptPath ? ['转至脚本目录', '退出'] : ['退出'] }) if (outputOpts.scriptPath && index == 0) { locate(outputOpts.scriptPath ); } copyTo(stderr); message("已复制报错信息"); utools.outPlugin(); } } else if (stdout) { $("#out").removeClass("error") // 有输出 switch (output) { case "text": $("#out").append(htmlEncode(stdout, true)) outputAutoFix(autoScroll, autoHeight) break; case "html": $("#out").append(stdout) outputAutoFix(autoScroll, autoHeight) break; case "clip": copyTo(stdout) break; case "send": send(stdout) break; case "notice": // 发送系统通知 message(stdout) break; case "ignore": break; default: break; } // } else { // // 无输出 // utools.outPlugin() } } // 替换上个版本弃用的功能 let oldVersionFix = () => { utools.showNotification('第一次更新会对老版本命令做兼容处理,如插件显示空白请稍候', 'warning') var customFts = getDB('customFts'); Object.keys(customFts).forEach((x, i) => { // 旧版的 program if (customFts[x].program == 'simulation') customFts[x].program = 'quickcommand'; // 旧版的 sleep if (customFts[x].cmd.includes('await sleep')) customFts[x].cmd = customFts[x].cmd.replace(/await sleep/g, 'quickcommand.sleep') // 不规范的 code let code = customFts[x].features.code if (!/^(window|key|regex|files|default)_/.test(code)) { console.log(code); utoolsFull.removeFeature(code) let uid = Number(Math.random().toString().substr(3, 3) + (Date.now() + i * 10000)).toString(36) let type = customFts[x].features.cmds[0].type || 'key' let newCode = type + '_' + uid let newFts = customFts[x] newFts.features.code = newCode delete customFts[x] customFts[newCode] = newFts } putDB(x, customFts[x], 'customFts'); }) } let showChangeLog = () => { putDB('version', pluginInfo().version, 'plugin') utools.createBrowserWindow('./helps/CHANGELOG.html', {width: 1280, height: 920}) } let isRunningAtFirstTime = () => { var historyVersion = getDB('plugin').version if (!historyVersion) return 'init' if (pluginInfo().version > historyVersion) return 'update' return false } // 导入默认命令 let importDefaultCommands = () => { let customFts = getDB('customFts') let qc = Object.keys(customFts) let defaultCommands = getDefaultCommands() Object.keys(defaultCommands).forEach(d => { if (!qc.includes(d)) importCommand(defaultCommands[d]) }) } // 是否含有 quickcommand 键值 let isJsonQc = obj => { var keys = ["features", "program", "cmd", "output"] if (keys.filter(x => typeof obj[x] == 'undefined').length) return false return true } // 判断是否为可导入的快捷命令 let quickCommandParser = json => { try { var qc = JSON.parse(json) } catch (error) { return false } if (isJsonQc(qc)) return { single: true, qc: qc } else if (!Object.values(qc).filter(q => !isJsonQc(q)).length) return { single: false, qc: qc } else return false } // 导入 let importCommand = file => { var pushData, clipboardText = clipboardReadText() if (!file) pushData = quickCommandParser(clipboardText) if (!pushData) { var options = file ? { type: 'file', argvs: file } : { type: 'dialog', argvs: { filters: [{ name: 'json', extensions: ['json'] }] } } options.readfile = true var fileinfo = getFileInfo(options) if (!fileinfo) return pushData = quickCommandParser(fileinfo.data) if (!pushData) return false } // 单个命令导入 if (pushData.single) { var code = pushData.qc.features.code; putDB(code, pushData.qc, 'customFts'); // 多个命令导入 } else { for (var code of Object.keys(pushData.qc)) { putDB(code, pushData.qc[code], 'customFts'); } } return true } // 全部导出 let exportAll = () => { let jsonQc = getDB('customFts'), options = { title: '选择保存位置', defaultPath: 'quickCommand', filters: [ { name: 'json', extensions: ['json'] }, ] }; if (!isDev()) Object.keys(jsonQc).filter(k => jsonQc[k].tags && jsonQc[k].tags.includes('默认')).map(k => delete jsonQc[k]) window.saveFile(options, JSON.stringify(jsonQc)); } // 清空 let clearAll = () => { quickcommand.showConfirmBox('将会清空所有自定义命令,请确认!').then(x => { if (!x) return utoolsFull.db.remove('customFts'); importDefaultCommands(); clearAllFeatures(); showOptions(); }) } // 环境 const programs = { shell: { bin: 'bash', argv: '', ext: 'sh', color: '#89e051' }, applescript: { bin: 'osascript', argv: '', ext: 'scpt', color: '#101F1F' }, cmd: { bin: '', argv: '', ext: 'bat', codec: 'gbk', color: '#C1F12E' }, powershell: { bin: 'powershell', argv: '-NoProfile -File', ext: 'ps1', codec: utools.isWindows() ? 'gbk' : '', color: '#012456' }, python: { bin: 'python', argv: '-u', ext: 'py', codec: utools.isWindows() ? 'gbk' : '', color: '#3572A5' }, javascript: { bin: 'node', argv: '', ext: 'js', color: '#f1e05a' }, ruby: { bin: 'ruby', argv: '', ext: 'rb', color: '#701516' }, php: { bin: 'php', argv: '', ext: 'php', color: '#4F5D95' }, c: { bin: 'gcc', argv: '-o', ext: 'c', codec: utools.isWindows() ? 'gbk' : '', color: '#555555' }, csharp: { bin: 'C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe', argv: '/Nologo', ext: 'cs', codec: 'gbk', color: '#178600' }, lua: { bin: 'lua', argv: '', ext: 'lua', color: '#000080' }, perl: { bin: 'perl', argv: '', ext: 'pl', color: '#0298c3' }, custom: { bin: '', argv: '', ext: '', codec: '', color: '#438eff' } } // 显示设置界面 let showOptions = (tag = "默认") => { $("#options").empty().fadeIn(); var currentFts = utools.getFeatures(), customFts = getDB('customFts'); var allTags = ["默认"] var featureList = `
`; Object.values(customFts).some(fts => { var features = fts.features; if (fts.tags) { fts.tags.map(t => !allTags.includes(t) && allTags.push(t)) } if (tag == "未分类") { if (fts.tags && fts.tags.length) return false } else { if (!fts.tags) return false if (!fts.tags.includes(tag)) return false } var cmds = '', rules = features.cmds[0].match; if (features.cmds[0].type == 'regex') { if (rules.length > 14) rules = rules.slice(0, 14) + '...'; cmds = `
正则
${rules}
`; } else if (features.cmds[0].type == 'window') { cmds += `
窗口
` if (!rules) { cmds += `所有窗口` } else if (rules.title || rules.class) { cmds += `${JSON.stringify(rules).slice(0, 14) + '...'}`; } else if (rules.app) { rules = rules.app.join(",") if(rules.length > 14) rules = rules.slice(0, 14) + '...'; rules.split(',').forEach(r => { cmds += `${r}`; }); } cmds += `
` } else if (features.cmds[0].type == 'files') { if (rules.length > 14) rules = rules.slice(0, 14) + '...'; cmds = `
文件
${rules}
`; } else { rules = features.cmds.join(",") if(rules.length > 14) rules = rules.slice(0, 14) + '...'; cmds += `
关键字
` rules.split(',').forEach(r => { cmds += `${r}`; }); cmds += `
` } var isChecked = ''; for(var c of currentFts){ if (c.code == features.code) { isChecked = 'checked'; break; } } var platformIcons if (features.platform) platformIcons = features.platform.map(x => ``) else platformIcons = ['', '', ''] featureList += `` }) featureList += `
${features.explain}
${fts.program} | ${platformIcons.join('')}
${cmds} ${(tag == "默认" && !isDev()) ? "" : ` `}
` var sidebar = ` ` var footer = `
新建命令
导入命令
分享中心
查看帮助
全部导出
启用本页
清空数据
禁用本页
` $("#options").append(sidebar + featureList + footer) } // 显示新建命令界面 let showCustomize = (readonly = false) => { $("#customize").remove(); let options = `` let allTags = [] $('.sidebar li').each(function () { let val = $(this).text() if (val != "默认" && val != "未分类") allTags.push(``) }) let customWindow = `

匹 配 关键字

说 明

环 境 标 签

变 量 输 出

脚 本 ﹢动作 ﹢按键 ?文档 格式化

${(readonly && !isDev()) ? '' : ''}

` $("#options").append(customWindow) $("#icon").attr('src', 'logo/quickcommand.png'); getSpecialVars() createEditor() createProgramSelect2('40%') createTypeSelect2('40%') var singleSelectOpt = { width: '40%', minimumResultsForSearch: Infinity, dropdownParent: $("#customize") } $('#output').select2(singleSelectOpt); singleSelectOpt.placeholder = "插入特殊变量" $('#vars').select2(singleSelectOpt); $('#tags').select2({ width: '40%', placeholder: "选择或添加标签, 最多3个", tags: true, allowClear: true, tokenSeparators: [',', ' '], maximumSelectionLength: 3, dropdownParent: $("#customize") }).on("select2:unselecting", e => { (e.params.args.data.text == "默认") && !isDev() && e.preventDefault(); }).on("select2:selecting", e => { (e.params.args.data.text == "默认" || e.params.args.data.text == "未分类") && !isDev() && e.preventDefault(); }) } let getSelect2Option = (data, width, dropdownAutoWidth = false) => { var options = { data: data, minimumResultsForSearch: Infinity, dropdownParent: $("#customize"), dropdownAutoWidth: dropdownAutoWidth, escapeMarkup: markup => markup, templateSelection: data => data.text, templateResult: data => data.html } if (width) options.width = width return options } let getTypeSheet = () => { return [ { id: "key", text: "关键字", html: "关键字
在主输入框输入对应关键字进入插件,最通用的一种模式,关键字可以设置多个
" }, { id: "regex", text: "正则/划词", html: "正则/划词
正则匹配主输入框文本或唤出语音面板时选中的文本,可以获取输入框文本或选中文本作为变量
" }, { id: "window", text: "窗口/进程", html: "窗口/进程
匹配呼出uTools前或唤出语音面板时的活动窗口,可以获取窗口的信息或文件夹路径作为变量
" }, { id: "files", text: "复制/选中文件", html: "复制/选中文件
匹配拖入主输入框的文件或唤出语音面板时选中的文件,可以获取复制及选中的文件信息作为变量
" } ] } let createTypeSelect2 = (width = false) => { var data = getTypeSheet() $('#type').select2(getSelect2Option(data, width)); } let createProgramSelect2 = (width, dropdownAutoWidth = false) => { var programStyled = p => `${p}` var data = [{ id: "quickcommand", text: 'quickcommand', html: programStyled('quickcommand') }] data = data.concat(Object.keys(programs).map(x => { return { id: x, text: x, html: programStyled(x) } })) $('#program').select2(getSelect2Option(data, width, dropdownAutoWidth)); } let createEditor = () => { window.editor = CodeMirror.fromTextArea(document.getElementById("cmd"), { lineNumbers: true, matchBrackets: true, // lineWrapping: true, autoCloseBrackets: true, styleActiveLine: true, keyMap: "sublime", theme: utools.isDarkColors() ? 'material-darker' : "mdn-like", extraKeys: { "Alt-Enter": () => { $('.CodeMirror').hasClass('CodeMirror-fullscreen') && $('.CodeMirror').removeClass('CodeMirror-fullscreen') || $('.CodeMirror').addClass('CodeMirror-fullscreen') }, "Ctrl-B": () => { runCurrentCommand() }, "Ctrl-S": () => { SaveCurrentCommand() }, "Ctrl-Q": () => { quitCurrentCommand() }, "Shift-Alt-F": () => { beautifyCode() }, "Alt-Up": "swapLineUp", "Alt-Down": "swapLineDown", "Shift-Alt-Down": "duplicateLine" } }); window.editor.on("change", showHint); window.editor.setOption("mode", 'javascript'); } showHint = () => { editor.showHint({ completeSingle: false }); } let beautifyCode = () => { if ($("#customize").is(":parent")) { var cmd = window.editor.getValue() switch ($("#program").val()) { case "quickcommand": case "javascript": window.editor.setValue(js_beautify(cmd, { brace_style: "collapse,preserve-inline" })) break; case "python": py_beautify(cmd, data => { window.editor.setValue(data) }) break; default: quickcommand.showMessageBox('暂不支持该语言的格式化', 'error') break; } } } // 获取特殊变量 let getSpecialVars = () => { var specialVars = [] $("#vars option").each(i => { var selector = $("#vars option").eq(i) if (!selector.prop('disabled')) specialVars.push(selector.val()) }) localStorage['specialVars'] = specialVars } let typeCheck = () => { var type = $("#type").val(); switch (type) { case 'key': $("#ruleWord").html("关键字"); $(".var.regex, .var.window, .var.files").prop("disabled", true) $("#rule").prop("placeholder", '多个关键字用逗号隔开'); break; case 'regex': $("#ruleWord").html("正 则"); $(".var.window, .var.files").prop("disabled", true) $(".var.regex").prop("disabled", false) $("#rule").prop("placeholder", '匹配文本的正则,如 /.*?\\.exe$/i'); break; case 'files': $("#ruleWord").html("正 则"); $(".var.regex, .var.window").prop("disabled", true) $(".var.files").prop("disabled", false) $("#rule").prop("placeholder", '匹配文件的正则,如 /.*?\\.exe$/i'); break; case 'window': $("#ruleWord").html("进 程"); $(".var.regex, .var.files").prop("disabled", true) $(".var.window").prop("disabled", false) $("#rule").prop("placeholder", '多个窗口进程逗号隔开,留空匹配所有窗口'); break; default: break; } getSpecialVars() } let clearAllFeatures = () => { for (var fts of utools.getFeatures()) { utoolsFull.removeFeature(fts.code) } } let hasCustomIcon = () => { var src = $("#icon").attr('src'); var iconame = $("#iconame").val(); return /data:image\/png;base64,/.test(src) || iconame } let programCheck = () => { let mode = $('#program').val(); $('.customscript').hide(); $('.quickactions').hide(); $('#scptarg').show(); $('#showInTerm').prop("disabled", false); if (!hasCustomIcon()) $("#icon").attr('src', `logo/${mode}.png`); switch (mode) { case 'custom': $('.customscript').show(); var customext = $('#customext').val() customext && (mode = highlightIfKnown(customext)) break; case 'quickcommand': $('.quickactions').show(); $('#scptarg').hide(); $('#showInTerm').prop("disabled", true); mode = 'javascript'; break; case 'csharp': case 'c': mode = 'text/x-' + mode break; case 'python': getPythonMods() break; case 'cmd': getCmdCommand() break; case 'shell': getShellCommand() break; default: break; } window.editor.setOption("mode", mode); } // 合规性校验 let cmdCheck = (type, cmd) => { var blacklist switch (type) { case 'key': blacklist = cmd.match(/{{input}}|{{SelectFile}}|{{pwd}}|{{WindowInfo}}|{{MatchedFiles}}/g) break; case 'regex': blacklist = cmd.match(/{{SelectFile}}|{{WindowInfo}}|{{pwd}}|{{MatchedFiles}}/g) if (/^(|\/)\.[*+](|\/)$/.test($('#rule').val())) return quickcommand.showMessageBox('正则匹配 .* 和 .+ 已被uTools禁用!', 'error') break; case 'window': blacklist = cmd.match(/{{input}}|{{MatchedFiles}}/g) break; case 'files': blacklist = cmd.match(/{{input}}|{{SelectFile}}|{{pwd}}|{{WindowInfo}}/g) break; default: break; } if (blacklist) { quickcommand.showMessageBox(`当前模式无法使用${Array.from(new Set(blacklist)).join("、")}`, 'error') return false } else { return true } } // 开关 $("#options").on('change', 'input[type=checkbox]', function () { var customFts = getDB('customFts'), code = $(this).parents('tr').attr('id') if (!utoolsFull.removeFeature(code)) { utoolsFull.setFeature(customFts[code].features); } }); // 底部功能按钮 $("#options").on('click', '.footBtn', function () { switch ($(this).attr('id')) { case 'viewHelps': utools.createBrowserWindow('./helps/HELP.html', {width: 1280, height: 920}); break; case 'getShares': getSharedQCFromYuQue(); break; case 'add': showCustomize(); $("#customize").animate({ top: '0px' }); break; case 'import': var success = importCommand() if (success) showOptions() || quickcommand.showMessageBox("导入成功") else if (success == false) quickcommand.showMessageBox("导入失败,格式错误", "error") break; case 'enableAll': $(".checked-switch:not(:checked)").click(); break; case 'disableAll': $(".checked-switch:checked").click(); break; case 'exportAll': exportAll(); break; case 'clear': clearAll(); break; } }) let editCurrentCommand = data => { let code = data.features.code let cmds = data.features.cmds[0] let platform = data.features.platform let readonly = false let extraInfo = { authorName: data.authorName, authorId: data.authorId, fromShare: data.fromShare } if (data.tags && data.tags.includes("默认")) readonly = true showCustomize(readonly); $('#customize').data('extraInfo', extraInfo) data.tags && $('#tags').val(data.tags).trigger('change') platform && ["win32", "darwin", "linux"].map(x => (!platform.includes(x) && $(`#${x}`).addClass('disabled'))) $('#type').val(cmds.type).trigger("change") if (cmds.type == 'regex' || cmds.type == 'files') { $('#rule').val(cmds.match); } else if (cmds.type == 'window') { if (!cmds.match) $('#rule').val(''); else if (cmds.match.title || cmds.match.class) $('#rule').val(JSON.stringify(cmds.match)); else $('#rule').val(cmds.match.app); } else { $('#type').val('key').trigger("change") $('#rule').val(data.features.cmds.toString()); } $('#code').val(code); $('#program').val(data.program).trigger("change"); $('#output').val(data.output).trigger("change"); $('#desc').val(data.features.explain); $('#scptarg').val(data.scptarg); $("#icon").attr('src', data.features.icon); let mode = data.program; if (mode == 'custom') { $('#custombin').show().val(data.customOptions.bin); $('#customarg').show().val(data.customOptions.argv); $('#customext').show().val(data.customOptions.ext); $('#customcodec').show().val(data.customOptions.codec); } typeCheck(); programCheck(); // 分段载入,保障动画流畅 window.editor.setValue(data.cmd.slice(0, 2000)); $("#customize").animate({ top: '0px' }, () => { window.editor.replaceRange(data.cmd.slice(2000), {line: Infinity}); }); } // 编辑 $("#options").on('click', '.editBtn', function () { let code = $(this).parents('tr').attr('id') let data = getDB("customFts")[code] editCurrentCommand(data) }) // 添加模拟按键 $("#options").on('click', '#addKey', function () { $("#addKey").text("▶ 录制中").addClass('record') quickcommand.showMessageBox('开始录制按键,可连续录制', 'info') Mousetrap.record(sequence => { sequence.forEach(s => { var keys = s if (s.includes('+') && s.length > 1) keys = s.split('+').reverse().map(x=>x.trim().replace('meta', 'command')).join(`", "`) window.editor.replaceSelection(`keyTap("${keys}")\n`) }) $("#addKey").text("﹢按键").removeClass('record') }); }) // quickCommand的帮助 $("#options").on('click', '#showHelp', function () { utools.createBrowserWindow('./helps/quickcommand.html', {width: 1280, height: 920}) }) // 添加动作 $("#options").on('click', '#addAction', function () { var html = ` 加引号 ` Swal.fire({ title: "预设动作", onBeforeOpen: () => { $('#actionType').change(function () { $('#actionArgs').attr('placeholder', $(this).find(`[value=${$(this).val().replace('.', '\\.')}]`).attr('args')) }) }, html: html, showCancelButton: true, preConfirm: () => { var actionType = $("#actionType").val() var actionArgs = $("#actionArgs").val() if ($("#isString").is(':checked')) actionArgs = "String.raw\`" + actionArgs + "\`" var action = `${actionType}(${actionArgs})` if (actionType == 'utools.ubrowser.goto') action += `.run()` window.editor.replaceSelection(`${action}\n`) } }) }) let setYuQueToken = async () => { let yuQueToken = await quickcommand.showInputBox(["请输入 Token"]) if (!yuQueToken) return yuQueToken = yuQueToken[0] yuQueClient.defaults.headers['X-Auth-Token'] = yuQueToken try { let res = await yuQueClient('user') let authorId = res.data.data.account_id let authorName = res.data.data.name putDB('yuQueToken', yuQueToken, 'extraInfo') putDB('authorName', authorName, 'extraInfo') putDB('authorId', authorId, 'extraInfo') quickcommand.showMessageBox("设置成功~") } catch (e) { quickcommand.showMessageBox('Token 校验失败', "error") } } let createShareMenu = jsonQc => { let menu = ['复制到剪贴板', '导出到文件', '', '设置 Token'] let extraInfo = getDB('extraInfo') if (jsonQc.authorId) { if (jsonQc.authorId == extraInfo.authorId) menu[2] = '更新分享' else if (jsonQc.fromShare) menu[2] = '评论' else menu[2] = '分享自:' + jsonQc.authorName } else { if (extraInfo.yuQueToken) menu[2] = '分享命令' else menu[2] = '我要分享' } return menu } // 导出 $("#options").on('click', '.exportBtn', async function () { var code = $(this).parents('tr').attr('id') var jsonQc = getDB('customFts')[code] var stringifyQc = JSON.stringify(jsonQc, null, 4) var choise = await quickcommand.showButtonBox(createShareMenu(jsonQc)) switch (choise.text) { case '复制到剪贴板': utools.copyText(stringifyQc) && quickcommand.showMessageBox('已复制到剪贴板') break; case '导出到文件': window.saveFile({ title: '选择保存位置', defaultPath: `${jsonQc.features.explain}.json`, filters: [ { name: 'json', extensions: ['json'] }, ] }, stringifyQc) break; case '分享命令': case '更新分享': var result = await shareQCToYuQue(jsonQc) result && quickcommand.showMessageBox('分享成功,等待发布后即可在分享中心直接下载') break; case '我要分享': utools.createBrowserWindow('./helps/HELP.html?#分享命令', {width: 1280, height: 920}) break; case '评论': utools.shellOpenExternal(`https://www.yuque.com/fofolee/qcreleases/${code}`) break; case '设置 Token': await setYuQueToken() break; } }) // 一键分享到语雀 let shareQCToYuQue = async jsonQc => { let extraInfo = getDB('extraInfo') if (!extraInfo.yuQueToken) return quickcommand.showMessageBox("请先设置 Token,点击底部「查看帮助」可查看 Token 设置方法", "error") jsonQc.authorId = extraInfo.authorId jsonQc.authorName = extraInfo.authorName let stringifyQc = JSON.stringify(jsonQc, null, 4) console.log(jsonQc); if (stringifyQc.length > 5000000) return quickcommand.showMessageBox('命令大小超过5M无法分享,请检查图标或脚本内容是否过大', "error") let type = jsonQc.features.cmds[0].type || 'key' let tags = jsonQc.tags ? jsonQc.tags.join(' ') : "" let typeDescription = getTypeSheet().filter(x => x.id == type)[0].text let custom_description = { authorName: jsonQc.authorName, program: jsonQc.program, type: typeDescription, platform: jsonQc.features.platform || ['win32', 'darwin', 'linux'], tags: tags } let parameters = { title: jsonQc.features.explain, slug: jsonQc.features.code, public: 1, format: "markdown", body: '```json\n' + stringifyQc + '\n```', custom_description: JSON.stringify(custom_description) } yuQueClient.defaults.headers['X-Auth-Token'] = extraInfo.yuQueToken let res, repo = extraInfo.authorId == 1496740 ? 'qcreleases' : 'qcshares' try { res = await yuQueClient.post(`repos/fofolee/${repo}/docs`, parameters) if (!res.data.data) return quickcommand.showMessageBox("分享失败,不知道为啥", "error") let docId = res.data.data.id res = await yuQueClient.put(`repos/fofolee/${repo}/docs/${docId}`, parameters) if (!res.data.data) return quickcommand.showMessageBox("分享失败,不知道为啥", "error") putDB(jsonQc.features.code, jsonQc, 'customFts'); return jsonQc } catch (error) { return quickcommand.showMessageBox(error, "error") } } let getSharedQCFromYuQue = async () => { $('#options').hide() let extraInfo = getDB('extraInfo') if (extraInfo.yuQueToken) yuQueClient.defaults.headers['X-Auth-Token'] = extraInfo.yuQueToken let res = await yuQueClient('repos/fofolee/qcreleases/docs') let description, platform = window.processPlatform let docs = res.data.data .filter(d => JSON.parse(d.custom_description).platform.includes(platform)) .sort((x, y) => { if (y.updated_at > x.updated_at) return 1 else return -1 }) .map(d => { description = JSON.parse(d.custom_description) return { title: d.title, description: ` ${description.authorName}   ${description.type}   ${description.tags}   ${d.updated_at.split('T')[0]}`, slug: d.slug, icon: `logo/${description.program}.png` } }) let choise = await quickcommand.showSelectList(docs, { optionType: 'json' }) let doc = await yuQueClient(`repos/fofolee/qcreleases/docs/${choise.slug}?raw=1`) let body = doc.data.data.body let stringifyQc = body.match(/```json([\s\S]*)```/)[1] let qc = JSON.parse(stringifyQc) $('#options').show() qc.fromShare = true editCurrentCommand(qc) utools.setExpendHeight(600) } $("#out").on('click', '#importSharedQc', function () { importCommand() ? quickcommand.showMessageBox("导入成功") : quickcommand.showMessageBox("导入失败,格式错误", "error") showOptions() utools.setExpendHeight(600) $('#out').empty() }) // 删除 $("#options").on('click', '.delBtn', function () { quickcommand.showConfirmBox('删除这个快捷命令').then(x => { if (!x) return var code = $(this).parents('tr').attr('id'), db = utoolsFull.db.get("customFts"), data = db.data; delete data[code]; utoolsFull.removeFeature(code); utoolsFull.db.put({ _id: "customFts", data: data, _rev: db._rev }); var currentTag = $('.currentTag').text() if ($('#featureList tr').length == 2) currentTag = "默认" showOptions(currentTag); }) }) // 选择图标 $("#options").on('click', '#icon', function () { var options = { buttonLabel: '选择', filters: [{ name: 'Images', extensions: ['png'] }, ] } var file = getFileInfo({ type: 'dialog', argvs: options, readfile: false }) if (file) { $("#iconame").val(file.name); $("#icon").attr('src', file.path); } }) let SaveCurrentCommand = async () => { if ($('#tags').is(":parent")) { var type = $('#type').val(), code = $("#code").val(), tags = $('#tags').val(), rule = $('#rule').val(), cmd = window.editor.getValue(); if (tags && tags.includes("默认") && !isDev()) return if (type != "window" && !rule) return quickcommand.showMessageBox(`${$('#ruleWord').text().replace(" ", "")} 不能留空!`, 'error') if (!cmdCheck(type, cmd)) return if (!code) { // 生成唯一code var uid = Number(Math.random().toString().substr(3, 3) + Date.now()).toString(36); var code = `${type}_${uid}`; } var output = $('#output').val(), scptarg = $('#scptarg').val(), program = $('#program').val(), desc = $('#desc').val(), iconame = $("#iconame").val(), iconpath = $("#icon").attr('src'), icon, hasSubInput; if (!desc) desc = ' '; // 选择了图标的情况下 if (iconame) { icon = await window.getBase64Ico(iconpath); // 未自定义使用默认 } else { icon = iconpath; } if (type == 'key') { cmds = rule.split(",").map(x => x.trim()) } else if (type == 'regex') { if (!/^\/.*?\/[igm]*$/.test(rule)) { rule = "/" + rule + "/" } cmds = [{ "label": desc, "type": "regex", "match": rule, "minNum": 1 }]; } else if (type == 'window') { var cmdOfWin = { "label": desc, "type": "window" } if (rule) { try { cmdOfWin.match = JSON.parse(rule) } catch (error) { cmdOfWin.match = { "app": rule.split(',') } } } cmds = [cmdOfWin]; } else if (type == 'files') { if (!/^\/.*?\/[igm]*$/.test(rule)) { rule = "/" + rule + "/" } cmds = [{ "label": desc, "type": "files", "match": rule, "minNum": 1 }]; } // 需要子输入框 if (cmd.includes('{{subinput')) { hasSubInput = true; } else { hasSubInput = false; } // platform var platform = [] $('.platform').not('.disabled').each(function () { platform.push($(this).attr('id')) }) // 添加特性 var extraInfo = $('#customize').data('extraInfo') var pushData = { features: { "code": code, "explain": desc, "cmds": cmds, "icon": icon, "platform": platform }, program: program, cmd: cmd, output: output, hasSubInput: hasSubInput, scptarg: scptarg } if (extraInfo) Object.assign(pushData, extraInfo) if (tags) pushData.tags = tags if (program == 'custom') { pushData.customOptions = { "bin": $('#custombin').val(), "argv": $('#customarg').val(), "ext": $('#customext').val(), 'codec': $('#customcodec').val() } } putDB(code, pushData, 'customFts'); $("#customize").animate({ top: '100%' }, () => { // 保存后标签跳转处理 var redirectTag, currentTag = $('.currentTag').text() if (tags.length) { if (pushData.tags.includes(currentTag)) { redirectTag = currentTag } else { redirectTag = pushData.tags[0] } } else { redirectTag = "未分类" } showOptions(redirectTag); location.href = '#' + code $("#customize").empty() let checkSwitch = $(`#${code} .checked-switch`) checkSwitch.click() checkSwitch.is(':checked') || checkSwitch.click() }); } } // 显示运行结果 let showRunResult = (content, raw, success) => { var options, position, showClass, hideClass, maxlength = 100000 if ($("#vars").is(":parent")) { position = 'top' showClass = 'fadeInDownWindow' hideClass = 'fadeOutUpWindow' } else { position = 'bottom' showClass = 'fadeInUpWindow' hideClass = 'fadeOutDownWindow' } var preView = () => { var result = $('#swal2-content').text() result = htmlEncode(result, raw) $(".swal2-content").css("width", "100%") $('#swal2-content').html(`
${result}
`) $('.swal2-popup').addClass('swal2-toast') } var contlength = content.length if (contlength > maxlength) content = content.slice(0, maxlength - 100) + `\n\n...\n${contlength - maxlength - 100} 字省略\n...\n\n` + content.slice(contlength - 100) content += '\n' var outputchannel = $("#swal2-content > pre") if (outputchannel.is(":parent")) { outputchannel.append(htmlEncode(content, raw)) } else { options = { onBeforeOpen: preView, icon: success ? "success" : "error", text: content, position: position, width: 800, showConfirmButton: true, showClass: { popup: showClass }, hideClass: { popup: hideClass } } success ? swalOneByOne(options) : Swal.fire(options) } } let replaceTempInputVals = async cmd => { let tempInputVals = [] let specilaVals = ['input', 'subinput', 'pwd', 'SelectFile', 'WindowInfo', 'MatchedFiles'] specilaVals.forEach(x => { let m = cmd.match(new RegExp('{{' + x + '.*?}}', 'g')) m && m.forEach(y => tempInputVals.includes(y) || tempInputVals.push(y)) }) if (!tempInputVals.length) return cmd let inputs = await quickcommand.showInputBox(tempInputVals, '需要临时为以下变量赋值') tempInputVals.forEach((t, n) => { cmd = cmd.replace(new RegExp(t, 'g'), inputs[n]) }) return cmd } let runCurrentCommand = async () => { if ($("#customize").is(":parent")) { var cmd = window.editor.getValue() cmd = special(cmd) // cmd = await replaceTempInputVals(cmd) var program = $("#program").val() var output = $("#output").val() var terminal = false var raw = true switch (output) { case "html": raw = false break; case "terminal": terminal = true break; case "ignore": utools.hideMainWindow() break; } if (program == "quickcommand") { runCodeInVm(cmd, (stdout, stderr) => { if (stderr) return showRunResult(stderr, raw, false) showRunResult(stdout, raw, true) }); } else { var option = programs[program] if (program == "custom") option = { "bin": $('#custombin').val(), "argv": $('#customarg').val(), "ext": $('#customext').val(), 'codec': $('#customcodec').val() } option.scptarg = $('#scptarg').val() runCodeFile(cmd, option, terminal, (stdout, stderr) => { if (terminal) return if (stderr) return showRunResult(stderr, raw, false) showRunResult(stdout, raw, true) }) } } } let killCurrentCommand = () => { } let quitCurrentCommand = () => { if ($("#customize").is(":parent") && $("#featureList").is(":parent")) { $("#customize").animate({ top: '100%' }); $("#customize").empty() } } let highlightIfKnown = ext => { var lang = Object.keys(programs).filter(p => programs[p].ext == ext) if (lang.length) { if (lang[0] == 'python') getPythonMods() window.editor.setOption("mode", lang[0]) return lang[0] } } let showCodeEditor = file => { var customWindow = `
运 行 格式化 ?文档
` $("#options").html(customWindow) createEditor() $(".CodeMirror").addClass('CodeMirror-coderunner') $("#customize").css({ top: '0px', padding: '0px' }); $("span.customscript > input").css({"height": "30px"}) var db = getDB('codeHistory') createProgramSelect2(140, true) if (file) { var fileinfo = getFileInfo({ type: 'file', argvs: file, readfile: true }) window.editor.setValue(fileinfo.data) var program = Object.keys(programs).filter(x => `.${programs[x].ext}` == fileinfo.ext) if (program) $('#program').val(program[0]).trigger('change') // runCurrentCommand() } else if(db.history){ window.editor.setValue(db.history.cmd) $('#program').val(db.history.program).trigger('change') $('#scptarg').val(db.history.scptarg) var custom = db.history.customoptions if (db.history.program = 'custom' && custom) { $('#custombin').val(custom.custombin) $('#customarg').val(custom.customarg) $('#customext').val(custom.customext) $('#customcodec').val(custom.customcodec) } } programCheck() $("#options").show() } // 输出搜索 let showSearchBox = () => { if ($('#options').is(':hidden') && $('#out').is(":parent")) { $('#out').append(`
`) $('#outputSearch').animate({ opacity: 1, top: '10px', }) document.getElementById('find-next').onclick = () => { utools.findInPage($('#outputSearch > input').val()) } document.getElementById('find-prev').onclick = () => { utools.findInPage($('#outputSearch > input').val(), { forward: false }) } document.getElementById('find-close').onclick = () => { utools.stopFindInPage() $('#outputSearch').animate({ opacity: 0, top: '-30px', }, () => { $('#outputSearch').remove() }) } } } // 切换TAGS $("#options").on('click', '.sidebar li', function () { showOptions($(this).text()); }) // 运行 $("#options").on('click', '.cmdBtn.run, #runCode', function () { runCurrentCommand() }) // 格式化 $("#options").on('click', '#beautifyCode', function () { beautifyCode() }) // 取消 $("#options").on('click', '.cmdBtn.cancel', function () { quitCurrentCommand() }) // 保存 $("#options").on('click', '.cmdBtn.save', function () { SaveCurrentCommand() }) // 语言选项改变时 $("#options").on('change', '#program', function () { programCheck() }) // 变量选项改变时 $("#options").on('change', '#vars', function () { $("#vars").css({'color':'black'}) window.editor.replaceSelection($("#vars").val()); }) $("#options").on('change', '#action', function () { $("#action").css({ 'color': 'black' }) }) // 方式选项改变时 $("#options").on('change', '#type', function () { // resetVars(); typeCheck(); }) $("#options").on('change', '#customext', function () { highlightIfKnown($('#customext').val()) }) // 平台按钮 $("#options").on('click', '.platform', function () { if ($(this).hasClass('disabled')){ $(this).removeClass('disabled') } else { if ($('.disabled').length == 2) quickcommand.showMessageBox('至少保留一个平台', 'error') else $(this).addClass('disabled') } }) Mousetrap.bind('ctrl+s', () => { SaveCurrentCommand() return false }); Mousetrap.bind('ctrl+q', () => { quitCurrentCommand() return false }); Mousetrap.bind('ctrl+b', () => { runCurrentCommand() return false }); Mousetrap.bind('ctrl+f', () => { showSearchBox() return false }); }()