MAC / frontend /src /lib /stores.js
Aaryan17's picture
chore: upload MAC codebase to HF Space
0e76632 verified
import { writable, derived, get } from 'svelte/store';
import { auth as authApi, setup as setupApi, features as featuresApi } from './api.js';
// ── Auth store ────────────────────────────────────────────────────────────────
function createAuthStore() {
const { subscribe, set, update } = writable({
user: null,
token: null,
refreshToken: null,
loading: true,
initialized: false,
});
return {
subscribe,
async init() {
if (typeof localStorage === 'undefined') {
update(s => ({ ...s, loading: false, initialized: true }));
return;
}
const token = localStorage.getItem('mac_token');
const refreshToken = localStorage.getItem('mac_refresh');
if (!token) {
update(s => ({ ...s, loading: false, initialized: true }));
return;
}
try {
const user = await authApi.me();
update(s => ({ ...s, user, token, refreshToken, loading: false, initialized: true }));
} catch {
localStorage.removeItem('mac_token');
localStorage.removeItem('mac_refresh');
update(s => ({ ...s, loading: false, initialized: true }));
}
},
async login(identifier, password) {
const data = await authApi.login(identifier, password);
localStorage.setItem('mac_token', data.access_token);
if (data.refresh_token) localStorage.setItem('mac_refresh', data.refresh_token);
update(s => ({ ...s, user: data.user, token: data.access_token, refreshToken: data.refresh_token }));
return data;
},
async logout() {
const rt = localStorage.getItem('mac_refresh');
try { if (rt) await authApi.logout(); } catch {}
localStorage.removeItem('mac_token');
localStorage.removeItem('mac_refresh');
set({ user: null, token: null, refreshToken: null, loading: false, initialized: true });
},
setUser(user) {
update(s => ({ ...s, user }));
},
};
}
export const authStore = createAuthStore();
export const user = derived(authStore, $a => $a.user);
export const isAdmin = derived(authStore, $a => $a.user?.role === 'admin');
export const isFacultyOrAdmin = derived(authStore, $a => ['faculty', 'admin'].includes($a.user?.role));
export const isLoggedIn = derived(authStore, $a => !!$a.user);
// ── Setup store ───────────────────────────────────────────────────────────────
export const setupStore = writable({ is_first_run: null, checked: false });
export async function checkSetup() {
try {
const data = await setupApi.status();
setupStore.set({ ...data, checked: true });
return data;
} catch {
setupStore.set({ is_first_run: false, checked: true });
}
}
// ── Feature flags store ───────────────────────────────────────────────────────
export const featureStore = writable({ flags: {}, roles: {}, loaded: false });
export async function loadFeatures() {
try {
const data = await featuresApi.status();
featureStore.set({ ...data, loaded: true });
} catch {
featureStore.set({ flags: {}, roles: {}, loaded: true });
}
}
export function hasFeature(key) {
return derived(featureStore, $f => !!$f.flags[key]);
}
// ── UI state ──────────────────────────────────────────────────────────────────
export const sidebarOpen = writable(true);
export const theme = writable('light');
// ── Loading state ─────────────────────────────────────────────────────────────
export const globalLoading = writable(false);
export const loadingMessage = writable('');
export const locale = writable('en');
export const toast = writable(null);
let toastTimer = null;
export function showToast(message, type = 'info', duration = 4000) {
if (toastTimer) clearTimeout(toastTimer);
toast.set({ message, type, id: Date.now() });
toastTimer = setTimeout(() => toast.set(null), duration);
}
// ── Chat store ────────────────────────────────────────────────────────────────
export const chatStore = writable({
conversations: [], // [{ id, title, messages: [{role, content, model, ts}] }]
activeId: null,
streaming: false,
});
export function newConversation() {
const id = crypto.randomUUID();
chatStore.update(s => ({
...s,
conversations: [{ id, title: 'New Chat', messages: [] }, ...s.conversations],
activeId: id,
}));
return id;
}
export function appendMessage(convId, message) {
chatStore.update(s => ({
...s,
conversations: s.conversations.map(c =>
c.id === convId
? { ...c, messages: [...c.messages, message],
title: c.title === 'New Chat' && message.role === 'user'
? message.content.slice(0, 40) + (message.content.length > 40 ? '…' : '')
: c.title }
: c
),
}));
}
export function updateLastMessage(convId, patch) {
chatStore.update(s => ({
...s,
conversations: s.conversations.map(c =>
c.id === convId
? { ...c, messages: c.messages.map((m, i) =>
i === c.messages.length - 1 ? { ...m, ...patch } : m) }
: c
),
}));
}