+/**
+ * A live fenced block of UidMarkdown: the renderer's widget mounted into a
+ * plain element. A new source goes to the widget's `update`; without one, the
+ * widget is mounted again.
+ */
+import { onBeforeUnmount, onMounted, shallowRef, watch } from 'vue'
+import type { UidMarkdownBlockMountHandle, UidMarkdownBlockResult } from './blockRenderers.js'
+
+const props = defineProps<{
+ code: string
+ language: string
+ mount: NonNullable
+}>()
+
+const el = shallowRef(null)
+let handle: UidMarkdownBlockMountHandle | null = null
+
+function mountWidget(): void {
+ if (!el.value) return
+ el.value.replaceChildren()
+ const out = props.mount(el.value, {
+ code: props.code,
+ language: props.language,
+ editable: false,
+ setCode: () => {},
+ })
+ handle = typeof out === 'function' ? { destroy: out } : (out ?? {})
+}
+
+function unmountWidget(): void {
+ handle?.destroy?.()
+ handle = null
+}
+
+onMounted(mountWidget)
+onBeforeUnmount(unmountWidget)
+
+watch(
+ () => props.code,
+ (code) => {
+ if (handle?.update) {
+ handle.update(code)
+ return
+ }
+ unmountWidget()
+ mountWidget()
+ },
+)
+
+watch(
+ () => props.mount,
+ () => {
+ unmountWidget()
+ mountWidget()
+ },
+)
+
+
+
+
+
diff --git a/src/components/Markdown/blockRenderers.ts b/src/components/Markdown/blockRenderers.ts
index 5f7a434..4e924ae 100644
--- a/src/components/Markdown/blockRenderers.ts
+++ b/src/components/Markdown/blockRenderers.ts
@@ -13,6 +13,32 @@ export interface UidMarkdownBlockResult {
html?: string
/** Why the block could not be rendered; the block is then shown as code with this message. */
error?: string
+ /**
+ * A live block: the host's widget (an API console, a chart with controls)
+ * is mounted into the block's element instead of static markup. Return a
+ * handle or a cleanup function: `update` takes a changed source without
+ * remounting, `destroy` runs when the block goes away.
+ */
+ mount?: (
+ el: HTMLElement,
+ ctx: UidMarkdownBlockMountContext,
+ ) => UidMarkdownBlockMountHandle | (() => void) | void
+}
+
+/** What a live block's `mount` is told. The contract is the WYSIWYG editor's too. */
+export interface UidMarkdownBlockMountContext {
+ /** The block's source. */
+ code: string
+ language: string
+ /** Always false here: rendered markdown is read-only. */
+ editable: boolean
+ /** Ignored here — rendered markdown has no source to write to. */
+ setCode(code: string): void
+}
+
+export interface UidMarkdownBlockMountHandle {
+ update?(code: string): void
+ destroy?(): void
}
/**
diff --git a/src/components/Markdown/index.ts b/src/components/Markdown/index.ts
index 181ca4f..5a81e74 100644
--- a/src/components/Markdown/index.ts
+++ b/src/components/Markdown/index.ts
@@ -23,4 +23,6 @@ export type {
UidMarkdownBlockRenderer,
UidMarkdownBlockRenderers,
UidMarkdownBlockResult,
+ UidMarkdownBlockMountContext,
+ UidMarkdownBlockMountHandle,
} from './blockRenderers.js'