-
-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
35 changed files
with
931 additions
and
435 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
Large diffs are not rendered by default.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
import type { BackgroundEvents as BackgroundEventHandlers } from "@/entrypoints/background/listeners"; | ||
import type { MarkmapRendererEvents as MarkmapRendererEventHandlers } from "@/features/plugins/_core/markmap-renderer/listeners.main"; | ||
import type { MermaidRendererEvents as MermaidRendererEventHandlers } from "@/features/plugins/_core/mermaid-renderer/listeners.main"; | ||
import type { InterceptorsEvents as NetworkInterceptInterceptorsEventHandlers } from "@/features/plugins/_core/network-intercept/listeners"; | ||
import type { ReactVdomEvents as ReactVdomEventHandlers } from "@/features/plugins/_core/react-vdom/listeners.main"; | ||
import type { DispatchEvents as SpaRouterDispatchEventHandlers } from "@/features/plugins/_core/spa-router/listeners"; | ||
import type { CsUtilEvents as SpaRouterCsUtilEventHandlers } from "@/features/plugins/_core/spa-router/listeners.main"; | ||
|
||
export type AllEventHandlers = BackgroundEventHandlers & | ||
NetworkInterceptInterceptorsEventHandlers & | ||
SpaRouterCsUtilEventHandlers & | ||
SpaRouterDispatchEventHandlers & | ||
ReactVdomEventHandlers & | ||
MermaidRendererEventHandlers; | ||
MermaidRendererEventHandlers & | ||
MarkmapRendererEventHandlers; |
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
169 changes: 169 additions & 0 deletions
169
src/features/plugins/_core/markmap-renderer/index.main.ts
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,169 @@ | ||
import throttle from "lodash/throttle"; | ||
import type { Markmap } from "markmap-view"; | ||
|
||
import { setupMarkmapRendererListeners } from "@/features/plugins/_core/markmap-renderer/listeners.main"; | ||
import { injectMainWorldScriptBlock } from "@/utils/utils"; | ||
|
||
export class MarkmapRenderer { | ||
private static instance: MarkmapRenderer | null = null; | ||
private importPromise: Promise<void> | null = null; | ||
|
||
private constructor() {} | ||
|
||
private currentMarkmapSvgNode: HTMLElement | null = null; | ||
private currentMarkmapInstance: Markmap | null = null; | ||
|
||
static getInstance() { | ||
if (!MarkmapRenderer.instance) { | ||
MarkmapRenderer.instance = new MarkmapRenderer(); | ||
} | ||
return MarkmapRenderer.instance; | ||
} | ||
|
||
initialize() { | ||
setupMarkmapRendererListeners(); | ||
$(() => this.importMarkmap()); | ||
} | ||
|
||
private async importMarkmap(): Promise<void> { | ||
if (!this.importPromise) { | ||
const scriptContent = ` | ||
import * as markmapLib from "https://cdn.jsdelivr.net/npm/markmap-lib/+esm"; | ||
import * as markmapView from "https://cdn.jsdelivr.net/npm/markmap-view/+esm"; | ||
import * as markmapRender from "https://cdn.jsdelivr.net/npm/markmap-render/+esm"; | ||
window.markmapLib = markmapLib; | ||
window.markmapView = markmapView; | ||
window.markmapRender = markmapRender; | ||
const transformer = new markmapLib.Transformer(); | ||
window.markmapTransformer = transformer; | ||
const { scripts, styles } = transformer.getAssets(); | ||
markmapView.loadCSS(styles); | ||
markmapView.loadJS(scripts, { getMarkmap: () => markmapView }); | ||
`; | ||
|
||
this.importPromise = injectMainWorldScriptBlock({ | ||
scriptContent, | ||
waitForExecution: true, | ||
}).catch((error) => { | ||
console.error("Failed to import Markmap:", error); | ||
throw error; | ||
}); | ||
} | ||
|
||
return this.importPromise; | ||
} | ||
|
||
isInitialized() { | ||
return this.importPromise?.then(() => true).catch(() => false); | ||
} | ||
|
||
handleRenderRequest = throttle( | ||
async ({ | ||
content, | ||
selector, | ||
}: { | ||
content: string; | ||
selector: string; | ||
}): Promise<{ success: boolean; error?: string }> => { | ||
const $target = $(selector); | ||
|
||
if ($target.length === 0) { | ||
console.warn("No elements found for rendering Markmap canvas"); | ||
return { | ||
success: false, | ||
error: "No elements found for rendering Markmap canvas", | ||
}; | ||
} | ||
|
||
try { | ||
await MarkmapRenderer.waitForInitialization(); | ||
|
||
const transformer = window.markmapTransformer!; | ||
|
||
const { root } = transformer.transform(content); | ||
|
||
if (!document.body.contains(this.currentMarkmapSvgNode)) { | ||
const $target = $(selector); | ||
|
||
this.currentMarkmapSvgNode = $target[0]; | ||
|
||
const { Markmap } = window.markmapView!; | ||
|
||
this.currentMarkmapInstance = Markmap.create( | ||
selector, | ||
{ | ||
duration: 50, | ||
autoFit: true, | ||
}, | ||
root, | ||
); | ||
} else { | ||
this.currentMarkmapInstance?.setData(root); | ||
this.currentMarkmapInstance?.fit(); | ||
} | ||
|
||
return { | ||
success: true, | ||
}; | ||
} catch (error) { | ||
return { | ||
success: false, | ||
error: (error as any).str, | ||
}; | ||
} | ||
}, | ||
500, | ||
{ | ||
leading: true, | ||
trailing: true, | ||
}, | ||
); | ||
|
||
private static getInteractiveHtml({ content }: { content: string }) { | ||
const fillTemplate = window.markmapRender!.fillTemplate; | ||
|
||
const { root, features } = window.markmapTransformer!.transform(content); | ||
const assets = window.markmapTransformer!.getUsedAssets(features); | ||
|
||
const html = fillTemplate(root, assets); | ||
|
||
return html; | ||
} | ||
|
||
static openAsInteractiveHtml({ content }: { content: string }) { | ||
const html = MarkmapRenderer.getInteractiveHtml({ content }); | ||
|
||
const blob = new Blob([html], { type: "text/html" }); | ||
const url = URL.createObjectURL(blob); | ||
|
||
window.open(url, "_blank"); | ||
} | ||
|
||
static downloadAsInteractiveHtml({ | ||
content, | ||
title, | ||
}: { | ||
content: string; | ||
title: string; | ||
}) { | ||
const html = MarkmapRenderer.getInteractiveHtml({ content }); | ||
|
||
const blob = new Blob([html], { type: "text/html" }); | ||
|
||
const url = URL.createObjectURL(blob); | ||
const a = document.createElement("a"); | ||
a.href = url; | ||
a.download = `${title}.html`; | ||
a.click(); | ||
} | ||
|
||
static async waitForInitialization() { | ||
while (!MarkmapRenderer.getInstance().isInitialized()) { | ||
await sleep(100); | ||
} | ||
} | ||
} | ||
|
||
MarkmapRenderer.getInstance().initialize(); |
51 changes: 51 additions & 0 deletions
51
src/features/plugins/_core/markmap-renderer/listeners.main.ts
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,51 @@ | ||
import { onMessage } from "webext-bridge/window"; | ||
|
||
import { MarkmapRenderer } from "@/features/plugins/_core/markmap-renderer/index.main"; | ||
|
||
export type MarkmapRendererEvents = { | ||
"markmapRenderer:isInitialized": () => boolean; | ||
"markmapRenderer:render": (params: { content: string; selector: string }) => { | ||
success: boolean; | ||
error?: string; | ||
}; | ||
"markmapRenderer:openAsInteractiveHtml": (params: { | ||
content: string; | ||
}) => void; | ||
"markmapRenderer:downloadAsInteractiveHtml": (params: { | ||
content: string; | ||
title: string; | ||
}) => void; | ||
}; | ||
|
||
export function setupMarkmapRendererListeners() { | ||
onMessage( | ||
"markmapRenderer:isInitialized", | ||
() => MarkmapRenderer.getInstance()?.isInitialized() ?? false, | ||
); | ||
|
||
onMessage("markmapRenderer:render", ({ data: { content, selector } }) => { | ||
return MarkmapRenderer.getInstance().handleRenderRequest({ | ||
content, | ||
selector, | ||
}); | ||
}); | ||
|
||
onMessage( | ||
"markmapRenderer:openAsInteractiveHtml", | ||
({ data: { content } }) => { | ||
return MarkmapRenderer.openAsInteractiveHtml({ | ||
content, | ||
}); | ||
}, | ||
); | ||
|
||
onMessage( | ||
"markmapRenderer:downloadAsInteractiveHtml", | ||
({ data: { content, title } }) => { | ||
return MarkmapRenderer.downloadAsInteractiveHtml({ | ||
content, | ||
title, | ||
}); | ||
}, | ||
); | ||
} |
Oops, something went wrong.