import { addIcon, App, Editor, MarkdownView, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
interface CypherPluginSettings {
cypherStyle: string;
}
const DEFAULT_SETTINGS: CypherPluginSettings = {
cypherStyle: 'diagrammatic'
};
export default class CypherPlugin extends Plugin {
settings: CypherPluginSettings;
private typingTimeout: number | undefined;
private isTypingHandled: boolean = false;
private isCypherEnabled: boolean = false;
async onload() {
await this.loadSettings();
// Ribbon icon for toggling cypher mode
addIcon('cypher', ``);
this.addRibbonIcon('cypher', 'Cypher Text', () => {
this.isCypherEnabled = !this.isCypherEnabled;
if (this.isCypherEnabled) {
new Notice('Cypher Activated!');
this.applyCypher();
} else {
new Notice('Cypher Deactivated!');
this.revertCypher();
}
}).addClass('cypher-plugin-ribbon-class');
// Command for transforming selected t`ext
this.addCommand({
id: 'cypher-transform',
name: 'Encrypt Selected Text',
editorCallback: (editor: Editor, view: MarkdownView) => this.transformText(editor),
});
// Add settings tab
this.addSettingTab(new CypherSettingTab(this.app, this));
// Editor change event for real-time typing transformation
this.registerEvent(
this.app.workspace.on("editor-change", () => {
const editor = this.app.workspace.getActiveViewOfType(MarkdownView)?.editor;
if (editor && this.isCypherEnabled) {
this.debounceTyping(editor, 500);
}
})
);
}
onunload() {
console.log("Cypher Unloaded.");
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
// Debounce function to delay the execution of handleTyping
debounceTyping(editor: Editor, delay = 500) {
if (this.isTypingHandled) return;
// Clear existing timeout, if any
if (this.typingTimeout) clearTimeout(this.typingTimeout);
this.isTypingHandled = true;
this.typingTimeout = window.setTimeout(() => {
this.handleTyping(editor);
this.isTypingHandled = false;
}, delay);
}
// Cypher transformation with distinct symbols for uppercase and lowercase
toCypherText(input: string): string {
const iconCypherMap: Record = {
A: "🔺", B: "🅱️", C: "🌜", D: "🔷", E: "🍏",
F: "🍀", G: "🌀", H: "🏠", I: "🦋", J: "🎷",
K: "🔑", L: "🌿", M: "🏆", N: "🎵", O: "💎",
P: "🎩", Q: "🎈", R: "🌹", S: "🌞", T: "🎾",
U: "🐢", V: "🎻", W: "🍉", X: "❌", Y: "🌱",
Z: "⚡️",
a: "🍎", b: "🍌", c: "🍒", d: "🍩", e: "🌽",
f: "🐸", g: "🦍", h: "🍯", i: "🍦", j: "🌶️",
k: "🥝", l: "🦁", m: "🍈", n: "🥜", o: "🍊",
p: "🍕", q: "👑", r: "🐀", s: "🐍", t: "🌮",
u: "🐡", v: "🍇", w: "💫", x: "🍪", y: "🦓",
z: "🍫"
};
let result = "";
let wasLastCharSpace = false;
for (let i = 0; i < input.length; i++) {
const char = input[i];
if (char === " ") {
if (!wasLastCharSpace) {
result += " ";
wasLastCharSpace = true;
}
} else {
result += iconCypherMap[char] || char;
wasLastCharSpace = false;
}
}
return result;
}
// Revert cyphered text back to normal
revertCypherText(input: string): string {
const reverseCypherMap: Record = {
"🔺": "A", "🅱️": "B", "🌜": "C", "🔷": "D", "🍏": "E",
"🍀": "F", "🌀": "G", "🏠": "H", "🦋": "I", "🎷": "J",
"🔑": "K", "🌿": "L", "🏆": "M", "🎵": "N", "💎": "O",
"🎩": "P", "🎈": "Q", "🌹": "R", "🌞": "S", "🎾": "T",
"🐢": "U", "🎻": "V", "🍉": "W", "❌": "X", "🌱": "Y",
"⚡️": "Z",
"🍎": "a", "🍌": "b", "🍒": "c", "🍩": "d", "🌽": "e",
"🐸": "f", "🦍": "g", "🍯": "h", "🍦": "i", "🌶️": "j",
"🥝": "k", "🦁": "l", "🍈": "m", "🥜": "n", "🍊": "o",
"🍕": "p", "👑": "q", "🐀": "r", "🐍": "s", "🌮": "t",
"🐡": "u", "🍇": "v", "💫": "w", "🍪": "x", "🦓": "y",
"🍫": "z"
};
return Array.from(input).map(char => reverseCypherMap[char] || char).join("");
}
handleTyping(editor: Editor) {
const cursor = editor.getCursor();
const lineText = editor.getLine(cursor.line);
const modifiedText = this.isCypherEnabled ? this.toCypherText(lineText) : this.revertCypherText(lineText);
editor.setLine(cursor.line, modifiedText);
}
applyCypher() {
const editor = this.app.workspace.getActiveViewOfType(MarkdownView)?.editor;
if (editor) {
const fullText = editor.getValue();
editor.setValue(this.toCypherText(fullText));
}
}
revertCypher() {
const editor = this.app.workspace.getActiveViewOfType(MarkdownView)?.editor;
if (editor) {
const fullText = editor.getValue();
editor.setValue(this.revertCypherText(fullText));
}
}
transformText(editor: Editor) {
const selections = editor.getSelection();
const cypheredText = this.isCypherEnabled ? this.toCypherText(selections) : this.revertCypherText(selections);
editor.replaceSelection(cypheredText);
}
}
// Updated settings tab
class CypherSettingTab extends PluginSettingTab {
plugin: CypherPlugin;
constructor(app: App, plugin: CypherPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
new Setting(containerEl)
.setName('Cypher Style')
.setDesc('Choose The Style For Cyphering Text')
.addDropdown(dropdown => dropdown
.addOption('diagrammatic', 'Diagrammatic')
.setValue(this.plugin.settings.cypherStyle)
.onChange(async (value) => {
this.plugin.settings.cypherStyle = value;
await this.plugin.saveSettings();
}));
}
}