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

View File

@@ -0,0 +1,12 @@
import AsyncReader from '../readers/async';
import type Settings from '../settings';
import type { Entry, Errno } from '../types';
export declare type AsyncCallback = (error: Errno, entries: Entry[]) => void;
export default class AsyncProvider {
private readonly _root;
private readonly _settings;
protected readonly _reader: AsyncReader;
private readonly _storage;
constructor(_root: string, _settings: Settings);
read(callback: AsyncCallback): void;
}

View File

@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const async_1 = require("../readers/async");
class AsyncProvider {
constructor(_root, _settings) {
this._root = _root;
this._settings = _settings;
this._reader = new async_1.default(this._root, this._settings);
this._storage = [];
}
read(callback) {
this._reader.onError((error) => {
callFailureCallback(callback, error);
});
this._reader.onEntry((entry) => {
this._storage.push(entry);
});
this._reader.onEnd(() => {
callSuccessCallback(callback, this._storage);
});
this._reader.read();
}
}
exports.default = AsyncProvider;
function callFailureCallback(callback, error) {
callback(error);
}
function callSuccessCallback(callback, entries) {
callback(null, entries);
}

View File

@@ -0,0 +1,4 @@
import AsyncProvider from './async';
import StreamProvider from './stream';
import SyncProvider from './sync';
export { AsyncProvider, StreamProvider, SyncProvider };

View File

@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SyncProvider = exports.StreamProvider = exports.AsyncProvider = void 0;
const async_1 = require("./async");
exports.AsyncProvider = async_1.default;
const stream_1 = require("./stream");
exports.StreamProvider = stream_1.default;
const sync_1 = require("./sync");
exports.SyncProvider = sync_1.default;

View File

@@ -0,0 +1,12 @@
/// <reference types="node" />
import { Readable } from 'stream';
import AsyncReader from '../readers/async';
import type Settings from '../settings';
export default class StreamProvider {
private readonly _root;
private readonly _settings;
protected readonly _reader: AsyncReader;
protected readonly _stream: Readable;
constructor(_root: string, _settings: Settings);
read(): Readable;
}

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const stream_1 = require("stream");
const async_1 = require("../readers/async");
class StreamProvider {
constructor(_root, _settings) {
this._root = _root;
this._settings = _settings;
this._reader = new async_1.default(this._root, this._settings);
this._stream = new stream_1.Readable({
objectMode: true,
read: () => { },
destroy: () => {
if (!this._reader.isDestroyed) {
this._reader.destroy();
}
}
});
}
read() {
this._reader.onError((error) => {
this._stream.emit('error', error);
});
this._reader.onEntry((entry) => {
this._stream.push(entry);
});
this._reader.onEnd(() => {
this._stream.push(null);
});
this._reader.read();
return this._stream;
}
}
exports.default = StreamProvider;

View File

@@ -0,0 +1,10 @@
import SyncReader from '../readers/sync';
import type Settings from '../settings';
import type { Entry } from '../types';
export default class SyncProvider {
private readonly _root;
private readonly _settings;
protected readonly _reader: SyncReader;
constructor(_root: string, _settings: Settings);
read(): Entry[];
}

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const sync_1 = require("../readers/sync");
class SyncProvider {
constructor(_root, _settings) {
this._root = _root;
this._settings = _settings;
this._reader = new sync_1.default(this._root, this._settings);
}
read() {
return this._reader.read();
}
}
exports.default = SyncProvider;