Problem: The first time you browse notes, it has to scan all 13,496 files,
which is very slow. This makes the initial user experience poor.
Solution: Pre-warm the vault file cache in the background:
1. When Obsidian connection is tested (first-time setup)
2. When FMG loads and Obsidian is already enabled (page reload)
Implementation:
- Add prewarmCache() function that scans vault in background
- Call it from testConnection() (don't await - runs async)
- Call it from init() if config.enabled is true
- Scan happens silently in the background
- By the time user clicks Browse, cache is already loaded
Benefits:
- First browse is instant (cache already loaded)
- Works on every page reload
- Non-blocking (doesn't slow down FMG startup)
- Silent/automatic (no user interaction needed)
User experience:
- First time: Test connection → cache warms in background → browse is instant
- Subsequent loads: Page loads → cache warms → browse is instant
- Cache lasts 5 minutes, so multiple browses within that window are all instant
EOF
)
jQuery UI dialog buttons don't work properly with async function handlers.
The Browse button was showing up blank/broken.
Changed Browse button handler from:
- async function with await
- to regular function with .then()/.catch()
This matches the pattern used for the Search button and should make all
three buttons (Search, Browse, Cancel) work properly.
jQuery UI dialog buttons don't work properly with emojis in the label text.
Removed emojis from button labels while keeping them in the dialog content.
Changed:
- '🔍 Search' → 'Search'
- '📁 Browse' → 'Browse'
The emojis are still visible in the dialog content boxes, just not in the
actual button labels.
Problem: When clicking on a burg/marker, automatic search would run
immediately without giving the user an option to skip it and browse
manually. Users had to wait for the search to complete before getting
to the browse option.
Solution: Add initial dialog (showSearchMethodDialog) that appears
first, asking the user to choose:
- 🔍 Search: Run automatic search (FMG ID lookup + coordinate search)
- 📁 Browse: Skip search and go straight to manual browse/folder tree
- Cancel: Close without doing anything
Benefits:
- Users who want to browse manually can do so immediately
- Users who want automatic search can still use it
- No forced waiting for search when you know you want to browse
- Clear visual distinction between the two methods
- Folder tree is now just one click away
This gives users complete control from the very first interaction,
making manual browsing much more accessible.
Problem: When a single note was found by coordinates, it would
automatically load that note without giving the user any choice.
This removed the ability to manually browse/search for a different note.
Solution: Add showSingleMatchDialog() that appears when one note is
found by coordinates, giving the user 3 options:
1. "Use This Note" - Use the matched note (default/fast path)
2. "Browse/Search" - Manually browse/search for a different note
3. "Cancel" - Close without doing anything
This gives users control over note selection in all scenarios:
- Linked note found (via FMG ID) → Can choose different note
- Single match by coordinates → Can choose different note
- Multiple matches → Selection dialog (already had manual option)
- No matches → Browse/search/create dialog
Now users always have the option to manually browse, regardless of
what the automatic search finds.
Problem: When clicking on a burg/marker that already has a linked note,
it would automatically open that note without giving the user any choice.
Users need the ability to manually browse/search for a different note.
Solution: Show a dialog when a linked note is found with 3 options:
1. "Open Linked Note" - Open the currently linked note (default/fast path)
2. "Choose Different Note" - Browse/search for a different note to link
3. "Cancel" - Close the dialog
Benefits:
- Gives users full control over note selection
- Still makes the common case (opening linked note) fast and easy
- Allows re-linking burgs/markers to different notes
- Shows the path of the linked note before opening
This addresses the need to manually browse even when a note is already
linked, while preserving the fast instant-open behavior for linked notes.
Problem: Every time you click on a burg/marker, it had to scan through
all 13,496 vault files looking for a matching fmg-id. This was extremely
slow and made the feature unusable for large vaults.
Solution: Implement a smart index that maps fmg-id to file paths.
How it works:
- Index stored in memory (fmgIdIndex object)
- Persisted to localStorage (survives page reloads)
- Loaded on init, saved on changes
- When looking up by fmg-id:
1. Check index first (instant!)
2. If found, verify file still exists and ID matches
3. If not in index, search vault and add to index
- Automatically updates when notes are created/saved through FMG
- Handles stale entries (file deleted/modified)
Performance improvement:
- Before: O(n) - scan all 13k files (very slow)
- After: O(1) - instant lookup from index
- First click on burg: May need to search (builds index)
- Second click on same burg: Instant! Opens note directly
This makes the Obsidian integration actually usable. Create a note once
for a burg, and every time you click that burg again, it opens instantly.
The recursive directory scan was running every time the user opened
the note browser, rescanning 13k+ files repeatedly. This was slow
and wasteful.
Changes:
- Add vaultFilesCache object with files, timestamp, and TTL (5 min)
- Check cache before scanning in getVaultFiles()
- Cache results after successful scan
- Add clearVaultCache() function for manual refresh
- Add forceRefresh parameter to bypass cache
- Log cache hits vs misses for debugging
Performance:
- First load: Scans vault (slow, but only once)
- Subsequent loads: Instant (uses cache)
- Cache expires after 5 minutes (auto-refresh)
- User can manually clear cache if needed
This makes opening the note browser instant on subsequent uses.
The previous implementation read ALL file contents to get frontmatter
when browsing notes, which caused massive slowdown for large vaults
(13k+ files = 13k+ HTTP requests).
Changes:
- Add listAllNotePaths() function that only gets file paths (fast)
- Build folder tree from paths only, no content reading
- Lazy load file content only when user clicks on a file
- Change message from "Loading all notes" to "Loading file list"
- Add logging to show file count being displayed
Performance improvement:
- Before: O(n) HTTP requests where n = number of files
- After: O(1) - just the recursive directory scan
- File content loaded on-demand (1 request per clicked file)
This makes the folder tree instant even for vaults with 10k+ files.
The Obsidian Local REST API /vault/ endpoint returns folder entries with
trailing slashes (e.g., "01 Projects/"). The previous code filtered these
out, only keeping .md files, which meant nested files were never discovered.
Changes:
- Add scanDirectory() function to recursively scan folders
- Detect folders by trailing "/" character
- Recursively query each folder with /vault/{foldername}/
- Build full paths as we traverse (e.g., "State/Province/City.md")
- Remove search endpoint attempt (not needed)
- Add timing logs to measure performance
This now correctly discovers ALL markdown files in the vault, regardless
of how deeply nested they are in folders.
Fixes nested folder display issue.
- Try /search/ endpoint first to get all .md files recursively
- Fall back to /vault/ endpoint if search doesn't work
- Add warning when using /vault/ that nested folders may not be visible
- Handle multiple response formats from search endpoint
- Add debug logging to getVaultFiles() to show file counts
- Add logging to listAllNotes() to show processing details
- Add logging to buildFolderTree() to trace folder creation
- Filter .md files in getVaultFiles() for better performance
- Log sample file paths to help diagnose API response format
This will help identify why nested folders aren't appearing in the
folder tree browser.
Replaced flat list with hierarchical folder tree structure for better
navigation when browsing vault notes.
**Features:**
- Collapsible folders with ▼/▶ toggle arrows
- Proper indentation showing folder hierarchy
- 📁 folder and 📄 file icons
- Click folder name to expand/collapse
- Click file to select it
- Handles root-level files and nested folders
- Hover highlights for files
**Functions added:**
- buildFolderTree(): Converts flat note list to tree structure
- renderFolderTree(): Recursively renders folders with nesting
- renderFiles(): Renders files at current folder level
Perfect for vaults organized like:
```
State1/
Province1/
City1.md
City2.md
Province2/
City3.md
State2/
Province3/
City4.md
```
Much easier to navigate than a flat list of 100+ notes!
When a user selects or creates a note for a burg/marker, the system now
automatically links them together by adding/updating the note's frontmatter.
**How it works:**
1. **When saving a note**:
- Adds `fmg-id: burg123` to frontmatter
- Updates `x:` and `y:` coordinates
- Preserves existing frontmatter fields
2. **When creating a new note**:
- Template includes fmg-id from the start
- Uses the proper elementId (e.g., "burg123")
3. **Next time you edit**:
- System finds note instantly by fmg-id
- No need to search by coordinates again
- Permanent two-way link established
**Changes:**
- obsidian-notes-editor.js:
- Pass elementId and coordinates to showMarkdownEditor()
- updateFrontmatterWithFmgData() injects fmg-id on save
- Automatically updates coordinates to keep in sync
- obsidian-bridge.js:
- generateNoteTemplate() accepts elementId parameter
- Uses elementId for fmg-id instead of element.id
Users no longer need to manually match notes - once linked, the
association is permanent in the note's frontmatter.
Enhanced the Obsidian note finding system with:
**ObsidianBridge:**
- searchNotes(query): Search vault by filename
- listAllNotes(): Browse all notes in vault
**Obsidian Notes Editor:**
- Text search box with auto-populated state/province
- "Browse All Notes" button to see full vault
- Display burg's state and province in dialog
- Pre-fill search with state name for easier finding
- Enter key triggers search
- Click any result to load that note
**getElementData enhancement:**
- Extract and include state/province names for burgs
- Extract state/province from cell data for markers
Now users can:
1. See which state/province the burg belongs to
2. Search by state name (pre-filled)
3. Search by any text in filename
4. Browse all notes manually
5. Click to select matching note
This addresses the user's organization structure where notes are
stored in State/Province folders matching the map structure.
The editObsidianNote function expects elementId as a string (e.g. 'burg123')
but was receiving a number. This caused elementId.replace() to fail.
Fixed in both:
- burg-editor.js: Pass 'burg' + id instead of id
- markers-editor.js: Pass id (already a string) instead of marker.i
Replace the old TinyMCE notes editor with Obsidian integration in:
- Burg editor "Edit note" button
- Marker editor "Edit note" button
Changes:
- modules/ui/burg-editor.js: Update editBurgLegend() to call editObsidianNote()
- modules/ui/markers-editor.js: Update editMarkerLegend() to call editObsidianNote()
- Both functions fall back to old editNotes() if Obsidian is not configured
- index.html: Update version hashes to 1.108.13
Now when users click "Edit note" on a burg or marker, they get the modern
Markdown editor with Obsidian vault integration instead of the old WYSIWYG.
The default map feature was not working on page refresh because the
onloadBehavior dropdown value was not being persisted or restored.
Changes:
- main.js: Restore onloadBehavior from localStorage on page load
- save.js: saveAsDefaultMap() now saves "default" to localStorage
- save.js: clearDefaultMap() now removes the localStorage setting
- index.html: Update version hashes to 1.108.13
Now when users click "Set as default", the setting persists across
page refreshes and their default map loads automatically.
Add typeof checks for all function calls to modules loaded with defer attribute.
This prevents ReferenceError when loading old maps (1.108.11) in version 1.108.13.
Functions protected:
- getCurrentPreset() - from style.js
- addCustomColorScheme() - from style.js
- updateTextureSelectValue() - from style.js
- focusOn() - from editors.js
- invokeActiveZooming() - from zoom.js
- fitMapToScreen() - from zoom.js
- declareFont() - from fonts.js
- moveBurgToGroup() - from burgs.js (3 locations)
This fixes zoom/pan issues when loading old maps.
Add comprehensive Obsidian integration as intermediate step toward
PostgreSQL migration, enabling modern Markdown-based note editing:
**Features:**
- Obsidian Local REST API integration for vault access
- Coordinate-based note matching (searches vault YAML frontmatter)
- Shows top 5-8 closest matches when clicking burgs/markers
- Modern Markdown editor with live preview
- [[Wikilink]] support for connecting notes
- "Open in Obsidian" button to jump to native app
- Configuration UI for API setup and testing
**Technical Implementation:**
- modules/io/obsidian-bridge.js - Core API integration layer
- modules/ui/obsidian-notes-editor.js - Markdown editor UI
- modules/ui/obsidian-config.js - Configuration panel
- OBSIDIAN_INTEGRATION.md - Complete setup/usage guide
**Coordinate Matching:**
- Parses YAML frontmatter for x/y coordinates
- Calculates distance to clicked element
- Supports nested (coordinates.x) and flat (x:) formats
- Handles missing FMG IDs (common with PostgreSQL imports)
**User Workflow:**
1. Configure Obsidian REST API connection
2. Click burg/marker in FMG
3. System finds matching notes by coordinates
4. Select note or create new one
5. Edit in modern Markdown editor
6. Save syncs to Obsidian vault instantly
This replaces the "Win95-style TinyMCE" editor with a clean,
modern Markdown experience while maintaining compatibility with
the eventual PostgreSQL backend migration. Users can edit notes
in either FMG or Obsidian - both stay in sync via file system.
Version: 1.108.13
Add ability to set a specific map as the default that opens on load:
- Add 'Open default map' option to onload behavior dropdown
- Implement saveAsDefaultMap() to store map as default in IndexedDB
- Implement clearDefaultMap() to remove default map setting
- Modify checkLoadParameters() to load default map when configured
- Add UI buttons in Save dialog for setting/clearing default map
- Update version to 1.108.12 and hash in index.html
Users can now:
1. Open any map they want as default
2. Go to Options > Onload behavior > Select "Open default map"
3. Save > Click "Set as default" button
4. The map will now open automatically every time
This sets the foundation for the planned time-based worldbuilding
and lore database features by ensuring users always start with
their primary world map.
Add detailed documentation to help AI assistants understand and work
with the Fantasy Map Generator codebase, including:
- Project architecture and technology stack
- Directory structure and file organization
- Data model and core concepts (pack object, Voronoi mesh)
- Code conventions and module patterns
- Development workflow and versioning process
- Common development tasks and patterns
- Performance considerations and best practices
- Troubleshooting guide and quick reference
This guide provides AI assistants with essential context about the
codebase's unique characteristics (no build system, global object
pattern, typed arrays, D3.js rendering) to enable more effective
contributions.