forked from TuanManhCao/digital-garden
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add embed link support (TuanManhCao#25)
* fix: image data handling issue * refactor: avoid loading unnecessary file data * refactor: replace paragraph element tag to div * chore: add kotlin codes * refactor: remove unused imports * feat: add embed link support * docs: add embed link in Features
- Loading branch information
Showing
18 changed files
with
789 additions
and
598 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
kotlin/src/main/kotlin/markdown/parser/ObsidianEmbedLinkParser.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package markdown.parser | ||
|
||
import markdown.type.ObsidianElementTypes | ||
import org.intellij.markdown.MarkdownTokenTypes | ||
import org.intellij.markdown.parser.sequentialparsers.RangesListBuilder | ||
import org.intellij.markdown.parser.sequentialparsers.SequentialParser | ||
import org.intellij.markdown.parser.sequentialparsers.TokensCache | ||
|
||
class ObsidianEmbedLinkParser : SequentialParser { | ||
override fun parse(tokens: TokensCache, rangesToGlue: List<IntRange>): SequentialParser.ParsingResult { | ||
var result = SequentialParser.ParsingResultBuilder() | ||
val delegateIndices = RangesListBuilder() | ||
var iterator: TokensCache.Iterator = tokens.RangesListIterator(rangesToGlue) | ||
while (iterator.type != null) { | ||
val isTargetFile = iterator.type == MarkdownTokenTypes.EXCLAMATION_MARK && | ||
iterator.rawLookup(1) == MarkdownTokenTypes.LBRACKET && | ||
iterator.rawLookup(2) == MarkdownTokenTypes.LBRACKET | ||
if (isTargetFile) { | ||
// ![>>[<<link]] | ||
iterator = iterator.advance().advance() | ||
val target = ObsidianLinkParser.parseTarget(iterator) | ||
if (target != null) { | ||
result = result.withNode( | ||
SequentialParser.Node( | ||
// Target >>![[link]]<< | ||
iterator.index - 2..target.iteratorPosition.index + 1, | ||
ObsidianElementTypes.EMBED_LINK, | ||
), | ||
) | ||
iterator = target.iteratorPosition.advance() | ||
continue | ||
} | ||
} | ||
|
||
delegateIndices.put(iterator.index) | ||
iterator = iterator.advance() | ||
} | ||
|
||
return result.withFurtherProcessing(delegateIndices.get()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
112 changes: 112 additions & 0 deletions
112
kotlin/src/main/kotlin/markdown/processor/element/ObsidianEmbedLinkProcessor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package markdown.processor.element | ||
|
||
import DependencyData | ||
import FileNameInfo | ||
import FileNameString | ||
import SlugString | ||
import csstype.ClassName | ||
import embedTargets | ||
import external.CodeEncoder | ||
import external.MermaidRender | ||
import external.NextRouter | ||
import markdown.LeafVisitor | ||
import markdown.TagConsumer | ||
import markdown.convertMarkdownToReactElement | ||
import org.intellij.markdown.ast.ASTNode | ||
import org.intellij.markdown.html.LinkGeneratingProvider | ||
import org.intellij.markdown.html.URI | ||
import org.w3c.dom.HTMLElement | ||
import react.ChildrenBuilder | ||
import react.IntrinsicType | ||
import react.dom.html.HTMLAttributes | ||
import react.dom.html.ReactHTML.audio | ||
import react.dom.html.ReactHTML.div | ||
import react.dom.html.ReactHTML.img | ||
import react.dom.html.ReactHTML.strong | ||
|
||
/** | ||
* See [Official Help about EmbeddingLink](https://help.obsidian.md/Linking+notes+and+files/Embedding+files) | ||
*/ | ||
class ObsidianEmbedLinkProcessor<Parent>( | ||
baseURI: URI?, | ||
router: NextRouter?, | ||
fileName: FileNameString, | ||
dependencyData: DependencyData, | ||
fileNameInfo: FileNameInfo, | ||
private val codeEncoder: CodeEncoder?, | ||
private val mermaidRender: MermaidRender?, | ||
resolveAnchors: Boolean = false, | ||
) : LinkElementProcessor<Parent>(baseURI, router, fileName, dependencyData, fileNameInfo, resolveAnchors) | ||
where Parent : HTMLAttributes<HTMLElement>, Parent : ChildrenBuilder { | ||
private val obsidianLinkProcessor = ObsidianLinkElementProcessor<Parent>(baseURI, router, fileName, dependencyData, fileNameInfo, resolveAnchors) | ||
override fun getRenderInfo(markdownText: String, node: ASTNode): LinkGeneratingProvider.RenderInfo { | ||
return obsidianLinkProcessor.getRenderInfo(markdownText, node) | ||
} | ||
|
||
override fun <Visitor> renderLink(visitor: Visitor, markdownText: String, node: ASTNode, info: LinkGeneratingProvider.RenderInfo) where Visitor : TagConsumer<IntrinsicType<HTMLAttributes<HTMLElement>>, Parent>, Visitor : org.intellij.markdown.ast.visitors.Visitor, Visitor : LeafVisitor { | ||
val destination = info.destination.toString() | ||
// [internalDestination] specifies the display range for markdown and pdf | ||
val (rawUrl, internalDestination) = destination.split('#').let { it.first() to it.getOrNull(1) } | ||
// [url] may point to media content in the public folder | ||
val url = when (val resolvedDestination = resolveUrl(rawUrl)) { | ||
is Destination.Router -> resolvedDestination.slug.slug | ||
is Destination.RawLink -> { | ||
println("Warn: Failed to get slug data. target:$rawUrl") | ||
"/${resolvedDestination.link}" | ||
} | ||
} | ||
val sizeOption = info.title | ||
// for image link option | ||
val (imageWidth, imageHeight) = if (sizeOption != null && destination != sizeOption) { | ||
sizeOption.split('x').let { it.first().toDouble() to it.getOrNull(1)?.toDouble() } | ||
} else { | ||
null to null | ||
} | ||
val targetFile = FileNameString(destination) | ||
when { | ||
targetFile.isImageFile -> visitor.consume { | ||
img { | ||
src = url | ||
alt = destination | ||
if (imageWidth != null) { | ||
width = imageWidth | ||
} | ||
if (imageWidth != null) { | ||
height = imageHeight | ||
} | ||
} | ||
} | ||
targetFile.isSoundFile -> visitor.consume { | ||
audio { | ||
controls = true | ||
controlsList = "nodownload" | ||
src = url | ||
} | ||
} | ||
// markdown file | ||
// TODO support internalDestination | ||
else -> { | ||
val targetFileName = SlugString(url).toFileName(fileNameInfo.duplicatedFile) | ||
if (targetFileName == fileName) { | ||
println("Warning: Embed loop is detected in ${fileName.fileName}") | ||
return | ||
} | ||
if (embedTargets != null) { | ||
embedTargets?.add(targetFile) | ||
} else { | ||
val content = dependencyData.embedContents[targetFileName] ?: "" | ||
val element = convertMarkdownToReactElement(targetFileName, content, dependencyData, fileNameInfo, router, codeEncoder, mermaidRender) | ||
visitor.consume { | ||
div { | ||
className = ClassName("border-l-2 px-4 border-gray-300 dark:border-gray-600") | ||
strong { | ||
+targetFileName.fileName | ||
} | ||
element() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.