File size: 2,784 Bytes
1778c9e
 
 
 
 
d7cd63b
1778c9e
 
 
 
 
 
 
 
d7cd63b
 
 
1778c9e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d7cd63b
 
 
 
1778c9e
 
 
 
 
 
d7cd63b
1778c9e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d7cd63b
1778c9e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
109
110
111
import { idb } from "$lib/remult.js";
import { dequal } from "dequal";
import { Entity, Fields, repo, type MembersOnly } from "remult";
import { PersistedState } from "runed";
import { checkpoints } from "./checkpoints.svelte";
import { conversations } from "./conversations.svelte";

@Entity("project")
export class ProjectEntity {
	@Fields.cuid()
	id!: string;

	@Fields.string()
	name!: string;

	@Fields.string()
	systemMessage?: string;
}

export type ProjectEntityMembers = MembersOnly<ProjectEntity>;

const projectsRepo = repo(ProjectEntity, idb);

const LOCAL_STORAGE_KEY = "hf_inf_pg_active_pid";
export const DEFAULT_PROJECT_ID = "default";
const defaultProj = projectsRepo.create({ id: DEFAULT_PROJECT_ID, name: "Default" });

class Projects {
	#projects: Record<ProjectEntity["id"], ProjectEntity> = $state({ default: defaultProj });
	#activeId = new PersistedState(LOCAL_STORAGE_KEY, "default");

	get activeId() {
		return this.#activeId.current;
	}

	set activeId(id: string) {
		this.#activeId.current = id;
	}

	constructor() {
		projectsRepo.find().then(res => {
			if (!res.some(p => p.id === this.activeId)) this.activeId === DEFAULT_PROJECT_ID;

			res.forEach(p => {
				if (dequal(this.#projects[p.id], p)) return;
				this.#projects[p.id] = p;
			});
		});
	}

	async create(args: Omit<ProjectEntity, "id">): Promise<string> {
		const p = await projectsRepo.save({ ...args });
		this.#projects[p.id] = p;
		return p.id;
	}

	saveProject = async (args: { name: string; moveCheckpoints?: boolean }) => {
		const defaultProject = this.all.find(p => p.id === DEFAULT_PROJECT_ID);
		if (!defaultProject) return;

		const id = await this.create({ name: args.name, systemMessage: defaultProject.systemMessage });

		if (args.moveCheckpoints) {
			checkpoints.migrate(defaultProject.id, id);
		}

		// conversations.migrate(defaultProject.id, id).then(_ => (this.#activeId.current = id));
		conversations.migrate(defaultProject.id, id).then(() => {
			this.activeId = id;
		});

		return id;
	};

	setCurrent = async (id: string) => {
		await checkpoints.migrate(id, this.activeId);
		conversations.migrate(this.activeId, id).then(() => {
			this.#activeId.current = id;
		});
		this.activeId = id;
	};

	get current() {
		return this.#projects[this.activeId];
	}

	get all() {
		return Object.values(this.#projects);
	}

	async update(data: ProjectEntity) {
		if (!data.id) return;
		await projectsRepo.upsert({ where: { id: data.id }, set: data });
		this.#projects[data.id] = { ...data };
	}

	async delete(id: string) {
		if (!id) return;

		await projectsRepo.delete(id);
		await conversations.deleteAllFrom(id);
		delete this.#projects[id];

		if (this.activeId === id) {
			this.activeId = DEFAULT_PROJECT_ID;
		}
	}
}

export const projects = new Projects();