2024-03-16 22:29:24 +08:00
|
|
|
import emojiBlob from "./emoji.json" with { type: "json" };
|
2024-02-29 22:23:05 +08:00
|
|
|
import type { CompleteEvent } from "../../plug-api/types.ts";
|
2024-08-07 02:11:38 +08:00
|
|
|
import { editor, system } from "@silverbulletmd/silverbullet/syscalls";
|
|
|
|
import type { EmojiConfig } from "@silverbulletmd/silverbullet/type/client";
|
2024-02-06 21:07:38 +08:00
|
|
|
|
|
|
|
let emojiConfig: EmojiConfig = { aliases: [] };
|
2022-04-01 21:02:35 +08:00
|
|
|
|
2023-11-21 19:19:47 +08:00
|
|
|
const emojis = emojiBlob.split("|").map((line) => line.split(" "));
|
|
|
|
|
2022-12-21 21:55:24 +08:00
|
|
|
export function emojiCompleter({ linePrefix, pos }: CompleteEvent) {
|
2024-02-06 21:07:38 +08:00
|
|
|
updateConfig(); // no need to await, will be ready for completion by next keystrokes
|
|
|
|
|
2022-12-21 21:55:24 +08:00
|
|
|
const match = /:([\w]+)$/.exec(linePrefix);
|
|
|
|
if (!match) {
|
2022-04-01 21:02:35 +08:00
|
|
|
return null;
|
|
|
|
}
|
2022-12-21 21:55:24 +08:00
|
|
|
|
|
|
|
const [fullMatch, emojiName] = match;
|
|
|
|
|
2024-02-06 21:07:38 +08:00
|
|
|
const filteredEmoji = [...emojiConfig.aliases, ...emojis].filter(
|
|
|
|
([shortcode]) => shortcode.includes(emojiName),
|
2022-04-01 21:02:35 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
return {
|
2022-12-21 21:55:24 +08:00
|
|
|
from: pos - fullMatch.length,
|
2022-04-01 21:02:35 +08:00
|
|
|
filter: false,
|
2023-11-21 19:19:47 +08:00
|
|
|
options: filteredEmoji.map(([shortcode, emoji]) => ({
|
2022-04-01 21:02:35 +08:00
|
|
|
detail: shortcode,
|
|
|
|
label: emoji,
|
|
|
|
type: "emoji",
|
|
|
|
})),
|
|
|
|
};
|
|
|
|
}
|
2024-02-06 21:07:38 +08:00
|
|
|
|
|
|
|
let lastConfigUpdate = 0;
|
2024-02-07 00:20:31 +08:00
|
|
|
|
2024-02-06 21:07:38 +08:00
|
|
|
async function updateConfig() {
|
|
|
|
// Update at most every 5 seconds
|
|
|
|
if (Date.now() < lastConfigUpdate + 5000) return;
|
|
|
|
lastConfigUpdate = Date.now();
|
2024-08-02 22:47:36 +08:00
|
|
|
const config = await system.getSpaceConfig("emoji");
|
2024-02-07 00:20:31 +08:00
|
|
|
if (!config) {
|
|
|
|
return;
|
|
|
|
}
|
2024-02-06 21:07:38 +08:00
|
|
|
|
|
|
|
// This is simpler to write in SETTINGS and prevents duplicates,
|
|
|
|
// which could be supported but probably aren't user's intent
|
|
|
|
const errorMsg =
|
2024-08-02 22:47:36 +08:00
|
|
|
"Emoji aliases in space config should be a map with entries 'name: 😀'";
|
2024-02-06 21:07:38 +08:00
|
|
|
|
|
|
|
let aliasMap: Record<string, any> = {};
|
|
|
|
if (config.aliases && typeof config.aliases !== "object") {
|
|
|
|
await editor.flashNotification(errorMsg, "error");
|
|
|
|
} else {
|
|
|
|
aliasMap = config.aliases;
|
|
|
|
}
|
|
|
|
|
|
|
|
const aliases = [];
|
|
|
|
const badAliases = [];
|
|
|
|
for (const alias in aliasMap) {
|
|
|
|
if (typeof aliasMap[alias] !== "string") {
|
|
|
|
badAliases.push(alias);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const emoji: string = aliasMap[alias].trim();
|
|
|
|
// For detecting misconfiguration like 'smile: grinning_face' which wouldn't work.
|
|
|
|
// Side effect: can't use for phrases like 'br: Best regards', but Slash Commands are meant for that
|
|
|
|
if ([...emoji].find((c) => c.charCodeAt(0) <= 127)) {
|
|
|
|
// ASCII characters somewhere in text
|
|
|
|
badAliases.push(alias);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
aliases.push([alias, emoji]);
|
|
|
|
}
|
|
|
|
if (badAliases.length > 0) {
|
|
|
|
await editor.flashNotification(
|
|
|
|
errorMsg + `, need to fix: ${badAliases.join(",")}`,
|
|
|
|
"error",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
emojiConfig = {
|
|
|
|
aliases: aliases,
|
|
|
|
};
|
|
|
|
}
|