This commit is contained in:
Andras Schmelczer 2026-09-11 16:09:28 +01:00
commit d8f2407ddf
44 changed files with 10377 additions and 0 deletions

38
src/lib/use-project.ts Normal file
View file

@ -0,0 +1,38 @@
import { useCallback, useEffect, useState } from 'react';
import type { UrlState } from './types';
import { decodeState, encodeState } from './url-state';
/** The fragment is the durable source of truth. No local storage or server state. */
export function useProject() {
const [loaded, setLoaded] = useState(() => decodeState(window.location.hash));
useEffect(() => {
const restore = () => setLoaded(decodeState(window.location.hash));
window.addEventListener('hashchange', restore);
window.addEventListener('popstate', restore);
if (!window.location.hash) window.history.replaceState(null, '', encodeState(loaded.state));
return () => {
window.removeEventListener('hashchange', restore);
window.removeEventListener('popstate', restore);
};
}, []);
const update = useCallback((transform: (current: UrlState) => UrlState, milestone = false) => {
setLoaded((current) => {
const next = transform(current.state);
try {
const hash = encodeState(next);
if (hash !== window.location.hash)
window.history[milestone ? 'pushState' : 'replaceState'](null, '', hash);
const canonical = decodeState(hash).state;
if (next.project === current.state.project) canonical.project = current.state.project;
return { state: canonical, error: null };
} catch (error) {
return {
...current,
error:
error instanceof Error ? error.message : 'This project could not be saved in the URL.',
};
}
});
}, []);
return { ...loaded, update };
}