File size: 2,726 Bytes
8fc8501 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | import * as Y from "yjs";
/**
* Metadata describing a data file uploaded into the embed data store.
*
* Kept intentionally lean (strings + small numbers) so it can live
* alongside the raw content inside a single Y.Map entry without
* bloating the document size.
*/
export interface EmbedDataFileMeta {
name: string;
/** Extension without leading dot, lowercased: "csv" | "tsv" | "json" | "txt" */
ext: string;
/** Size of the stored text content in bytes (UTF-8). */
size: number;
/** Uploader user id (if available). */
uploader?: string;
/** Epoch ms when the file was added. */
addedAt: number;
/** Light metadata computed client-side after parsing. */
rowCount?: number;
columns?: string[];
}
export interface EmbedDataFile {
meta: EmbedDataFileMeta;
/** Raw text content (UTF-8). */
content: string;
}
/**
* Collaborative store for tabular/textual data files attached to a
* document. Each key is a filename (e.g. "sales.csv") and the value is
* an `EmbedDataFile` object (meta + raw text content).
*
* These files are accessible to the Embed Studio AI via tool calls so
* charts can reference user-provided datasets. The store is backed by
* `Y.Map("embed-data")` for realtime collaboration.
*/
export function createEmbedDataStore(ydoc: Y.Doc) {
const ymap = ydoc.getMap<EmbedDataFile>("embed-data");
function get(name: string): EmbedDataFile | undefined {
return ymap.get(name);
}
function getContent(name: string): string {
return ymap.get(name)?.content ?? "";
}
function getMeta(name: string): EmbedDataFileMeta | undefined {
return ymap.get(name)?.meta;
}
function has(name: string): boolean {
return ymap.has(name);
}
function set(file: EmbedDataFile) {
ymap.set(file.meta.name, file);
}
function remove(name: string) {
ymap.delete(name);
}
function rename(oldName: string, newName: string) {
const file = ymap.get(oldName);
if (!file) return;
ydoc.transact(() => {
ymap.set(newName, { ...file, meta: { ...file.meta, name: newName } });
ymap.delete(oldName);
});
}
function keys(): string[] {
return Array.from(ymap.keys());
}
function list(): EmbedDataFileMeta[] {
const items: EmbedDataFileMeta[] = [];
ymap.forEach((file) => {
items.push(file.meta);
});
return items.sort((a, b) => a.name.localeCompare(b.name));
}
function observe(callback: () => void) {
ymap.observe(callback);
return () => ymap.unobserve(callback);
}
return {
get,
getContent,
getMeta,
has,
set,
remove,
rename,
keys,
list,
observe,
};
}
export type EmbedDataStore = ReturnType<typeof createEmbedDataStore>;
|