mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 15:51:26 +00:00
Overhaul: real metrics, Git integration UI, open-source hygiene, root cleanup
Backend: - GET /services/:id/metrics with real Docker container stats - DELETE /git/providers/:id and GET repo branches endpoint Frontend: - Service detail shows real telemetry or honest empty states - Live status distinguishes idle/polling/reconnecting - Git providers section in Settings; repo+branch pickers in Add Service - Real logo, compact Projects header, canvas fitView fix - index.css deduplicated (1725 -> 1384 lines) - Fixed smoke tests, lint errors, ToastProvider/context unification Repo hygiene: - Added LICENSE (MIT), CONTRIBUTING.md, SECURITY.md; rewrote README - Untracked .env.prod and data/letsencrypt secrets; fixed .gitignore - Removed legacy root src/internal/cmd and stale configs/docs/artifacts - Moved guides and design references under docs/ Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
355a97bab4
commit
9f3b196032
499 files changed
+4471
-94235
No files matched your search
@@ -14,11 +14,12 @@ import '@xyflow/react/dist/style.css';
|
||||
import type { ServiceEntity } from '@/lib/api-client';
|
||||
import { inferAutoConnections, type ServiceVariable } from '../auto-connections';
|
||||
import {
|
||||
DEFAULT_VIEWPORT,
|
||||
type CanvasGroup,
|
||||
type CanvasNodeLayout,
|
||||
type ProjectCanvasMetadata,
|
||||
} from '../model';
|
||||
import { loadCanvasMetadata, saveCanvasMetadata } from '../storage';
|
||||
import { canvasStorageKey, loadCanvasMetadata, saveCanvasMetadata } from '../storage';
|
||||
import { GroupNode, ServiceNode, type GroupNodeData, type ServiceNodeData } from './nodes';
|
||||
import { Plus, Layers, Maximize2, RotateCcw, Box, Link2 } from 'lucide-react';
|
||||
|
||||
@@ -152,6 +153,7 @@ function CanvasInner({ projectId, services, variablesByService, onAddService, on
|
||||
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
||||
const persistTimeout = useRef<number | null>(null);
|
||||
const hydratedRef = useRef(false);
|
||||
const viewportRestoredRef = useRef(false);
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState<CanvasNode>([]);
|
||||
const [selectedServiceId, setSelectedServiceId] = useState<string | null>(null);
|
||||
const [viewportTick, setViewportTick] = useState(0);
|
||||
@@ -169,20 +171,34 @@ function CanvasInner({ projectId, services, variablesByService, onAddService, on
|
||||
const edges = useMemo(() => toFlowEdges(inferredLinks), [inferredLinks]);
|
||||
|
||||
useEffect(() => {
|
||||
const hasStoredViewport = localStorage.getItem(canvasStorageKey(projectId)) !== null;
|
||||
const metadata = loadCanvasMetadata(projectId, services);
|
||||
const nextNodes = toFlowNodes(metadata, services, onOpenService);
|
||||
|
||||
viewportRestoredRef.current = false;
|
||||
setNodes(nextNodes);
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
setViewport(metadata.viewport, { duration: 120 });
|
||||
window.requestAnimationFrame(() => {
|
||||
const isDefaultViewport =
|
||||
metadata.viewport.x === DEFAULT_VIEWPORT.x &&
|
||||
metadata.viewport.y === DEFAULT_VIEWPORT.y &&
|
||||
metadata.viewport.zoom === DEFAULT_VIEWPORT.zoom;
|
||||
|
||||
if (!hasStoredViewport || isDefaultViewport) {
|
||||
void fitView({ padding: 0.15, duration: 120 });
|
||||
} else {
|
||||
void setViewport(metadata.viewport, { duration: 120 });
|
||||
}
|
||||
viewportRestoredRef.current = true;
|
||||
});
|
||||
});
|
||||
|
||||
hydratedRef.current = true;
|
||||
}, [projectId, serviceFingerprint, onOpenService, setNodes, setViewport, services]);
|
||||
}, [projectId, serviceFingerprint, onOpenService, setNodes, setViewport, fitView, services]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hydratedRef.current) {
|
||||
if (!hydratedRef.current || !viewportRestoredRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -414,7 +430,6 @@ function CanvasInner({ projectId, services, variablesByService, onAddService, on
|
||||
}}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
onMoveEnd={() => setViewportTick((value) => value + 1)}
|
||||
fitView
|
||||
panOnDrag
|
||||
zoomOnScroll
|
||||
minZoom={0.25}
|
||||
|
||||
Reference in new issue
Block a user