use wikilinks for images
This commit is contained in:
parent
39c4486475
commit
abeb618c12
|
|
@ -164,6 +164,10 @@ function noteApp() {
|
||||||
byEndPath: new Map(), // '/filename' and '/filename.md' -> true
|
byEndPath: new Map(), // '/filename' and '/filename.md' -> true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Image lookup map for O(1) image wikilink resolution (built on loadNotes)
|
||||||
|
// Maps image filename (case-insensitive) -> full path
|
||||||
|
_imageLookup: new Map(),
|
||||||
|
|
||||||
// Preview rendering debounce
|
// Preview rendering debounce
|
||||||
_previewDebounceTimeout: null,
|
_previewDebounceTimeout: null,
|
||||||
_lastRenderedContent: '',
|
_lastRenderedContent: '',
|
||||||
|
|
@ -1004,12 +1008,25 @@ function noteApp() {
|
||||||
this._noteLookup.byName.clear();
|
this._noteLookup.byName.clear();
|
||||||
this._noteLookup.byNameLower.clear();
|
this._noteLookup.byNameLower.clear();
|
||||||
this._noteLookup.byEndPath.clear();
|
this._noteLookup.byEndPath.clear();
|
||||||
|
this._imageLookup.clear();
|
||||||
|
|
||||||
for (const note of this.notes) {
|
for (const note of this.notes) {
|
||||||
const path = note.path;
|
const path = note.path;
|
||||||
const pathLower = path.toLowerCase();
|
const pathLower = path.toLowerCase();
|
||||||
const name = note.name;
|
const name = note.name;
|
||||||
const nameLower = name.toLowerCase();
|
const nameLower = name.toLowerCase();
|
||||||
|
|
||||||
|
// Handle images separately - build image lookup map
|
||||||
|
if (note.type === 'image') {
|
||||||
|
// Map filename (case-insensitive) to full path
|
||||||
|
// First match wins if there are duplicates
|
||||||
|
if (!this._imageLookup.has(nameLower)) {
|
||||||
|
this._imageLookup.set(nameLower, path);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notes only from here
|
||||||
const nameWithoutMd = name.replace(/\.md$/i, '');
|
const nameWithoutMd = name.replace(/\.md$/i, '');
|
||||||
const nameWithoutMdLower = nameWithoutMd.toLowerCase();
|
const nameWithoutMdLower = nameWithoutMd.toLowerCase();
|
||||||
|
|
||||||
|
|
@ -1046,6 +1063,13 @@ function noteApp() {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Resolve image wikilink to full path (O(1) lookup)
|
||||||
|
// Returns the full path if found, null otherwise
|
||||||
|
resolveImageWikilink(imageName) {
|
||||||
|
const nameLower = imageName.toLowerCase();
|
||||||
|
return this._imageLookup.get(nameLower) || null;
|
||||||
|
},
|
||||||
|
|
||||||
// Load all tags
|
// Load all tags
|
||||||
async loadTags() {
|
async loadTags() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -1994,10 +2018,9 @@ function noteApp() {
|
||||||
|
|
||||||
let link;
|
let link;
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
// For images, insert image markdown
|
// For images, use wiki-style link (resolves by filename, never breaks)
|
||||||
const filename = notePath.split('/').pop().replace(/\.[^/.]+$/, ''); // Remove extension
|
const filename = notePath.split('/').pop();
|
||||||
// Use relative path (not /api/images/) for portability
|
link = `![[${filename}]]`;
|
||||||
link = ``;
|
|
||||||
} else {
|
} else {
|
||||||
// For notes, insert note link
|
// For notes, insert note link
|
||||||
const noteName = notePath.split('/').pop().replace('.md', '');
|
const noteName = notePath.split('/').pop().replace('.md', '');
|
||||||
|
|
@ -2084,11 +2107,21 @@ function noteApp() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Insert image markdown at cursor position
|
// Insert image markdown at cursor position using wiki-style syntax
|
||||||
|
// This ensures image links don't break when notes are moved
|
||||||
insertImageMarkdown(imagePath, altText, cursorPos) {
|
insertImageMarkdown(imagePath, altText, cursorPos) {
|
||||||
const filename = altText.replace(/\.[^/.]+$/, ''); // Remove extension
|
// Extract just the filename from the path (e.g., "folder/_attachments/image.png" -> "image.png")
|
||||||
// Use relative path (not /api/images/) for portability
|
const filename = imagePath.split('/').pop();
|
||||||
const markdown = ``;
|
|
||||||
|
// Use wiki-style image link: ![[filename.png]] or ![[filename.png|alt text]]
|
||||||
|
// The alt text is optional - only add if different from filename
|
||||||
|
const filenameWithoutExt = filename.replace(/\.[^/.]+$/, '');
|
||||||
|
const altWithoutExt = altText.replace(/\.[^/.]+$/, '');
|
||||||
|
|
||||||
|
// If alt text is meaningful (not just "pasted-image"), include it
|
||||||
|
const markdown = (altWithoutExt && altWithoutExt !== filenameWithoutExt && !altWithoutExt.startsWith('pasted-image'))
|
||||||
|
? `![[${filename}|${altWithoutExt}]]`
|
||||||
|
: `![[${filename}]]`;
|
||||||
|
|
||||||
const textBefore = this.noteContent.substring(0, cursorPos);
|
const textBefore = this.noteContent.substring(0, cursorPos);
|
||||||
const textAfter = this.noteContent.substring(cursorPos);
|
const textAfter = this.noteContent.substring(cursorPos);
|
||||||
|
|
@ -2098,7 +2131,7 @@ function noteApp() {
|
||||||
// Trigger autosave
|
// Trigger autosave
|
||||||
this.autoSave();
|
this.autoSave();
|
||||||
|
|
||||||
// Reload notes to show the new image in sidebar
|
// Reload notes to show the new image in sidebar and update lookup maps
|
||||||
this.loadNotes();
|
this.loadNotes();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -3763,7 +3796,38 @@ function noteApp() {
|
||||||
return `\x00CODEBLOCK${codeBlocks.length - 1}\x00`;
|
return `\x00CODEBLOCK${codeBlocks.length - 1}\x00`;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 2: Convert wiki links (now safe from code blocks)
|
// Step 2: Convert image wikilinks FIRST: ![[image.png]] or ![[image.png|alt text]]
|
||||||
|
// Must be before note wikilinks to prevent [[image.png]] from being matched first
|
||||||
|
contentToRender = contentToRender.replace(
|
||||||
|
/!\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g,
|
||||||
|
(match, imageName, altText) => {
|
||||||
|
const filename = imageName.trim();
|
||||||
|
const alt = altText ? altText.trim() : filename.replace(/\.[^/.]+$/, '');
|
||||||
|
|
||||||
|
// Resolve image path using O(1) lookup
|
||||||
|
const imagePath = self.resolveImageWikilink(filename);
|
||||||
|
|
||||||
|
if (imagePath) {
|
||||||
|
// URL-encode path segments for the API
|
||||||
|
const encodedPath = imagePath.split('/').map(segment => {
|
||||||
|
try {
|
||||||
|
return encodeURIComponent(decodeURIComponent(segment));
|
||||||
|
} catch (e) {
|
||||||
|
return encodeURIComponent(segment);
|
||||||
|
}
|
||||||
|
}).join('/');
|
||||||
|
|
||||||
|
const safeAlt = alt.replace(/"/g, '"');
|
||||||
|
return `<img src="/api/images/${encodedPath}" alt="${safeAlt}" title="${safeAlt}">`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image not found - return broken image indicator
|
||||||
|
const safeFilename = filename.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||||
|
return `<span class="wikilink-broken" title="Image not found">🖼️ ${safeFilename}</span>`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 2b: Convert note wikilinks: [[note]] or [[note|display text]]
|
||||||
contentToRender = contentToRender.replace(
|
contentToRender = contentToRender.replace(
|
||||||
/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g,
|
/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g,
|
||||||
(match, target, displayText) => {
|
(match, target, displayText) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue