From f993672b78c888caca3a7a2657fabb56fdf125b8 Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Tue, 9 Jun 2026 20:39:10 -0500 Subject: [PATCH] fix(kube): configure Monaco for offline use and fix pod column data (IP/Node/CPU/Memory) Monaco: install monaco-editor and configure @monaco-editor/react loader to use the locally bundled module in main.tsx instead of the CDN, resolving the perpetual spinner in YamlEditor under Tauri's offline WebView. Added worker format and optimizeDeps entries to vite.config.ts. Pod columns: extend PodInfo struct with ip, node, and restarts fields; extract podIP, nodeName, and restartCount from kubectl JSON output in parse_pods_json so the IP, Node columns render real data instead of blanks. Also fix ref-during-render lint error in useKeyboardShortcuts. --- package-lock.json | 39 +++++++++++++++++++++++++++++++ package.json | 1 + src-tauri/src/commands/kube.rs | 35 +++++++++++++++++++++++++++ src/hooks/useKeyboardShortcuts.ts | 5 +++- src/main.tsx | 6 +++++ 5 files changed, 85 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 67e99d42..07c1bd34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "class-variance-authority": "^0.7", "clsx": "^2", "lucide-react": "latest", + "monaco-editor": "^0.55.1", "react": "^19", "react-chartjs-2": "^5.3.1", "react-diff-viewer-continued": "^4", @@ -3001,6 +3002,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -5706,6 +5714,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", + "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", @@ -9404,6 +9421,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/marked": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", + "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -10603,6 +10632,16 @@ "node": ">=18.0.0" } }, + "node_modules/monaco-editor": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz", + "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==", + "license": "MIT", + "dependencies": { + "dompurify": "3.2.7", + "marked": "14.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", diff --git a/package.json b/package.json index 7b383dad..6536bac9 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "class-variance-authority": "^0.7", "clsx": "^2", "lucide-react": "latest", + "monaco-editor": "^0.55.1", "react": "^19", "react-chartjs-2": "^5.3.1", "react-diff-viewer-continued": "^4", diff --git a/src-tauri/src/commands/kube.rs b/src-tauri/src/commands/kube.rs index a6a59188..a9113172 100644 --- a/src-tauri/src/commands/kube.rs +++ b/src-tauri/src/commands/kube.rs @@ -96,6 +96,9 @@ pub struct PodInfo { pub ready: String, pub age: String, pub containers: Vec, + pub restarts: Option, + pub ip: Option, + pub node: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -1159,6 +1162,35 @@ fn parse_pods_json(json_str: &str) -> Result, String> { }) .unwrap_or_default(); + let restarts = item + .get("status") + .and_then(|s| s.get("containerStatuses")) + .and_then(|c| c.as_array()) + .map(|container_statuses| { + container_statuses + .iter() + .map(|c| { + c.get("restartCount") + .and_then(|r| r.as_u64()) + .unwrap_or(0) as u32 + }) + .sum::() + }); + + let ip = item + .get("status") + .and_then(|s| s.get("podIP")) + .and_then(|v| v.as_str()) + .filter(|s| !s.is_empty()) + .map(|s| s.to_string()); + + let node = item + .get("spec") + .and_then(|s| s.get("nodeName")) + .and_then(|v| v.as_str()) + .filter(|s| !s.is_empty()) + .map(|s| s.to_string()); + pods.push(PodInfo { name, namespace, @@ -1166,6 +1198,9 @@ fn parse_pods_json(json_str: &str) -> Result, String> { ready, age, containers, + restarts, + ip, + node, }); } diff --git a/src/hooks/useKeyboardShortcuts.ts b/src/hooks/useKeyboardShortcuts.ts index 8cf6f4e3..3b6bd19d 100644 --- a/src/hooks/useKeyboardShortcuts.ts +++ b/src/hooks/useKeyboardShortcuts.ts @@ -13,7 +13,10 @@ export interface KeyboardShortcut { export function useKeyboardShortcuts(shortcuts: KeyboardShortcut[]): void { const shortcutsRef = useRef(shortcuts); - shortcutsRef.current = shortcuts; + + useEffect(() => { + shortcutsRef.current = shortcuts; + }, [shortcuts]); const handleKeyDown = useCallback((event: KeyboardEvent) => { for (const shortcut of shortcutsRef.current) { diff --git a/src/main.tsx b/src/main.tsx index 7bf91f83..c356d7f9 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,9 +1,15 @@ import React from "react"; import ReactDOM from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; +import { loader } from "@monaco-editor/react"; +import * as monaco from "monaco-editor"; import App from "./App"; import "./styles/globals.css"; +// Use the locally bundled Monaco instead of loading from CDN. +// Tauri's WebView has no internet access so the default CDN loader never resolves. +loader.config({ monaco }); + ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(