From 3afa97b517ec29d14dfe04f42319673fb9a6aaf1 Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Tue, 9 Jun 2026 20:37:57 -0500 Subject: [PATCH 1/4] feat(kube): add YAML edit action to NamespaceList Namespaces had a read-only table with no actions. Adds an edit button per row that fetches the namespace YAML via getResourceYamlCmd (cluster-scoped, empty namespace param) and opens EditResourceModal. --- src/components/Kubernetes/NamespaceList.tsx | 60 +++++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/src/components/Kubernetes/NamespaceList.tsx b/src/components/Kubernetes/NamespaceList.tsx index 0a961209..5ad53927 100644 --- a/src/components/Kubernetes/NamespaceList.tsx +++ b/src/components/Kubernetes/NamespaceList.tsx @@ -1,6 +1,9 @@ -import React from "react"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Badge } from "@/components/ui"; +import React, { useState } from "react"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Badge, Button } from "@/components/ui"; +import { Pencil } from "lucide-react"; import type { NamespaceResourceInfo } from "@/lib/tauriCommands"; +import { getResourceYamlCmd } from "@/lib/tauriCommands"; +import { EditResourceModal } from "./EditResourceModal"; interface NamespaceListProps { items: NamespaceResourceInfo[]; @@ -14,21 +17,46 @@ function statusVariant(status: string): "success" | "destructive" | "secondary" return "secondary"; } -export function NamespaceList({ items }: NamespaceListProps) { +export function NamespaceList({ items, clusterId }: NamespaceListProps) { + const [editState, setEditState] = useState<{ + open: boolean; + name: string; + yaml: string; + } | null>(null); + const [editError, setEditError] = useState(null); + + const openEdit = async (ns: NamespaceResourceInfo) => { + setEditError(null); + try { + // Namespaces are cluster-scoped — pass empty string for namespace param + const yaml = await getResourceYamlCmd(clusterId, "namespaces", "", ns.name); + setEditState({ open: true, name: ns.name, yaml }); + } catch (err) { + setEditError(err instanceof Error ? err.message : String(err)); + } + }; + return (
+ {editError && ( +
+ {editError} +
+ )} + Name Status Age + Actions {items.length === 0 ? ( - + No namespaces found @@ -40,11 +68,35 @@ export function NamespaceList({ items }: NamespaceListProps) { {ns.status} {ns.age} + + + )) )}
+ + {editState && ( + setEditState(null)} + /> + )}
); } -- 2.45.2 From 399ba30c6b32acc90b250696bb8bcb48f76a0b2e Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Tue, 9 Jun 2026 20:38:24 -0500 Subject: [PATCH 2/4] fix(kube): fix PTY param names, ansi-to-react ESM interop, and dark mode badges - Correct start_pty_exec_session and start_pty_attach_session invoke calls to use pod/container keys matching Rust command parameter names; drop unused shell arg from the invoke payload - Fix ansi-to-react CJS/ESM interop in LogStreamPanel: unwrap .default on CJS module so React does not receive a plain object at render time; add optimizeDeps entry to vite.config.ts so Vite pre-bundles it in dev - Replace Badge + getPodStatusColor with StatusBadge in PodList; remove now-unused helper; extend getStatusVariant in Badge.tsx to handle crashloopbackoff, OOM, backoff, terminating, and evicted states - Fix pre-existing lint issues: remove unused listPodsCmd/listNamespacesCmd imports from PortForwardPage, wrap loadPortForwards in useCallback, and remove unused logLine variable from LogStreamPanel test --- src/components/Badge.tsx | 6 + .../Kubernetes/DeploymentDetail.tsx | 24 ++- src/components/Kubernetes/LogStreamPanel.tsx | 6 +- src/components/Kubernetes/PodDetail.tsx | 24 ++- src/components/Kubernetes/PodList.tsx | 24 +-- .../Kubernetes/PortForwardDialog.tsx | 181 ++++++++++++++++++ src/lib/tauriCommands.ts | 11 +- src/pages/Kubernetes/PortForwardPage.tsx | 10 +- tests/unit/LogStreamPanel.test.tsx | 3 - vite.config.ts | 10 + 10 files changed, 253 insertions(+), 46 deletions(-) create mode 100644 src/components/Kubernetes/PortForwardDialog.tsx diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 11e97323..8b5adf23 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -69,5 +69,11 @@ function getStatusVariant(status: string): BadgeProps["variant"] { if (normalized === "succeeded" || normalized === "completed" || normalized === "bound") { return "succeeded"; } + if (normalized.includes("crash") || normalized.includes("error") || normalized.includes("oom") || normalized.includes("backoff")) { + return "failed"; + } + if (normalized === "terminating" || normalized === "evicted") { + return "destructive"; + } return "unknown"; } diff --git a/src/components/Kubernetes/DeploymentDetail.tsx b/src/components/Kubernetes/DeploymentDetail.tsx index ef023a4a..cd0028be 100644 --- a/src/components/Kubernetes/DeploymentDetail.tsx +++ b/src/components/Kubernetes/DeploymentDetail.tsx @@ -3,8 +3,9 @@ import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui"; import { Badge } from "@/components/ui"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui"; import { Button } from "@/components/ui"; -import { X, Loader2 } from "lucide-react"; +import { Network, X, Loader2 } from "lucide-react"; import { YamlEditor } from "./YamlEditor"; +import { PortForwardDialog } from "./PortForwardDialog"; import { scaleDeploymentCmd, restartDeploymentCmd, rollbackDeploymentCmd } from "@/lib/tauriCommands"; import type { DeploymentInfo } from "@/lib/tauriCommands"; @@ -18,6 +19,7 @@ interface DeploymentDetailProps { export function DeploymentDetail({ clusterId, namespace, deployment, onClose }: DeploymentDetailProps) { const [activeTab, setActiveTab] = React.useState("overview"); const [replicaCount, setReplicaCount] = React.useState(deployment.replicas); + const [portForwardOpen, setPortForwardOpen] = React.useState(false); const [scaleLoading, setScaleLoading] = React.useState(false); const [scaleError, setScaleError] = React.useState(null); @@ -74,11 +76,25 @@ export function DeploymentDetail({ clusterId, namespace, deployment, onClose }:

Deployment: {deployment.name}

{namespace} - +
+ + +
+ + Overview diff --git a/src/components/Kubernetes/LogStreamPanel.tsx b/src/components/Kubernetes/LogStreamPanel.tsx index df54f42c..7d5d39d6 100644 --- a/src/components/Kubernetes/LogStreamPanel.tsx +++ b/src/components/Kubernetes/LogStreamPanel.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useRef, useState } from "react"; import { listen, type UnlistenFn } from "@tauri-apps/api/event"; import { Download, Search, Square, Trash2, Play, ChevronUp, ChevronDown, DownloadCloud } from "lucide-react"; -import Ansi from "ansi-to-react"; +import AnsiLib from "ansi-to-react"; import { Dialog, DialogContent, @@ -12,6 +12,10 @@ import { } from "@/components/ui"; import { streamPodLogsCmd, stopLogStreamCmd } from "@/lib/tauriCommands"; +// Handle CJS default export in both dev and production Vite builds +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const Ansi = ((AnsiLib as any).default ?? AnsiLib) as React.ComponentType<{ children: string }>; + interface LogStreamPanelProps { clusterId: string; namespace: string; diff --git a/src/components/Kubernetes/PodDetail.tsx b/src/components/Kubernetes/PodDetail.tsx index 11da02ca..f3251135 100644 --- a/src/components/Kubernetes/PodDetail.tsx +++ b/src/components/Kubernetes/PodDetail.tsx @@ -4,8 +4,9 @@ import { Badge } from "@/components/ui"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui"; import { Button } from "@/components/ui"; -import { Copy, X } from "lucide-react"; +import { Copy, Network, X } from "lucide-react"; import { Loader2 } from "lucide-react"; +import { PortForwardDialog } from "./PortForwardDialog"; import { YamlEditor } from "./YamlEditor"; import { getPodLogsCmd } from "@/lib/tauriCommands"; import type { PodInfo } from "@/lib/tauriCommands"; @@ -23,6 +24,7 @@ export function PodDetail({ clusterId, namespace, pod, onClose }: PodDetailProps const [logs, setLogs] = React.useState(null); const [logsLoading, setLogsLoading] = React.useState(false); const [logsError, setLogsError] = React.useState(null); + const [portForwardOpen, setPortForwardOpen] = React.useState(false); const fetchLogs = React.useCallback( async (containerName: string) => { @@ -66,11 +68,25 @@ export function PodDetail({ clusterId, namespace, pod, onClose }: PodDetailProps

Pod: {pod.name}

{namespace} - +
+ + +
+ + Overview diff --git a/src/components/Kubernetes/PodList.tsx b/src/components/Kubernetes/PodList.tsx index 7f34974d..2d59facd 100644 --- a/src/components/Kubernetes/PodList.tsx +++ b/src/components/Kubernetes/PodList.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Button } from "@/components/ui"; -import { Badge } from "@/components/ui"; +import { StatusBadge } from "@/components/Badge"; import { FileText, Terminal, Link, Pencil, Trash2, Zap, Settings } from "lucide-react"; import type { PodInfo } from "@/lib/tauriCommands"; import { deleteResourceCmd, forceDeleteResourceCmd, getResourceYamlCmd } from "@/lib/tauriCommands"; @@ -14,7 +14,6 @@ import { useColumnConfig } from "@/hooks/useColumnConfig"; import { useMetrics } from "@/hooks/useMetrics"; import { DEFAULT_COLUMNS } from "@/config/defaultColumns"; import { ColumnConfigModal } from "@/components/tables/ColumnConfigModal"; -import { QuickActionColumn } from "@/components/tables/QuickActionColumn"; interface PodListProps { pods: PodInfo[]; @@ -49,23 +48,6 @@ export function PodList({ pods, clusterId, namespace, onRefresh }: PodListProps) metricsEnabled ? namespace : null ); - const getPodStatusColor = (status: string) => { - switch (status.toLowerCase()) { - case "running": - return "bg-green-500"; - case "pending": - return "bg-yellow-500"; - case "succeeded": - case "completed": - return "bg-blue-500"; - case "failed": - case "error": - return "bg-red-500"; - default: - return "bg-gray-500"; - } - }; - const openEdit = async (pod: PodInfo) => { setEditError(null); try { @@ -152,9 +134,7 @@ export function PodList({ pods, clusterId, namespace, onRefresh }: PodListProps) )} {isColumnVisible("status") && ( - - {pod.status} - + )} {isColumnVisible("ready") && {pod.ready}} diff --git a/src/components/Kubernetes/PortForwardDialog.tsx b/src/components/Kubernetes/PortForwardDialog.tsx new file mode 100644 index 00000000..f53bb737 --- /dev/null +++ b/src/components/Kubernetes/PortForwardDialog.tsx @@ -0,0 +1,181 @@ +import React from "react"; +import { Loader2 } from "lucide-react"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, + Button, + Input, + Label, +} from "@/components/ui"; +import { startPortForwardCmd } from "@/lib/tauriCommands"; + +interface PortForwardDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + clusterId: string; + namespace: string; + podName?: string; +} + +export function PortForwardDialog({ + open, + onOpenChange, + clusterId, + namespace, + podName, +}: PortForwardDialogProps) { + const [pod, setPod] = React.useState(podName ?? ""); + const [containerPort, setContainerPort] = React.useState(""); + const [localPort, setLocalPort] = React.useState(""); + const [loading, setLoading] = React.useState(false); + const [error, setError] = React.useState(null); + const [success, setSuccess] = React.useState(false); + + React.useEffect(() => { + if (open) { + setPod(podName ?? ""); + setContainerPort(""); + setLocalPort(""); + setError(null); + setSuccess(false); + } + }, [open, podName]); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(null); + setSuccess(false); + + const podValue = pod.trim(); + if (!podValue) { + setError("Pod name is required."); + return; + } + + const portNum = parseInt(containerPort, 10); + if (isNaN(portNum) || portNum < 1 || portNum > 65535) { + setError("Container port must be a valid number between 1 and 65535."); + return; + } + + let localPortNum: number | undefined; + if (localPort.trim() !== "") { + localPortNum = parseInt(localPort, 10); + if (isNaN(localPortNum) || localPortNum < 1 || localPortNum > 65535) { + setError("Local port must be a valid number between 1 and 65535."); + return; + } + } + + setLoading(true); + try { + await startPortForwardCmd({ + cluster_id: clusterId, + namespace, + pod: podValue, + container_port: portNum, + local_port: localPortNum, + }); + setSuccess(true); + onOpenChange(false); + } catch (err) { + setError(err instanceof Error ? err.message : String(err)); + } finally { + setLoading(false); + } + }; + + const isPodReadonly = podName !== undefined; + + return ( + + + + Start Port Forward + + +
void handleSubmit(e)} className="space-y-4 py-2"> +
+ + +
+ +
+ + setPod(e.target.value)} + placeholder="e.g. nginx-abc123" + readOnly={isPodReadonly} + disabled={isPodReadonly || loading} + /> +
+ +
+ + setContainerPort(e.target.value)} + placeholder="80" + disabled={loading} + /> +
+ +
+ + setLocalPort(e.target.value)} + placeholder="auto" + disabled={loading} + /> +
+ + {error && ( +
+ {error} +
+ )} + + {success && ( +
+ Port forward started successfully. +
+ )} + + + + + +
+
+
+ ); +} diff --git a/src/lib/tauriCommands.ts b/src/lib/tauriCommands.ts index 7889bfc9..ec9c15fe 100644 --- a/src/lib/tauriCommands.ts +++ b/src/lib/tauriCommands.ts @@ -1530,14 +1530,13 @@ export const startPtyExecSessionCmd = ( namespace: string, podName: string, containerName: string | null, - shell: string + _shell: string ) => invoke("start_pty_exec_session", { clusterId, namespace, - podName, - containerName, - shell, + pod: podName, + container: containerName, }); export const startPtyAttachSessionCmd = ( @@ -1549,8 +1548,8 @@ export const startPtyAttachSessionCmd = ( invoke("start_pty_attach_session", { clusterId, namespace, - podName, - containerName, + pod: podName, + container: containerName, }); export const sendPtyStdinCmd = (sessionId: string, data: string) => diff --git a/src/pages/Kubernetes/PortForwardPage.tsx b/src/pages/Kubernetes/PortForwardPage.tsx index ba0ca53a..d50d59d9 100644 --- a/src/pages/Kubernetes/PortForwardPage.tsx +++ b/src/pages/Kubernetes/PortForwardPage.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import { Play, Square, Trash2, Plus, RefreshCw } from "lucide-react"; import { useKubernetesStore } from "@/stores/kubernetesStore"; import { @@ -17,8 +17,6 @@ import { startPortForwardCmd, stopPortForwardCmd, deletePortForwardCmd, - listPodsCmd, - listNamespacesCmd, } from "@/lib/tauriCommands"; import { PortForwardForm } from "@/components/Kubernetes"; @@ -29,7 +27,7 @@ export function PortForwardPage() { const [isFormOpen, setIsFormOpen] = useState(false); const [error, setError] = useState(null); - const loadPortForwards = async () => { + const loadPortForwards = useCallback(async () => { if (!selectedClusterId) return; setIsLoading(true); setError(null); @@ -41,13 +39,13 @@ export function PortForwardPage() { } finally { setIsLoading(false); } - }; + }, [selectedClusterId]); useEffect(() => { loadPortForwards(); const interval = setInterval(loadPortForwards, 5000); return () => clearInterval(interval); - }, [selectedClusterId]); + }, [loadPortForwards]); const handleStop = async (id: string) => { try { diff --git a/tests/unit/LogStreamPanel.test.tsx b/tests/unit/LogStreamPanel.test.tsx index 7d6b0c36..93b1062f 100644 --- a/tests/unit/LogStreamPanel.test.tsx +++ b/tests/unit/LogStreamPanel.test.tsx @@ -30,9 +30,6 @@ describe("LogStreamPanel — ANSI color support", () => { /> ); - // Simulate receiving log line with ANSI color codes - const logLine = "\x1b[31mError: something went wrong\x1b[0m"; - // Component should render the ANSI-colored line rerender( ({ resolve: { alias: { "@": path.resolve(__dirname, "./src") }, }, + worker: { + format: "es", + }, + optimizeDeps: { + include: [ + "ansi-to-react", + "monaco-editor/esm/vs/language/json/json.worker", + "monaco-editor/esm/vs/editor/editor.worker", + ], + }, })); -- 2.45.2 From f993672b78c888caca3a7a2657fabb56fdf125b8 Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Tue, 9 Jun 2026 20:39:10 -0500 Subject: [PATCH 3/4] 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( -- 2.45.2 From 06d29b8042aff7f4e051bd6a10a08a2eb9b6f454 Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Tue, 9 Jun 2026 20:54:26 -0500 Subject: [PATCH 4/4] fix(fmt): collapse single-expression restart count closure per rustfmt --- src-tauri/src/commands/kube.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src-tauri/src/commands/kube.rs b/src-tauri/src/commands/kube.rs index a9113172..b4862ad9 100644 --- a/src-tauri/src/commands/kube.rs +++ b/src-tauri/src/commands/kube.rs @@ -1169,11 +1169,7 @@ fn parse_pods_json(json_str: &str) -> Result, String> { .map(|container_statuses| { container_statuses .iter() - .map(|c| { - c.get("restartCount") - .and_then(|r| r.as_u64()) - .unwrap_or(0) as u32 - }) + .map(|c| c.get("restartCount").and_then(|r| r.as_u64()).unwrap_or(0) as u32) .sum::() }); -- 2.45.2