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>;