diff --git a/src/assets/index.js b/src/assets/index.js index e6b14db..29c7ba9 100644 --- a/src/assets/index.js +++ b/src/assets/index.js @@ -1,4 +1,5 @@ !function () { + // 暗黑模式 if (utools.isDarkColors()) { !$('#darkmode').length && $('head').append(` @@ -6,7 +7,7 @@ } else { $('#darkmode').length && $('#darkmode, #darkswal').remove() } - + // 进入插件 utools.onPluginEnter(async ({ code, type, payload }) => { if (fofoCommon.isRunningAtFirstTime()) { fofoCommon.showChangeLog() @@ -124,7 +125,6 @@ } }); - let runQuickCommand = (cmd, option, output, autoScroll = false, autoHeight = true) => { // 不需要输出的,提前关闭窗口 if (['ignore', 'clip', 'send', 'notice', 'terminal'].indexOf(output) !== -1) { @@ -237,7 +237,8 @@ }) } - importDefaultCommands = () => { + // 导入默认命令 + let importDefaultCommands = () => { let customFts = fofoCommon.getDB('customFts') let qc = Object.keys(customFts) let defaultCommands = getDefaultCommands() @@ -245,4 +246,1307 @@ 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; + fofoCommon.putDB(code, pushData.qc, 'customFts'); + // 多个命令导入 + } else { + for (var code of Object.keys(pushData.qc)) { + fofoCommon.putDB(code, pushData.qc[code], 'customFts'); + } + } + return true + } + + // 全部导出 + let exportAll = () => { + let jsonQc = utools.db.get('customFts').data, + options = { + title: '选择保存位置', + defaultPath: 'quickCommand', + filters: [ + { name: 'json', extensions: ['json'] }, + ] + }; + if (!fofoCommon.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 + utools.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 = fofoCommon.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 == "默认" && !fofoCommon.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 && !fofoCommon.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 == "默认") && !fofoCommon.isDev() && e.preventDefault(); + }).on("select2:selecting", e => { + (e.params.args.data.text == "默认" || e.params.args.data.text == "未分类") && !fofoCommon.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 createTypeSelect2 = (width = false) => { + var data = [ + { + id: "key", + text: "关键字", + html: "关键字
在主输入框输入对应关键字进入插件,最通用的一种模式,关键字可以设置多个
" + }, + { + id: "regex", + text: "正则/划词", + html: "正则/划词
正则匹配主输入框文本或唤出语音面板时选中的文本,可以获取输入框文本或选中文本作为变量
" + }, + { + id: "window", + text: "窗口/进程", + html: "窗口/进程
匹配呼出uTools前或唤出语音面板时的活动窗口,可以获取窗口的信息或文件夹路径作为变量
" + }, + { + id: "files", + text: "复制/选中文件", + html: "复制/选中文件
匹配拖入主输入框的文件或唤出语音面板时选中的文件,可以获取复制及选中的文件信息作为变量
" + } + ] + $('#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.css('display') != 'none') 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()) { + utools.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 = fofoCommon.getDB('customFts'), + code = $(this).parents('tr').attr('id') + if (!utools.removeFeature(code)) { + utools.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 = utools.db.get("customFts").data[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 + fofoCommon.putDB('yuQueToken', yuQueToken, 'extraInfo') + fofoCommon.putDB('authorName', authorName, 'extraInfo') + fofoCommon.putDB('authorId', authorId, 'extraInfo') + quickcommand.showMessageBox("设置成功~") + } catch (e) { + quickcommand.showMessageBox('Token 校验失败', "error") + } + } + + let createShareMenu = jsonQc => { + let menu = ['复制到剪贴板', '导出到文件', '', '设置 Token'] + let extraInfo = fofoCommon.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 = fofoCommon.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 = fofoCommon.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 platform = jsonQc.features.platform ? jsonQc.features.platform.join(" ") : "win32 darwin linux" + let type = jsonQc.features.cmds[0].type + let tags = jsonQc.tags ? jsonQc.tags.join(' ') : "" + type || (type = 'keywords') + let parameters = { + title: jsonQc.features.explain, + slug: jsonQc.features.code, + public: 1, + format: "markdown", + body: '```json\n' + stringifyQc + '\n```', + custom_description: `作者:${jsonQc.authorName} | 环境:${jsonQc.program} | 匹配:${type} | 平台:${platform} | 标签:${tags}` + } + 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") + fofoCommon.putDB(jsonQc.features.code, jsonQc, 'customFts'); + return jsonQc + } catch (error) { + return quickcommand.showMessageBox(error, "error") + } + } + + let getSharedQCFromYuQue = async () => { + $('#options').hide() + let extraInfo = fofoCommon.getDB('extraInfo') + if (extraInfo.yuQueToken) yuQueClient.defaults.headers['X-Auth-Token'] = extraInfo.yuQueToken + let res = await yuQueClient('repos/fofolee/qcreleases/docs') + let program, docs = res.data.data.map(d => { + program = d.custom_description.match(/环境:(.*?) /) + return { + title: d.title, + description: d.custom_description, + slug: d.slug, + icon: `logo/${program[1]}.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 = utools.db.get("customFts"), + data = db.data; + delete data[code]; + utools.removeFeature(code); + utools.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("默认") && !fofoCommon.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() + } + } + fofoCommon.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 runCurrentCommand = async () => { + if ($("#customize").is(":parent")) { + var cmd = window.editor.getValue() + cmd = special(cmd) + var requireInputVal = ['{{input}}', '{{subinput}}', '{{pwd}}', '{{SelectFile}}'] + .filter(x => cmd.includes(x)); + if (requireInputVal.length) { + var html = requireInputVal + .map(r => ``) + .join("") + await Swal.fire({ + title: "需要临时为以下变量赋值", + html: html, + focusConfirm: false, + preConfirm: () => { + requireInputVal.forEach(r => { + cmd = cmd.replace(new RegExp(r, 'g'), document.getElementById(r).value) + }) + } + }) + } + 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 = fofoCommon.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 + }); + }() diff --git a/src/assets/options.js b/src/assets/options.js deleted file mode 100644 index a22210a..0000000 --- a/src/assets/options.js +++ /dev/null @@ -1,1299 +0,0 @@ -!function () { - 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 - } - - // 导入 - 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; - fofoCommon.putDB(code, pushData.qc, 'customFts'); - // 多个命令导入 - } else { - for (var code of Object.keys(pushData.qc)) { - fofoCommon.putDB(code, pushData.qc[code], 'customFts'); - } - } - return true - } - - let exportAll = () => { - let jsonQc = utools.db.get('customFts').data, - options = { - title: '选择保存位置', - defaultPath: 'quickCommand', - filters: [ - { name: 'json', extensions: ['json'] }, - ] - }; - if (!fofoCommon.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 - utools.db.remove('customFts'); - importDefaultCommands(); - clearAllFeatures(); - showOptions(); - }) - } - - 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' - } - } - - showOptions = (tag = "默认") => { - $("#options").empty().fadeIn(); - var currentFts = utools.getFeatures(), - customFts = fofoCommon.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 == "默认" && !fofoCommon.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 && !fofoCommon.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 == "默认") && !fofoCommon.isDev() && e.preventDefault(); - }).on("select2:selecting", e => { - (e.params.args.data.text == "默认" || e.params.args.data.text == "未分类") && !fofoCommon.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 createTypeSelect2 = (width = false) => { - var data = [ - { - id: "key", - text: "关键字", - html: "关键字
在主输入框输入对应关键字进入插件,最通用的一种模式,关键字可以设置多个
" - }, - { - id: "regex", - text: "正则/划词", - html: "正则/划词
正则匹配主输入框文本或唤出语音面板时选中的文本,可以获取输入框文本或选中文本作为变量
" - }, - { - id: "window", - text: "窗口/进程", - html: "窗口/进程
匹配呼出uTools前或唤出语音面板时的活动窗口,可以获取窗口的信息或文件夹路径作为变量
" - }, - { - id: "files", - text: "复制/选中文件", - html: "复制/选中文件
匹配拖入主输入框的文件或唤出语音面板时选中的文件,可以获取复制及选中的文件信息作为变量
" - } - ] - $('#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.css('display') != 'none') 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()) { - utools.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 = fofoCommon.getDB('customFts'), - code = $(this).parents('tr').attr('id') - if (!utools.removeFeature(code)) { - utools.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 = utools.db.get("customFts").data[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 - fofoCommon.putDB('yuQueToken', yuQueToken, 'extraInfo') - fofoCommon.putDB('authorName', authorName, 'extraInfo') - fofoCommon.putDB('authorId', authorId, 'extraInfo') - quickcommand.showMessageBox("设置成功~") - } catch (e) { - quickcommand.showMessageBox('Token 校验失败', "error") - } - } - - let createShareMenu = jsonQc => { - let menu = ['复制到剪贴板', '导出到文件', '', '设置 Token'] - let extraInfo = fofoCommon.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 = fofoCommon.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 = fofoCommon.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 platform = jsonQc.features.platform ? jsonQc.features.platform.join(" ") : "win32 darwin linux" - let type = jsonQc.features.cmds[0].type - let tags = jsonQc.tags ? jsonQc.tags.join(' ') : "" - type || (type = 'keywords') - let parameters = { - title: jsonQc.features.explain, - slug: jsonQc.features.code, - public: 1, - format: "markdown", - body: '```json\n' + stringifyQc + '\n```', - custom_description: `作者:${jsonQc.authorName} | 环境:${jsonQc.program} | 匹配:${type} | 平台:${platform} | 标签:${tags}` - } - 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") - fofoCommon.putDB(jsonQc.features.code, jsonQc, 'customFts'); - return jsonQc - } catch (error) { - return quickcommand.showMessageBox(error, "error") - } - } - - let getSharedQCFromYuQue = async () => { - $('#options').hide() - let extraInfo = fofoCommon.getDB('extraInfo') - if (extraInfo.yuQueToken) yuQueClient.defaults.headers['X-Auth-Token'] = extraInfo.yuQueToken - let res = await yuQueClient('repos/fofolee/qcreleases/docs') - let program, docs = res.data.data.map(d => { - program = d.custom_description.match(/环境:(.*?) /) - return { - title: d.title, - description: d.custom_description, - slug: d.slug, - icon: `logo/${program[1]}.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 = utools.db.get("customFts"), - data = db.data; - delete data[code]; - utools.removeFeature(code); - utools.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("默认") && !fofoCommon.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, - base64ico, - 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() - } - } - fofoCommon.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 runCurrentCommand = async () => { - if ($("#customize").is(":parent")) { - var cmd = window.editor.getValue() - cmd = special(cmd) - var requireInputVal = ['{{input}}', '{{subinput}}', '{{pwd}}', '{{SelectFile}}'] - .filter(x => cmd.includes(x)); - if (requireInputVal.length) { - var html = requireInputVal - .map(r => ``) - .join("") - await Swal.fire({ - title: "需要临时为以下变量赋值", - html: html, - focusConfirm: false, - preConfirm: () => { - requireInputVal.forEach(r => { - cmd = cmd.replace(new RegExp(r, 'g'), document.getElementById(r).value) - }) - } - }) - } - 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] - } - } - - 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 = fofoCommon.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 - }); -}() diff --git a/src/index.html b/src/index.html index 8f7a284..17f28c0 100755 --- a/src/index.html +++ b/src/index.html @@ -22,7 +22,6 @@

-