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

2
frontend/node_modules/ts-jest/dist/config/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export * from './paths-to-module-name-mapper';
export * from './types';

18
frontend/node_modules/ts-jest/dist/config/index.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./paths-to-module-name-mapper"), exports);
__exportStar(require("./types"), exports);

View File

@@ -0,0 +1,9 @@
import type { Config } from '@jest/types';
import type { CompilerOptions } from 'typescript';
type TsPathMapping = Exclude<CompilerOptions['paths'], undefined>;
type JestPathMapping = Config.InitialOptions['moduleNameMapper'];
export declare const pathsToModuleNameMapper: (mapping: TsPathMapping, { prefix, useESM }?: {
prefix?: string;
useESM?: boolean;
}) => JestPathMapping;
export {};

View File

@@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.pathsToModuleNameMapper = void 0;
const bs_logger_1 = require("bs-logger");
const utils_1 = require("../utils");
const messages_1 = require("../utils/messages");
// we don't need to escape all chars, so commented out is the real one
// const escapeRegex = (str: string) => str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
const escapeRegex = (str) => str.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
const logger = utils_1.rootLogger.child({ [bs_logger_1.LogContexts.namespace]: 'path-mapper' });
const pathsToModuleNameMapper = (mapping, { prefix = '', useESM = false } = {}) => {
const jestMap = {};
for (const fromPath of Object.keys(mapping)) {
const toPaths = mapping[fromPath];
// check that we have only one target path
if (toPaths.length === 0) {
logger.warn((0, messages_1.interpolate)("Not mapping \"{{path}}\" because it has no target." /* Errors.NotMappingPathWithEmptyMap */, { path: fromPath }));
continue;
}
// split with '*'
const segments = fromPath.split(/\*/g);
if (segments.length === 1) {
const paths = toPaths.map((target) => {
const enrichedPrefix = prefix !== '' && !prefix.endsWith('/') ? `${prefix}/` : prefix;
return `${enrichedPrefix}${target}`;
});
const cjsPattern = `^${escapeRegex(fromPath)}$`;
jestMap[cjsPattern] = paths.length === 1 ? paths[0] : paths;
}
else if (segments.length === 2) {
const paths = toPaths.map((target) => {
const enrichedTarget = target.startsWith('./') && prefix !== '' ? target.substring(target.indexOf('/') + 1) : target;
const enrichedPrefix = prefix !== '' && !prefix.endsWith('/') ? `${prefix}/` : prefix;
return `${enrichedPrefix}${enrichedTarget.replace(/\*/g, '$1')}`;
});
if (useESM) {
const esmPattern = `^${escapeRegex(segments[0])}(.*)${escapeRegex(segments[1])}\\.js$`;
jestMap[esmPattern] = paths.length === 1 ? paths[0] : paths;
}
const cjsPattern = `^${escapeRegex(segments[0])}(.*)${escapeRegex(segments[1])}$`;
jestMap[cjsPattern] = paths.length === 1 ? paths[0] : paths;
}
else {
logger.warn((0, messages_1.interpolate)("Not mapping \"{{path}}\" because it has more than one star (`*`)." /* Errors.NotMappingMultiStarPath */, { path: fromPath }));
}
}
if (useESM) {
jestMap['^(\\.{1,2}/.*)\\.js$'] = '$1';
}
return jestMap;
};
exports.pathsToModuleNameMapper = pathsToModuleNameMapper;

2
frontend/node_modules/ts-jest/dist/config/types.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
import type { TsConfigJson } from 'type-fest';
export type TsConfigCompilerOptionsJson = TsConfigJson.CompilerOptions;

2
frontend/node_modules/ts-jest/dist/config/types.js generated vendored Normal file
View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });