tfrere's picture
tfrere HF Staff
feat(frontend): editor refresh (embed studio, comment popover, shiki, top bar, hooks, styles)
76fc93a
import * as Y from "yjs";
/**
* Collaborative store for HTML embed content, backed by Y.Map("embeds").
*
* Each key is a filename (e.g. "d3-scaling-chart.html") and the value is
* the raw HTML fragment (not a full document - buildDoc wraps it for display).
*
* The ProseMirror htmlEmbed node only stores the `src` attribute as a
* reference key. The actual HTML lives here so multiple collaborators
* can edit chart content concurrently.
*/
export function createEmbedStore(ydoc: Y.Doc) {
const ymap = ydoc.getMap<string>("embeds");
function get(src: string): string {
return ymap.get(src) ?? "";
}
function set(src: string, html: string) {
ymap.set(src, html);
}
function has(src: string): boolean {
return ymap.has(src);
}
function remove(src: string) {
ymap.delete(src);
}
function rename(oldSrc: string, newSrc: string) {
const html = ymap.get(oldSrc);
if (html !== undefined) {
ydoc.transact(() => {
ymap.set(newSrc, html);
ymap.delete(oldSrc);
});
}
}
/** Apply a search/replace patch to an existing embed. */
function patch(src: string, search: string, replace: string): boolean {
const html = ymap.get(src);
if (!html || !html.includes(search)) return false;
ymap.set(src, html.replace(search, replace));
return true;
}
function keys(): string[] {
return Array.from(ymap.keys());
}
function getAll(): Record<string, string> {
const result: Record<string, string> = {};
ymap.forEach((val, key) => {
result[key] = val;
});
return result;
}
/**
* Observe changes to any embed.
* Returns an unsubscribe function.
*/
function observe(callback: () => void) {
ymap.observe(callback);
return () => ymap.unobserve(callback);
}
/**
* Observe changes to a specific embed key.
* Returns an unsubscribe function.
*/
function observeKey(src: string, callback: (html: string) => void) {
const handler = (event: Y.YMapEvent<string>) => {
if (event.keysChanged.has(src)) {
callback(ymap.get(src) ?? "");
}
};
ymap.observe(handler);
return () => ymap.unobserve(handler);
}
return {
get,
set,
has,
remove,
rename,
patch,
keys,
getAll,
observe,
observeKey,
};
}
export type EmbedStore = ReturnType<typeof createEmbedStore>;