diff --git a/index.html b/index.html index 1a7b2bd0..68ad5fae 100644 --- a/index.html +++ b/index.html @@ -8281,8 +8281,8 @@ - - + + diff --git a/modules/io/obsidian-bridge.js b/modules/io/obsidian-bridge.js index 1d8325fc..a2874726 100644 --- a/modules/io/obsidian-bridge.js +++ b/modules/io/obsidian-bridge.js @@ -343,13 +343,13 @@ const ObsidianBridge = (() => { } // Generate note template for FMG element - function generateNoteTemplate(element, type) { + function generateNoteTemplate(element, type, elementId) { const {x, y} = element; const lat = pack.cells.lat?.[element.cell] || 0; const lon = pack.cells.lon?.[element.cell] || 0; const frontmatter = { - "fmg-id": element.id || `${type}${element.i}`, + "fmg-id": elementId || element.id || `${type}${element.i}`, "fmg-type": type, coordinates: {x, y, lat, lon}, tags: [type], @@ -396,6 +396,66 @@ Add your lore here... `; } + // Search notes by text query (searches in filename and frontmatter) + async function searchNotes(query) { + if (!query || query.trim() === "") { + return []; + } + + const allFiles = await getVaultFiles(); + const searchTerm = query.toLowerCase(); + const results = []; + + for (const filePath of allFiles) { + const fileName = filePath.split("/").pop().replace(".md", "").toLowerCase(); + + // Check if filename matches + if (fileName.includes(searchTerm)) { + try { + const content = await getNote(filePath); + const {frontmatter} = parseFrontmatter(content); + + results.push({ + path: filePath, + name: filePath.split("/").pop().replace(".md", ""), + frontmatter, + matchType: "filename" + }); + } catch (error) { + WARN && console.warn(`Could not read file ${filePath}:`, error); + } + } + } + + return results; + } + + // List all notes with basic info + async function listAllNotes() { + const allFiles = await getVaultFiles(); + const notes = []; + + for (const filePath of allFiles) { + try { + const content = await getNote(filePath); + const {frontmatter} = parseFrontmatter(content); + + notes.push({ + path: filePath, + name: filePath.split("/").pop().replace(".md", ""), + frontmatter, + folder: filePath.includes("/") ? filePath.substring(0, filePath.lastIndexOf("/")) : "" + }); + } catch (error) { + WARN && console.warn(`Could not read file ${filePath}:`, error); + } + } + + // Sort by path + notes.sort((a, b) => a.path.localeCompare(b.path)); + return notes; + } + return { init, config, @@ -408,7 +468,9 @@ Add your lore here... parseFrontmatter, findNotesByCoordinates, findNoteByFmgId, - generateNoteTemplate + generateNoteTemplate, + searchNotes, + listAllNotes }; })(); diff --git a/modules/ui/obsidian-notes-editor.js b/modules/ui/obsidian-notes-editor.js index 78fa133e..60b93386 100644 --- a/modules/ui/obsidian-notes-editor.js +++ b/modules/ui/obsidian-notes-editor.js @@ -11,7 +11,7 @@ function editObsidianNote(elementId, elementType, coordinates) { // Try to find note by FMG ID first, then by coordinates findOrCreateNote(elementId, elementType, coordinates) .then(noteData => { - showMarkdownEditor(noteData, elementType); + showMarkdownEditor(noteData, elementType, elementId, coordinates); }) .catch(error => { ERROR && console.error("Failed to load note:", error); @@ -153,21 +153,49 @@ async function promptCreateNewNote(elementId, elementType, coordinates) { const element = getElementData(elementId, elementType); const suggestedName = element.name || `${elementType}-${element.i}`; + // Build context info for the element + let contextInfo = ""; + if (element.state) { + contextInfo += `
No matching notes found. Create a new note in your Obsidian vault?
-${element.name || elementId}
+ ${contextInfo} +No matching notes found by coordinates.
Or create a new note:
+Enter a search term
"; + return; + } + + resultsDiv.innerHTML = "Searching...
"; + + try { + const results = await ObsidianBridge.searchNotes(query); + + if (results.length === 0) { + resultsDiv.innerHTML = "No matching notes found
"; + return; + } + + resultsDiv.innerHTML = results + .map( + (note, index) => ` +Search failed: ${error.message}
`; + } + }; + + const showBrowse = async () => { + resultsDiv.innerHTML = "Loading all notes...
"; + + try { + const allNotes = await ObsidianBridge.listAllNotes(); + + if (allNotes.length === 0) { + resultsDiv.innerHTML = "No notes in vault
"; + return; + } + + resultsDiv.innerHTML = allNotes + .map( + (note, index) => ` +Failed to load notes: ${error.message}
`; + } + }; + + searchBtn.addEventListener("click", performSearch); + browseBtn.addEventListener("click", showBrowse); + searchInput.addEventListener("keypress", e => { + if (e.key === "Enter") performSearch(); + }); }); } @@ -213,10 +363,35 @@ function getElementData(elementId, elementType) { // Extract element data based on type if (elementType === "burg") { const burgId = parseInt(elementId.replace("burg", "")); - return pack.burgs[burgId]; + const burg = pack.burgs[burgId]; + + // Enhance with state and province names + const stateId = burg.state; + const provinceId = burg.province; + + return { + ...burg, + state: stateId && pack.states[stateId] ? pack.states[stateId].name : null, + province: provinceId && pack.provinces[provinceId] ? pack.provinces[provinceId].name : null + }; } else if (elementType === "marker") { const markerId = parseInt(elementId.replace("marker", "")); - return pack.markers[markerId]; + const marker = pack.markers[markerId]; + + // Enhance with state and province if marker has a cell + if (marker.cell) { + const cell = pack.cells; + const stateId = cell.state[marker.cell]; + const provinceId = cell.province[marker.cell]; + + return { + ...marker, + state: stateId && pack.states[stateId] ? pack.states[stateId].name : null, + province: provinceId && pack.provinces[provinceId] ? pack.provinces[provinceId].name : null + }; + } + + return marker; } else { // Generic element const el = document.getElementById(elementId); @@ -229,7 +404,7 @@ function getElementData(elementId, elementType) { } } -function showMarkdownEditor(noteData, elementType) { +function showMarkdownEditor(noteData, elementType, elementId, coordinates) { const {path, name, content, frontmatter, isNew} = noteData; // Extract frontmatter and body @@ -241,9 +416,12 @@ function showMarkdownEditor(noteData, elementType) { byId("obsidianMarkdownEditor").value = content; byId("obsidianMarkdownPreview").innerHTML = renderMarkdown(bodyContent); - // Store current note data + // Store current note data and FMG element info showMarkdownEditor.currentNote = noteData; showMarkdownEditor.originalContent = content; + showMarkdownEditor.elementId = elementId; + showMarkdownEditor.elementType = elementType; + showMarkdownEditor.coordinates = coordinates; $("#obsidianNotesEditor").dialog({ title: `Obsidian Note: ${name}`, @@ -316,19 +494,55 @@ async function saveObsidianNote() { return; } - const content = byId("obsidianMarkdownEditor").value; + let content = byId("obsidianMarkdownEditor").value; const {path} = showMarkdownEditor.currentNote; + const elementId = showMarkdownEditor.elementId; + const coordinates = showMarkdownEditor.coordinates; + + // Update/add frontmatter with FMG ID and coordinates + if (elementId && coordinates) { + content = updateFrontmatterWithFmgData(content, elementId, coordinates); + } try { await ObsidianBridge.updateNote(path, content); showMarkdownEditor.originalContent = content; - tip("Note saved to Obsidian vault", true, "success", 2000); + // Update the editor to show the new frontmatter + byId("obsidianMarkdownEditor").value = content; + tip("Note saved to Obsidian vault (linked to FMG element)", true, "success", 3000); } catch (error) { ERROR && console.error("Failed to save note:", error); tip("Failed to save note: " + error.message, true, "error", 5000); } } +function updateFrontmatterWithFmgData(content, elementId, coordinates) { + const {x, y} = coordinates; + const {frontmatter, content: bodyContent} = ObsidianBridge.parseFrontmatter(content); + + // Update frontmatter with FMG data + frontmatter["fmg-id"] = elementId; + frontmatter["x"] = Math.round(x * 100) / 100; + frontmatter["y"] = Math.round(y * 100) / 100; + + // Rebuild frontmatter + let frontmatterLines = ["---"]; + for (const [key, value] of Object.entries(frontmatter)) { + if (typeof value === "object" && value !== null) { + // Handle nested objects + frontmatterLines.push(`${key}:`); + for (const [nestedKey, nestedValue] of Object.entries(value)) { + frontmatterLines.push(` ${nestedKey}: ${nestedValue}`); + } + } else { + frontmatterLines.push(`${key}: ${value}`); + } + } + frontmatterLines.push("---"); + + return frontmatterLines.join("\n") + "\n" + bodyContent; +} + function openInObsidian() { if (!showMarkdownEditor.currentNote) return;