Fantasy-Map-Generator/modules/ui/notes-editor.js
Azgaar 6df54d1ef6
AI generation for notes (#1112)
* feat: ai generation for notes

* feat - ai generation -default to gpt-4o-mini

* feat - ai generation - change update text

* feat - ai generation - improve prompt

---------

Co-authored-by: Azgaar <azgaar.fmg@yandex.com>
2024-08-23 18:08:50 +02:00

208 lines
6.6 KiB
JavaScript

"use strict";
function editNotes(id, name) {
// elements
const notesLegend = byId("notesLegend");
const notesName = byId("notesName");
const notesSelect = byId("notesSelect");
const notesPin = byId("notesPin");
// update list of objects
notesSelect.options.length = 0;
notes.forEach(({id}) => notesSelect.options.add(new Option(id, id)));
// update pin notes icon
const notesArePinned = options.pinNotes;
if (notesArePinned) notesPin.classList.add("pressed");
else notesPin.classList.remove("pressed");
// select an object
if (notes.length || id) {
if (!id) id = notes[0].id;
let note = notes.find(note => note.id === id);
if (!note) {
if (!name) name = id;
note = {id, name, legend: ""};
notes.push(note);
notesSelect.options.add(new Option(id, id));
}
notesSelect.value = id;
notesName.value = note.name;
notesLegend.innerHTML = note.legend;
initEditor();
updateNotesBox(note);
} else {
// if notes array is empty
notesName.value = "";
notesLegend.innerHTML = "No notes added. Click on an element (e.g. label or marker) and add a free text note";
}
$("#notesEditor").dialog({
title: "Notes Editor",
width: window.innerWidth * 0.8,
height: window.innerHeight * 0.75,
position: {my: "center", at: "center", of: "svg"},
close: removeEditor
});
if (modules.editNotes) return;
modules.editNotes = true;
// add listeners
byId("notesSelect").addEventListener("change", changeElement);
byId("notesName").addEventListener("input", changeName);
byId("notesLegend").addEventListener("blur", updateLegend);
byId("notesPin").addEventListener("click", toggleNotesPin);
byId("notesFocus").addEventListener("click", validateHighlightElement);
byId("notesGenerateWithAi").addEventListener("click", openAiGenerator);
byId("notesDownload").addEventListener("click", downloadLegends);
byId("notesUpload").addEventListener("click", () => legendsToLoad.click());
byId("legendsToLoad").addEventListener("change", function () {
uploadFile(this, uploadLegends);
});
byId("notesRemove").addEventListener("click", triggerNotesRemove);
async function initEditor() {
if (!window.tinymce) {
const url = "https://azgaar.github.io/Fantasy-Map-Generator/libs/tinymce/tinymce.min.js";
try {
await import(url);
} catch (error) {
// error may be caused by failed request being cached, try again with random hash
try {
const hash = Math.random().toString(36).substring(2, 15);
await import(`${url}#${hash}`);
} catch (error) {
console.error(error);
}
}
}
if (window.tinymce) {
window.tinymce._setBaseUrl("https://azgaar.github.io/Fantasy-Map-Generator/libs/tinymce");
tinymce.init({
license_key: "gpl",
selector: "#notesLegend",
height: "90%",
menubar: false,
plugins: `autolink lists link charmap code fullscreen image link media table wordcount`,
toolbar: `code | undo redo | removeformat | bold italic strikethrough | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media table | fontselect fontsizeselect | blockquote hr charmap | print fullscreen`,
media_alt_source: false,
media_poster: false,
browser_spellcheck: true,
contextmenu: false,
setup: editor => {
editor.on("Change", updateLegend);
}
});
}
}
function updateLegend() {
const note = notes.find(note => note.id === notesSelect.value);
if (!note) return tip("Note element is not found", true, "error", 4000);
const isTinyEditorActive = window.tinymce?.activeEditor;
note.legend = isTinyEditorActive ? tinymce.activeEditor.getContent() : notesLegend.innerHTML;
updateNotesBox(note);
}
function updateNotesBox(note) {
byId("notesHeader").innerHTML = note.name;
byId("notesBody").innerHTML = note.legend;
}
function changeElement() {
const note = notes.find(note => note.id === this.value);
if (!note) return tip("Note element is not found", true, "error", 4000);
notesName.value = note.name;
notesLegend.innerHTML = note.legend;
updateNotesBox(note);
if (window.tinymce) tinymce.activeEditor.setContent(note.legend);
}
function changeName() {
const note = notes.find(note => note.id === notesSelect.value);
if (!note) return tip("Note element is not found", true, "error", 4000);
note.name = this.value;
}
function validateHighlightElement() {
const element = byId(notesSelect.value);
if (element) return highlightElement(element, 3);
confirmationDialog({
title: "Element not found",
message: "Note element is not found. Would you like to remove the note?",
confirm: "Remove",
cancel: "Keep",
onConfirm: removeLegend
});
}
function openAiGenerator() {
const note = notes.find(note => note.id === notesSelect.value);
let prompt = `Respond with description. Use simple dry language. Invent facts, names and details. Split to paragraphs and format to HTML. Remove h tags, remove markdown.`;
if (note?.name) prompt += ` Name: ${note.name}.`;
if (note?.legend) prompt += ` Data: ${note.legend}`;
const onApply = result => {
notesLegend.innerHTML = result;
if (note) {
note.legend = result;
updateNotesBox(note);
if (window.tinymce) tinymce.activeEditor.setContent(note.legend);
}
};
geneateWithAi(prompt, onApply);
}
function downloadLegends() {
const notesData = JSON.stringify(notes);
const name = getFileName("Notes") + ".txt";
downloadFile(notesData, name);
}
function uploadLegends(dataLoaded) {
if (!dataLoaded) return tip("Cannot load the file. Please check the data format", false, "error");
notes = JSON.parse(dataLoaded);
notesSelect.options.length = 0;
editNotes(notes[0].id, notes[0].name);
}
function triggerNotesRemove() {
function removeLegend() {
notes = notes.filter(({id}) => id !== notesSelect.value);
if (!notes.length) {
$("#notesEditor").dialog("close");
return;
}
removeEditor();
editNotes(notes[0].id, notes[0].name);
}
confirmationDialog({
title: "Remove note",
message: "Are you sure you want to remove the selected note? There is no way to undo this action",
confirm: "Remove",
onConfirm: removeLegend
});
}
function toggleNotesPin() {
options.pinNotes = !options.pinNotes;
this.classList.toggle("pressed");
}
function removeEditor() {
if (window.tinymce) tinymce.remove();
}
}