-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'release/0.1.1' into main
- Loading branch information
Showing
7 changed files
with
196 additions
and
249 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
"presets": [ | ||
[ | ||
"@babel/preset-env", | ||
"@babel/typescript", | ||
{ | ||
"targets": { | ||
"electron": "8.2.5" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,212 +1,115 @@ | ||
import { Plugin } from 'obsidian'; | ||
import { App, FuzzySuggestModal, Plugin, FuzzyMatch, Notice, MarkdownView } from 'obsidian'; | ||
import emoji from 'emojilib'; | ||
|
||
const QUICK_SWITCHER_ID = 'switcher'; | ||
const EMOJIPREFIX = '@'; | ||
|
||
const indicatorStyle = | ||
const indicatorStyle: string = | ||
'color: var(--text-accent); width: 2.5em; text-align: center; float:left; font-weight:800;'; | ||
|
||
function getQuickSwitcher(app) { | ||
const switcher = app.internalPlugins.getPluginById(QUICK_SWITCHER_ID); | ||
if (!switcher) { | ||
return null; | ||
} | ||
|
||
return switcher.instance.modal.constructor; | ||
} | ||
|
||
function createEmojiPickerModal(app, emojis) { | ||
const QuickSwitcher = getQuickSwitcher(app); | ||
if (QuickSwitcher === null) { | ||
return null; | ||
} | ||
|
||
class EmojiPicker extends QuickSwitcher { | ||
constructor(app) { | ||
super(app); | ||
} | ||
|
||
onOpen() { | ||
// force reset suggestions so any suggestions from a previous operation | ||
// won't be incorrectly used for symbol search | ||
this.chooser.setSuggestions([]); | ||
this.isOpen = true; | ||
|
||
// prefix inut with @ symbol | ||
this.inputEl.value = EMOJIPREFIX; | ||
this.inputEl.focus(); | ||
this.updateSuggestions(); | ||
} | ||
|
||
getSearchData() { | ||
const { | ||
inputEl: { value }, | ||
} = this; | ||
let startIndex = 0; | ||
|
||
startIndex = value.indexOf(EMOJIPREFIX) + EMOJIPREFIX.length; | ||
|
||
return EmojiPicker.extractTokens(value, startIndex); | ||
} | ||
|
||
static extractTokens(str, startIndex = 0) { | ||
// shamelessly stolen directly from Obsidian | ||
const p = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?@\[\]^_`{|}~]/; | ||
const u = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f]/; | ||
const b = /\s/; | ||
const query = str.slice(startIndex).toLowerCase(); | ||
const tokens = []; | ||
let pos = 0; | ||
|
||
for (let i = 0; i < query.length; i++) { | ||
const char = query.charAt(i); | ||
|
||
if (b.test(char)) { | ||
if (pos !== i) { | ||
tokens.push(query.slice(pos, i)); | ||
} | ||
|
||
pos = i + 1; | ||
} else if (p.test(char) || u.test(char)) { | ||
if (pos !== i) { | ||
tokens.push(query.slice(pos, i)); | ||
} | ||
|
||
tokens.push(char); | ||
pos = i + 1; | ||
} | ||
} | ||
|
||
if (pos !== query.length) { | ||
tokens.push(query.slice(pos, query.length)); | ||
} | ||
|
||
return { query, tokens, fuzzy: query.split('') }; | ||
} | ||
|
||
updateSuggestions() { | ||
const searchData = this.getSearchData(); | ||
const suggestions = this.makeSuggestions(searchData); | ||
|
||
this.chooser.setSuggestions(suggestions); | ||
} | ||
export default class MyPlugin extends Plugin { | ||
emojis: EmojiItem[] | ||
|
||
makeSuggestions(searchData) { | ||
const suggestions = []; | ||
const hasSearchTerm = searchData.query.length > 0; | ||
|
||
emojis.forEach((item) => { | ||
let sugg; | ||
|
||
if (hasSearchTerm) { | ||
const match = this.match(searchData, item); | ||
|
||
if (match !== null) { | ||
sugg = { | ||
match, | ||
}; | ||
} | ||
} else { | ||
sugg = { | ||
match: null, | ||
}; | ||
} | ||
|
||
if (sugg) { | ||
sugg.item = item; | ||
suggestions.push(sugg); | ||
} | ||
}); | ||
|
||
if (hasSearchTerm) { | ||
suggestions.sort((a, b) => b.match.score - a.match.score); | ||
loadEmojis(): EmojiItem[] { | ||
function titleCase(string: string) { | ||
let sentence = string.toLowerCase().split('_'); | ||
for (let i = 0; i < sentence.length; i++) { | ||
sentence[i] = sentence[i][0].toUpperCase() + sentence[i].slice(1); | ||
} | ||
return suggestions; | ||
} | ||
|
||
getItemText(item) { | ||
const { symbol } = item; | ||
|
||
let text; | ||
text = symbol.heading; | ||
return text; | ||
|
||
return sentence.join(' '); | ||
} | ||
|
||
let items = emoji.ordered.map((name) => { | ||
return { | ||
name: titleCase(name), | ||
char: emoji.lib[name].char, | ||
}; | ||
}); | ||
|
||
return items; | ||
} | ||
|
||
insertTextAtCursor(editor, text) { | ||
var doc = editor.getDoc(); | ||
var cursor = doc.getCursor(); | ||
doc.replaceRange(text, cursor); | ||
} | ||
onload() { | ||
|
||
this.emojis = this.loadEmojis(); | ||
|
||
this.addCommand({ | ||
id: 'open-sample-modal', | ||
name: 'Open Sample Modal', | ||
hotkeys: [ | ||
{ | ||
modifiers: ["Mod", "Shift"], | ||
key: "o", | ||
}, | ||
], | ||
checkCallback: (checking: boolean) => { | ||
let leaf = this.app.workspace.activeLeaf; | ||
if (leaf) { | ||
if (!checking) { | ||
new EmojiFuzzySuggestModal(this.app, this.emojis).open(); | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
}); | ||
|
||
} | ||
|
||
onunload() { | ||
} | ||
} | ||
|
||
onChooseOption(suggestionItem) { | ||
let activeLeaf = this.app.workspace.activeLeaf; | ||
let editor = activeLeaf.view.sourceMode.cmEditor; | ||
this.insertTextAtCursor(editor, suggestionItem.symbol.char); | ||
} | ||
interface EmojiItem { | ||
name: string; | ||
char: string; | ||
} | ||
|
||
renderSuggestion(sugg, parentEl) { | ||
super.renderSuggestion(sugg, parentEl); | ||
this.updateSuggestionElForMode(sugg, parentEl); | ||
} | ||
|
||
updateSuggestionElForMode(sugg, parentEl) { | ||
const { symbol } = sugg.item; | ||
class EmojiFuzzySuggestModal extends FuzzySuggestModal<EmojiItem> { | ||
app: App; | ||
emojis: EmojiItem[]; | ||
|
||
const indicatorEl = createEl('div', { | ||
text: symbol.char, | ||
attr: { style: indicatorStyle }, | ||
}); | ||
parentEl.insertAdjacentElement('afterbegin', indicatorEl); | ||
} | ||
constructor(app: App, emojis: EmojiItem[]) { | ||
super(app); | ||
this.app = app; | ||
this.emojis = emojis; | ||
} | ||
|
||
return new EmojiPicker(app); | ||
} | ||
|
||
function loadEmojis() { | ||
function titleCase(string) { | ||
let sentence = string.toLowerCase().split('_'); | ||
for (let i = 0; i < sentence.length; i++) { | ||
sentence[i] = sentence[i][0].toUpperCase() + sentence[i].slice(1); | ||
} | ||
getItems(): EmojiItem[] { | ||
return this.emojis; | ||
} | ||
|
||
return sentence.join(' '); | ||
getItemText(item: EmojiItem): string { | ||
return item.name; | ||
} | ||
|
||
let items = emoji.ordered.map((name) => { | ||
return { | ||
symbol: { | ||
heading: titleCase(name), | ||
char: emoji.lib[name].char, | ||
}, | ||
}; | ||
}); | ||
renderSuggestion(item: FuzzyMatch<EmojiItem>, el: HTMLElement) { | ||
super.renderSuggestion(item, el); | ||
this.updateSuggestionElForMode(item, el); | ||
} | ||
|
||
return items; | ||
} | ||
updateSuggestionElForMode(item: FuzzyMatch<EmojiItem>, el: HTMLElement) { | ||
|
||
export default class EmojiPickerPlugin extends Plugin { | ||
onload() { | ||
this.emojis = loadEmojis(); | ||
this.addCommand({ | ||
id: 'emoji-picker:open-picker', | ||
name: 'Open emoji picker', | ||
hotkeys: [], | ||
checkCallback: (checking) => { | ||
let leaf = this.app.workspace.activeLeaf; | ||
if (leaf) { | ||
if (!checking) { | ||
new createEmojiPickerModal(this.app, this.emojis).open(); | ||
} | ||
return true; | ||
} | ||
return false; | ||
}, | ||
const indicatorEl = createEl('div', { | ||
text: item.item.char, | ||
attr: { style: indicatorStyle }, | ||
}); | ||
el.insertAdjacentElement('afterbegin', indicatorEl); | ||
} | ||
|
||
onunload() { | ||
this.modal = null; | ||
insertTextAtCursor(view: MarkdownView, text:string): void { | ||
let editor = view.sourceMode.cmEditor | ||
let doc = editor.getDoc(); | ||
let cursor = doc.getCursor(); | ||
doc.replaceRange(text, cursor); | ||
} | ||
} | ||
|
||
onChooseItem(item: EmojiItem, evt: MouseEvent | KeyboardEvent): void { | ||
let activeEditor = this.app.workspace.getActiveViewOfType(MarkdownView) | ||
if (activeEditor) { | ||
this.insertTextAtCursor(activeEditor, item.char) | ||
} else { | ||
new Notice("You'll need to open a markdown editor to insert an emoji"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.