riogesulgon_obsidian-cryptopad/main.js
Gesulgon, Rio Garque b86ccb4cea Fixed obsidian-lint warnings
css lints
2026-06-15 23:12:05 +08:00

1 line
12 KiB
JavaScript

var C=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var H=(i,s)=>{for(var t in s)C(i,t,{get:s[t],enumerable:!0})},N=(i,s,t,r)=>{if(s&&typeof s=="object"||typeof s=="function")for(let p of O(s))!R.call(i,p)&&p!==t&&C(i,p,{get:()=>s[p],enumerable:!(r=B(s,p))||r.enumerable});return i};var U=i=>N(C({},"__esModule",{value:!0}),i);var _={};H(_,{default:()=>E});module.exports=U(_);var o=require("obsidian"),I={passphrase:"",rememberPassphrase:!1,defaultMode:"encrypt"},q=2e5,c=16,u=12;function V(i){let s=new Uint8Array(i),t="";for(let r=0;r<s.byteLength;r++)t+=String.fromCharCode(s[r]);return btoa(t)}function G(i){let s=atob(i),t=new Uint8Array(s.length);for(let r=0;r<s.length;r++)t[r]=s.charCodeAt(r);return t}async function P(i,s){let t=await crypto.subtle.importKey("raw",new TextEncoder().encode(i),{name:"PBKDF2"},!1,["deriveKey"]);return crypto.subtle.deriveKey({name:"PBKDF2",salt:s,iterations:q,hash:"SHA-256"},t,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}async function W(i,s){if(!i)throw new Error("Passphrase is required.");if(!s)throw new Error("Text to encrypt/decrypt is required.");let t=crypto.getRandomValues(new Uint8Array(c)),r=crypto.getRandomValues(new Uint8Array(u)),p=await P(i,t),a=await crypto.subtle.encrypt({name:"AES-GCM",iv:r},p,new TextEncoder().encode(s)),n=new Uint8Array(c+u+a.byteLength);return n.set(t,0),n.set(r,c),n.set(new Uint8Array(a),c+u),V(n.buffer)}async function F(i,s){if(!i)throw new Error("Passphrase is required.");if(!s)throw new Error("Ciphertext is required.");let t;try{t=G(s.trim())}catch(m){throw new Error("Invalid ciphertext \u2014 not valid base64.")}if(t.byteLength<=c+u)throw new Error("Invalid ciphertext \u2014 data too short.");let r=t.slice(0,c),p=t.slice(c,c+u),a=t.slice(c+u),n=await P(i,r),h;try{h=await crypto.subtle.decrypt({name:"AES-GCM",iv:p},n,a)}catch(m){throw new Error("Decryption failed \u2014 wrong passphrase or corrupted data.")}return new TextDecoder().decode(h)}var y=class extends o.Modal{constructor(t,r){super(t);this.mode="encrypt";this.toastEl=null;this.plugin=r,this.mode=r.settings.defaultMode,r.lastModal=this}setMode(t){this.mode=t}setToastElement(t){this.toastEl=t}getOutputValue(){return this.outputArea.value}replaceSelectedTextWithOutput(){var r;if(!this.outputArea.value)return!1;let t=(r=this.app.workspace.getActiveViewOfType(o.MarkdownView))==null?void 0:r.editor;return t?(t.replaceSelection(this.outputArea.value),new o.Notice("\u2705 Text replaced!"),this.close(),!0):!1}onOpen(){let{contentEl:t}=this;t.empty(),t.addClass("cryptopad-modal"),this.buildUI();let r=this.getSelectedText();r&&(this.inputArea.value=r,this.inputArea.focus())}getSelectedText(){let t=this.app.workspace.getActiveViewOfType(o.MarkdownView);return t&&t.editor&&t.editor.getSelection()||""}buildUI(){let{contentEl:t}=this,r=t.createDiv({cls:"cryptopad-header"});r.createSpan({cls:"cryptopad-title",text:"\u{1F510} CryptoPad"}),this.statusEl=r.createDiv({cls:"cryptopad-status"}),this.updateStatus();let p=t.createDiv({cls:"cryptopad-command-row"});p.createSpan({text:":"}),this.commandInput=p.createEl("input",{cls:"cryptopad-command-input",placeholder:"e/d/c/q"}),this.commandInput.autocomplete="off",this.commandInput.spellcheck=!1,this.commandInput.tabIndex=0;let a=t.createDiv({cls:"cryptopad-body"});a.createEl("label",{cls:"cryptopad-label",text:"Passphrase"});let n=a.createDiv({cls:"cryptopad-pass-row"});this.passInput=n.createEl("input",{cls:"cryptopad-input-field",placeholder:"Enter passphrase... (Ctrl+P to toggle visibility)"}),this.passInput.type="password",this.passInput.autocomplete="off",this.passInput.spellcheck=!1,this.passInput.tabIndex=0,this.plugin.settings.rememberPassphrase&&(this.passInput.value=this.plugin.settings.passphrase);let m=a.createDiv({cls:"cryptopad-remember-row"}).createEl("label",{cls:"cryptopad-checkbox-label"}),v=m.createEl("input");v.type="checkbox",v.checked=this.plugin.settings.rememberPassphrase,v.tabIndex=0,m.appendText(" Remember passphrase"),a.createEl("label",{cls:"cryptopad-label",text:"Text to encrypt/decrypt"}),this.inputArea=a.createEl("textarea",{cls:"cryptopad-input-field cryptopad-textarea",placeholder:"Type or paste text here... (Ctrl+Enter to process, Ctrl+C to clear)"}),this.inputArea.rows=3,this.inputArea.spellcheck=!1,this.inputArea.tabIndex=0,this.errorEl=a.createDiv({cls:"cryptopad-error cryptopad-error--hidden"}),this.resultBlock=a.createDiv({cls:"cryptopad-result-block cryptopad-result-block--hidden"}),this.resultBlock.createEl("label",{cls:"cryptopad-label",text:"Encrypted output"}),this.outputArea=this.resultBlock.createEl("textarea",{cls:"cryptopad-input-field cryptopad-textarea cryptopad-output"}),this.outputArea.rows=6,this.outputArea.readOnly=!0,this.outputArea.spellcheck=!1,this.outputArea.tabIndex=0;let k=this.resultBlock.createDiv({cls:"cryptopad-result-buttons"}),f=k.createEl("button",{text:"\u{1F4CB} Copy (Ctrl+c)"});f.addClass("cryptopad-button"),f.tabIndex=0;let b=k.createEl("button",{text:"\u{1F501} Replace (Ctrl+r) in editor"});b.addClass("cryptopad-button"),b.tabIndex=0;let A=a.createDiv({cls:"cryptopad-help"});A.createDiv({cls:"cryptopad-help-title",text:"\u2328\uFE0F Keyboard Shortcuts"});let K=A.createDiv({cls:"cryptopad-help-content"});[[":e","Encrypt mode"],[":d","Decrypt mode"],[":q","Quit"],["Ctrl+Enter","Process"],["Ctrl+P","Show passphrase"],["Ctrl+C","Copy output"],["Ctrl+R","Replace in editor"]].forEach(([e,l])=>{let d=K.createDiv();d.createEl("strong",{text:e}),d.appendText(" \u2014 "+l)});let g=t.createDiv({cls:"cryptopad-toast",text:"\u2705 Copied!"});this.setToastElement(g);let T=e=>{this.mode=e,this.resultBlock.removeClass("cryptopad-result-block--visible"),this.resultBlock.addClass("cryptopad-result-block--hidden"),this.errorEl.removeClass("cryptopad-error--visible"),this.errorEl.addClass("cryptopad-error--hidden"),this.updateStatus(),this.passInput.focus()},M=async()=>{let e=this.passInput.value.trim(),l=this.inputArea.value;this.errorEl.removeClass("cryptopad-error--visible"),this.errorEl.addClass("cryptopad-error--hidden"),this.resultBlock.removeClass("cryptopad-result-block--visible"),this.resultBlock.addClass("cryptopad-result-block--hidden");try{let d=v.checked;this.plugin.settings.rememberPassphrase=d,this.plugin.settings.passphrase=d?e:"",await this.plugin.saveSettings();let L=this.mode==="encrypt"?await W(e,l):await F(e,l);this.outputArea.value=L,this.resultBlock.removeClass("cryptopad-result-block--hidden"),this.resultBlock.addClass("cryptopad-result-block--visible"),this.outputArea.select()}catch(d){this.errorEl.textContent=d.message,this.errorEl.removeClass("cryptopad-error--hidden"),this.errorEl.addClass("cryptopad-error--visible")}},D=()=>{g.addClass("cryptopad-toast--visible"),window.setTimeout(()=>g.removeClass("cryptopad-toast--visible"),2e3)},w=async()=>{if(this.outputArea.value)try{await navigator.clipboard.writeText(this.outputArea.value),D()}catch(e){this.outputArea.select();try{await navigator.clipboard.writeText(this.outputArea.value)}catch(l){}D()}};f.addEventListener("click",()=>{w()}),b.addEventListener("click",()=>{this.replaceSelectedTextWithOutput()||(this.errorEl.textContent="No output to replace or no editor active",this.errorEl.removeClass("cryptopad-error--hidden"),this.errorEl.addClass("cryptopad-error--visible"))}),this.passInput.addEventListener("keydown",e=>{(e.ctrlKey||e.metaKey)&&e.key==="p"&&(e.preventDefault(),this.passInput.type=this.passInput.type==="password"?"text":"password"),e.key==="Tab"&&!e.shiftKey&&(e.preventDefault(),this.inputArea.focus()),e.key==="Tab"&&e.shiftKey&&(e.preventDefault(),this.commandInput.focus())}),this.inputArea.addEventListener("keydown",e=>{(e.ctrlKey||e.metaKey)&&e.key==="Enter"&&(e.preventDefault(),M()),(e.ctrlKey||e.metaKey)&&e.key==="c"&&!e.shiftKey&&(e.preventDefault(),this.inputArea.value="",this.resultBlock.removeClass("cryptopad-result-block--visible"),this.resultBlock.addClass("cryptopad-result-block--hidden"),this.errorEl.removeClass("cryptopad-error--visible"),this.errorEl.addClass("cryptopad-error--hidden"))}),this.outputArea.addEventListener("keydown",e=>{(e.ctrlKey||e.metaKey)&&e.key==="c"&&!e.shiftKey&&(e.preventDefault(),w()),(e.ctrlKey||e.metaKey)&&e.key==="r"&&!e.shiftKey&&(e.preventDefault(),this.replaceSelectedTextWithOutput())});let S=e=>{switch(e){case"e":return T("encrypt"),!0;case"d":return T("decrypt"),!0;case"c":return w(),!0;case"q":case"quit":case"exit":return this.close(),!0;case"h":case"help":return this.errorEl.textContent="Commands: e(ncrypt) d(ecrypt) c(opy) q(uit). Ctrl+Enter: process | Ctrl+C: copy",this.errorEl.removeClass("cryptopad-error--hidden"),this.errorEl.addClass("cryptopad-error--visible"),!0;default:return e.length>0&&(this.errorEl.textContent=`Unknown command: ${e}`,this.errorEl.removeClass("cryptopad-error--hidden"),this.errorEl.addClass("cryptopad-error--visible")),!1}};this.commandInput.addEventListener("keydown",e=>{if(e.key==="Tab"){e.preventDefault();let l=this.commandInput.value.toLowerCase().trim();l.length>0&&(this.commandInput.value="",S(l)),this.passInput.focus()}if(e.key==="Enter"){e.preventDefault();let l=this.commandInput.value.toLowerCase().trim();this.commandInput.value="",S(l)}e.key==="Escape"&&(this.commandInput.value="")}),this.passInput.value?this.inputArea.focus():this.passInput.focus()}updateStatus(){let t=this.mode==="encrypt"?"ENCRYPT":"DECRYPT";this.statusEl.empty();let r=this.statusEl.createEl("span",{cls:"cryptopad-mode-badge",text:t});this.statusEl.appendText(" E/D to switch | Ctrl+Enter to process"),r.classList.remove("highlight"),r.offsetWidth,r.classList.add("highlight")}onClose(){this.contentEl.empty()}},x=class extends o.PluginSettingTab{constructor(s,t){super(s,t),this.plugin=t}display(){let{containerEl:s}=this;s.empty(),new o.Setting(s).setName("Saved passphrase").setDesc("Pre-filled in the modal when opened. Leave empty to always type it manually.").addText(a=>{let n=a.inputEl;n.type="password",a.setPlaceholder("Enter passphrase...").setValue(this.plugin.settings.passphrase).onChange(async h=>{this.plugin.settings.passphrase=h,await this.plugin.saveSettings()})}),new o.Setting(s).setName("Remember passphrase").setDesc("Pre-fill the passphrase field each time the modal opens.").addToggle(a=>a.setValue(this.plugin.settings.rememberPassphrase).onChange(async n=>{this.plugin.settings.rememberPassphrase=n,await this.plugin.saveSettings()})),new o.Setting(s).setName("Default mode").setDesc("Start with encrypt or decrypt mode.").addDropdown(a=>a.addOption("encrypt","Encrypt").addOption("decrypt","Decrypt").setValue(this.plugin.settings.defaultMode).onChange(async n=>{this.plugin.settings.defaultMode=n,await this.plugin.saveSettings()})),new o.Setting(s).setName("Keyboard shortcuts").setHeading();let t=["Ctrl+Shift+E \u2014 Open CryptoPad","Ctrl+Shift+C \u2014 Open in Encrypt mode","Ctrl+Shift+D \u2014 Open in Decrypt mode","Tab \u2014 Navigate to next field","Ctrl+P \u2014 Toggle passphrase visibility","Ctrl+Enter \u2014 Encrypt/Decrypt","Ctrl+C \u2014 Clear input (in textarea) or copy output","Ctrl+R \u2014 Replace selected text in editor with output",":e \u2014 Switch to Encrypt mode",":d \u2014 Switch to Decrypt mode",":c \u2014 Copy output",":q \u2014 Close modal",":help \u2014 Show help"],r=s.createEl("ul");t.forEach(a=>{r.createEl("li",{text:a}).addClass("cryptopad-list-item")}),new o.Setting(s).setName("Encryption details").setHeading();let p=s.createEl("ul");["Cipher: AES-256-GCM","Key derivation: PBKDF2-SHA256 (200,000 iterations)","Salt: 16 bytes \u2014 random per encryption","IV: 12 bytes \u2014 random per encryption","Output encoding: Base64","API: Web Crypto API (native browser/Electron)"].forEach(a=>{p.createEl("li",{text:a}).addClass("cryptopad-list-item")})}},E=class extends o.Plugin{constructor(){super(...arguments);this.settings=I;this.lastModal=null}async onload(){await this.loadSettings(),this.addCommand({id:"open-cryptopad",name:"Open CryptoPad",callback:()=>{new y(this.app,this).open()}}),this.addCommand({id:"encrypt-quick",name:"Quick Encrypt",callback:()=>{let t=new y(this.app,this);t.setMode("encrypt"),t.open()}}),this.addCommand({id:"decrypt-quick",name:"Quick Decrypt",callback:()=>{let t=new y(this.app,this);t.setMode("decrypt"),t.open()}}),this.addCommand({id:"replace-with-output",name:"Replace selection with encrypted/decrypted text",callback:()=>{this.lastModal&&this.lastModal.getOutputValue()&&this.lastModal.replaceSelectedTextWithOutput()}}),this.addSettingTab(new x(this.app,this))}onunload(){}async loadSettings(){let t=await this.loadData();this.settings=Object.assign({},I,t||{})}async saveSettings(){await this.saveData(this.settings)}};