Add side panels, task selection, graph animation, and project docs

- Foldable left panel (user profile) and right panel (task details)
- Clicking a task in the list or graph node selects it and shows details
- Both views (task list + graph) always mounted via absolute inset-0 for
  correct canvas dimensions; tabs toggle visibility with opacity
- Graph node selection animation: other nodes repel outward (charge -600),
  then selected node smoothly slides to center (500ms cubic ease-out),
  then charge restores to -120 and graph stabilizes
- Graph re-fits on tab switch and panel resize via ResizeObserver
- Fix UUID string IDs throughout (backend returns UUIDs, not integers)
- Add TaskDetailPanel, UserPanel components
- Add CLAUDE.md project documentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Alvis
2026-04-08 11:23:06 +00:00
parent 5c7edd4bbc
commit f1d51b8cc8
23998 changed files with 3242708 additions and 0 deletions

67
frontend/node_modules/preact/compat/src/hooks.js generated vendored Normal file
View File

@@ -0,0 +1,67 @@
import { useState, useLayoutEffect, useEffect } from 'preact/hooks';
import { is } from './util';
/**
* This is taken from https://github.com/facebook/react/blob/main/packages/use-sync-external-store/src/useSyncExternalStoreShimClient.js#L84
* on a high level this cuts out the warnings, ... and attempts a smaller implementation
* @typedef {{ _value: any; _getSnapshot: () => any }} Store
*/
export function useSyncExternalStore(subscribe, getSnapshot) {
const value = getSnapshot();
/**
* @typedef {{ _instance: Store }} StoreRef
* @type {[StoreRef, (store: StoreRef) => void]}
*/
const [{ _instance }, forceUpdate] = useState({
_instance: { _value: value, _getSnapshot: getSnapshot }
});
useLayoutEffect(() => {
_instance._value = value;
_instance._getSnapshot = getSnapshot;
if (didSnapshotChange(_instance)) {
forceUpdate({ _instance });
}
}, [subscribe, value, getSnapshot]);
useEffect(() => {
if (didSnapshotChange(_instance)) {
forceUpdate({ _instance });
}
return subscribe(() => {
if (didSnapshotChange(_instance)) {
forceUpdate({ _instance });
}
});
}, [subscribe]);
return value;
}
/** @type {(inst: Store) => boolean} */
function didSnapshotChange(inst) {
try {
return !is(inst._value, inst._getSnapshot());
} catch (error) {
return true;
}
}
export function startTransition(cb) {
cb();
}
export function useDeferredValue(val) {
return val;
}
export function useTransition() {
return [false, startTransition];
}
// TODO: in theory this should be done after a VNode is diffed as we want to insert
// styles/... before it attaches
export const useInsertionEffect = useLayoutEffect;