@64mb/freelens-power 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/THIRD_PARTY_LICENSES +41 -0
- package/out/main/ai.js +9 -6
- package/out/main/ai.js.map +1 -1
- package/out/main/common/ai-extras.js +17 -0
- package/out/main/common/ai-extras.js.map +1 -1
- package/out/main/common/dashboard-readiness.js +88 -0
- package/out/main/common/dashboard-readiness.js.map +1 -0
- package/out/main/common/dashboard-selector.js +9 -1
- package/out/main/common/dashboard-selector.js.map +1 -1
- package/out/main/common/dashboard-views.js +2 -0
- package/out/main/common/dashboard-views.js.map +1 -1
- package/out/main/dashboard-network.js +104 -0
- package/out/main/dashboard-network.js.map +1 -0
- package/out/main/dashboard-views.js +98 -21
- package/out/main/dashboard-views.js.map +1 -1
- package/out/renderer/ai-extras.js +41 -12
- package/out/renderer/ai-extras.js.map +1 -1
- package/out/renderer/ai-message.js +1 -0
- package/out/renderer/ai-message.js.map +1 -1
- package/out/renderer/ai-provider-icon.js +44 -0
- package/out/renderer/ai-provider-icon.js.map +1 -0
- package/out/renderer/ai-shortcuts.js +1 -1
- package/out/renderer/ai-shortcuts.js.map +1 -1
- package/out/renderer/ai.js +53 -10
- package/out/renderer/ai.js.map +1 -1
- package/out/renderer/appearance.js +2 -1
- package/out/renderer/appearance.js.map +1 -1
- package/out/renderer/assets/claude.js +6 -0
- package/out/renderer/assets/claude.js.map +1 -0
- package/out/renderer/dashboard.js +39 -4
- package/out/renderer/dashboard.js.map +1 -1
- package/out/renderer/index.js +2 -0
- package/out/renderer/index.js.map +1 -1
- package/out/renderer/menus/secure-node-shell-menu.js +1 -1
- package/out/renderer/menus/secure-node-shell-menu.js.map +1 -1
- package/out/renderer/monitoring.js +671 -91
- package/out/renderer/monitoring.js.map +1 -1
- package/out/renderer/pod-monitoring.js +214 -0
- package/out/renderer/pod-monitoring.js.map +1 -0
- package/out/renderer/preferences/ai-preferences.js +2 -2
- package/out/renderer/preferences/ai-preferences.js.map +1 -1
- package/out/renderer/preferences/dashboard-preferences.js +1 -8
- package/out/renderer/preferences/dashboard-preferences.js.map +1 -1
- package/out/renderer/preferences/preferences.js +1 -1
- package/out/renderer/preferences/preferences.js.map +1 -1
- package/out/renderer/preferences/settings-icon-action.js +3 -1
- package/out/renderer/preferences/settings-icon-action.js.map +1 -1
- package/out/renderer/src/common/ai-extras.js +17 -0
- package/out/renderer/src/common/ai-extras.js.map +1 -1
- package/out/renderer/src/common/dashboard-selector.js.map +1 -1
- package/out/renderer/src/common/dashboard-views.js +2 -0
- package/out/renderer/src/common/dashboard-views.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"monitoring.js","names":[],"sources":["../../src/renderer/monitoring.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport ReactDOM from \"react-dom\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\n\nexport type MonitoringSample = { time: number; cpu: number; memory: number };\nconst INTERVAL = 30_000;\nexport const MAX_SAMPLES = 60;\n\nexport function parseQuantity(raw: string, memory = false): number {\n const match = /^(\\d+(?:\\.\\d+)?)(n|u|m|Ki|Mi|Gi|Ti|Pi|Ei|k|K|M|G|T|P|E)?$/.exec(raw);\n if (!match) throw new Error(\"Invalid metrics quantity\");\n const suffix = match[2] ?? \"\";\n const factors: Record<string, number> = {\n n: 1e-9,\n u: 1e-6,\n m: 1e-3,\n k: 1e3,\n K: 1e3,\n M: 1e6,\n G: 1e9,\n T: 1e12,\n P: 1e15,\n E: 1e18,\n };\n const value =\n Number(match[1]) *\n (suffix.endsWith(\"i\")\n ? 1024 ** ([\"Ki\", \"Mi\", \"Gi\", \"Ti\", \"Pi\", \"Ei\"].indexOf(suffix) + 1)\n : (factors[suffix] ?? 1));\n if (!Number.isFinite(value) || (!memory && suffix.endsWith(\"i\"))) throw new Error(\"Invalid metrics quantity\");\n return value;\n}\n\nexport function aggregateMetrics(\n nodes: { usage: { cpu: string; memory: string }; timestamp: string }[],\n now = Date.now(),\n): MonitoringSample {\n if (!nodes.length) throw new Error(\"No node metrics available\");\n let cpu = 0;\n let memory = 0;\n for (const node of nodes) {\n const timestamp = Date.parse(node.timestamp);\n if (!Number.isFinite(timestamp) || now - timestamp > 120_000 || timestamp > now + 30_000)\n throw new Error(\"Node metrics are stale\");\n cpu += parseQuantity(node.usage.cpu);\n memory += parseQuantity(node.usage.memory, true);\n }\n return { time: now, cpu, memory };\n}\n\nfunction MonitoringCharts({ samples, status }: { samples: MonitoringSample[]; status: string }) {\n return (\n <div style={{ display: \"grid\", gridTemplateColumns: \"repeat(2,minmax(0,1fr))\", gap: 16, marginBottom: 16 }}>\n {([\"cpu\", \"memory\"] as const).map((metric) => {\n const last = samples.at(-1);\n const format = (value: number) =>\n metric === \"cpu\" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;\n const max = Math.max(...samples.map((sample) => sample[metric]), 0.001) * 1.1;\n const end = last?.time ?? Date.now();\n const start = samples[0]?.time ?? end;\n const points = samples\n .map(\n (sample) =>\n `${8 + ((sample.time - start) / Math.max(end - start, INTERVAL)) * 384},${112 - (sample[metric] / max) * 100}`,\n )\n .join(\" \");\n return (\n <section\n key={metric}\n style={{\n padding: 16,\n border: \"1px solid var(--borderFaintColor)\",\n borderRadius: 8,\n background: \"var(--contentColor)\",\n minWidth: 0,\n }}\n aria-label={`${metric === \"cpu\" ? \"CPU\" : \"Memory\"} metrics-server usage`}\n >\n <div style={{ display: \"flex\", justifyContent: \"space-between\", gap: 8 }}>\n <strong>{metric === \"cpu\" ? \"CPU\" : \"Memory\"}</strong>\n <span>{status ? \"Unavailable\" : last ? format(last[metric]) : \"Collecting…\"}</span>\n </div>\n <svg\n viewBox=\"0 0 400 128\"\n role=\"img\"\n aria-label={`${metric} usage over collected samples`}\n style={{ width: \"100%\", height: 128, color: \"var(--primary)\" }}\n >\n {[12, 62, 112].map((y) => (\n <line key={y} x1=\"8\" x2=\"392\" y1={y} y2={y} stroke=\"var(--borderFaintColor)\" />\n ))}\n <polyline points={points} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" />\n {samples.map((sample) => (\n <circle\n key={sample.time}\n cx={8 + ((sample.time - start) / Math.max(end - start, INTERVAL)) * 384}\n cy={112 - (sample[metric] / max) * 100}\n r=\"2\"\n fill=\"currentColor\"\n >\n <title>\n {new Date(sample.time).toLocaleTimeString()}: {format(sample[metric])}\n </title>\n </circle>\n ))}\n </svg>\n <div\n style={{\n display: \"flex\",\n justifyContent: \"space-between\",\n color: \"var(--textColorSecondary)\",\n fontSize: 11,\n }}\n >\n <span>{samples.length ? new Date(start).toLocaleTimeString() : \"Waiting for samples\"}</span>\n <span>{samples.length ? new Date(end).toLocaleTimeString() : \"\"}</span>\n </div>\n <div style={{ marginTop: 8, fontSize: 11, color: \"var(--textColorSecondary)\" }} role=\"status\">\n {status || \"metrics-server · reported node usage · session samples\"}\n </div>\n </section>\n );\n })}\n </div>\n );\n}\n\nexport function startMonitoring(): () => void {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let stopCurrent: (() => void) | undefined;\n const stop = reaction(\n () => preferences.enabled,\n (enabled) => {\n stopCurrent?.();\n stopCurrent = enabled ? mountMonitoring() : undefined;\n },\n { fireImmediately: true },\n );\n return () => {\n stop();\n stopCurrent?.();\n };\n}\n\nfunction mountMonitoring(): () => void {\n let host: HTMLElement | undefined;\n let overview: Element | null = null;\n let samples: MonitoringSample[] = [];\n let status = \"\";\n let cluster: string | undefined;\n let stopped = false;\n let pendingGeneration: number | undefined;\n let generation = 0;\n let nextSample = 0;\n const hidden = new Map<HTMLElement, string | null>();\n const restore = () => {\n for (const [element, original] of hidden) {\n if (original === null) element.removeAttribute(\"style\");\n else element.setAttribute(\"style\", original);\n }\n hidden.clear();\n if (host) {\n ReactDOM.unmountComponentAtNode(host);\n host.remove();\n host = undefined;\n }\n };\n const update = async () => {\n const currentCluster = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const currentOverview = document.querySelector('[data-testid=\"cluster-overview-page\"]');\n if (cluster !== currentCluster || overview !== currentOverview) {\n generation++;\n restore();\n samples = [];\n status = \"\";\n nextSample = 0;\n cluster = currentCluster;\n overview = currentOverview;\n }\n samples = samples.filter((sample) => sample.time >= Date.now() - MAX_SAMPLES * INTERVAL);\n if (!overview || !cluster || document.visibilityState === \"hidden\") return;\n if (!host) {\n host = document.createElement(\"div\");\n host.setAttribute(\"data-freelens-power-monitoring\", \"\");\n overview.prepend(host);\n }\n for (const title of overview.querySelectorAll<HTMLElement>(\".PieChart > .chart-title\")) {\n if (title.textContent !== \"CPU\" && title.textContent !== \"Memory\") continue;\n const panel = title.closest(\".PieChart\")?.parentElement;\n if (panel && !hidden.has(panel)) {\n hidden.set(panel, panel.getAttribute(\"style\"));\n panel.style.display = \"none\";\n }\n }\n const render = () => {\n if (host) ReactDOM.render(<MonitoringCharts samples={samples} status={status} />, host);\n };\n render();\n if (pendingGeneration === generation || Date.now() < nextSample) return;\n pendingGeneration = generation;\n nextSample = Date.now() + INTERVAL;\n const requestGeneration = generation;\n const isCurrent = () =>\n !stopped &&\n requestGeneration === generation &&\n document.visibilityState !== \"hidden\" &&\n overview?.isConnected === true &&\n Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster;\n try {\n const nodes = await Renderer.K8sApi.nodesMetricsApi.list();\n if (!isCurrent()) return;\n samples = [...samples, aggregateMetrics(nodes ?? [])].slice(-MAX_SAMPLES);\n status = \"\";\n } catch {\n if (!isCurrent()) return;\n status = \"Node metrics unavailable, stale or access denied. Last collected samples remain visible.\";\n } finally {\n if (pendingGeneration === requestGeneration) pendingGeneration = undefined;\n }\n if (!stopped && requestGeneration === generation) render();\n };\n const timer = window.setInterval(() => {\n void update();\n }, 1000);\n void update();\n return () => {\n stopped = true;\n generation++;\n window.clearInterval(timer);\n restore();\n samples = [];\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"monitoring.js","names":[],"sources":["../../src/renderer/monitoring.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport { type CSSProperties, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\n\ntype ResourceValues = { requests?: Partial<Record<string, string>>; limits?: Partial<Record<string, string>> };\ntype ResourceContainer = { resources?: ResourceValues; restartPolicy?: string };\nexport type MonitoringPod = {\n spec?: {\n nodeName?: string;\n containers?: ResourceContainer[];\n initContainers?: ResourceContainer[];\n overhead?: Partial<Record<string, string>>;\n resources?: ResourceValues;\n };\n status?: { phase?: string };\n};\nexport type MonitoringNode = {\n metadata: { name: string };\n status?: { capacity?: Partial<Record<string, string>>; allocatable?: Partial<Record<string, string>> };\n};\ntype ResourceTotals = {\n capacity?: number;\n allocatable?: number;\n requests?: number;\n limits?: number;\n unbounded?: boolean;\n};\nexport type MonitoringSample = {\n time: number;\n cpu: number;\n memory: number;\n resources?: Record<\"cpu\" | \"memory\", ResourceTotals>;\n resourceStatus?: string;\n completeUsage?: boolean;\n};\n\nexport function effectivePodResource(\n pod: MonitoringPod,\n metric: \"cpu\" | \"memory\",\n kind: \"requests\" | \"limits\",\n): number {\n const read = (resources?: ResourceValues) => {\n const raw = resources?.[kind]?.[metric];\n if (raw !== undefined) return parseQuantity(raw, metric === \"memory\");\n if (kind === \"requests\" && resources?.limits?.[metric] !== undefined)\n return parseQuantity(resources.limits[metric], metric === \"memory\");\n return kind === \"requests\" ? 0 : Infinity;\n };\n const spec = pod.spec;\n if (!spec?.containers?.length) throw new Error(\"Missing pod containers\");\n let sidecars = 0;\n let initPeak = 0;\n for (const container of spec.initContainers ?? []) {\n const value = read(container.resources);\n if (container.restartPolicy === \"Always\") {\n sidecars += value;\n initPeak = Math.max(initPeak, sidecars);\n } else initPeak = Math.max(initPeak, sidecars + value);\n }\n let effective = Math.max(\n initPeak,\n sidecars + spec.containers.reduce((sum, container) => sum + read(container.resources), 0),\n );\n if (spec.resources?.[kind]?.[metric] !== undefined) effective = read(spec.resources);\n const overhead = spec.overhead?.[metric];\n if (overhead !== undefined && (kind === \"requests\" || effective > 0))\n effective += parseQuantity(overhead, metric === \"memory\");\n return effective;\n}\n\nexport function aggregateResources(\n nodes: MonitoringNode[],\n pods?: MonitoringPod[],\n): Record<\"cpu\" | \"memory\", ResourceTotals> {\n if (!nodes.length || nodes.some((node) => !node.metadata?.name)) throw new Error(\"Missing node inventory\");\n const names = new Set(nodes.map((node) => node.metadata.name));\n const scheduled = pods?.filter(\n (pod) =>\n pod.spec?.nodeName &&\n names.has(pod.spec.nodeName) &&\n pod.status?.phase !== \"Succeeded\" &&\n pod.status?.phase !== \"Failed\",\n );\n const sum = (metric: \"cpu\" | \"memory\"): ResourceTotals => {\n const capacity = nodes.reduce(\n (total, node) => total + parseQuantity(node.status?.capacity?.[metric] ?? \"\", metric === \"memory\"),\n 0,\n );\n const allocatable = nodes.reduce(\n (total, node) => total + parseQuantity(node.status?.allocatable?.[metric] ?? \"\", metric === \"memory\"),\n 0,\n );\n const requests = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, \"requests\"), 0);\n const limits = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, \"limits\"), 0);\n return {\n capacity,\n allocatable,\n requests,\n limits: limits === Infinity ? undefined : limits,\n unbounded: limits === Infinity,\n };\n };\n return { cpu: sum(\"cpu\"), memory: sum(\"memory\") };\n}\nconst INTERVAL = 30_000;\nexport const MAX_SAMPLES = 60;\n\nexport function parseQuantity(raw: string, memory = false): number {\n const match = /^(\\d+(?:\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+|n|u|m|Ki|Mi|Gi|Ti|Pi|Ei|k|K|M|G|T|P|E)?$/.exec(raw);\n if (!match) throw new Error(\"Invalid metrics quantity\");\n const suffix = match[2] ?? \"\";\n const factors: Record<string, number> = {\n n: 1e-9,\n u: 1e-6,\n m: 1e-3,\n k: 1e3,\n K: 1e3,\n M: 1e6,\n G: 1e9,\n T: 1e12,\n P: 1e15,\n E: 1e18,\n };\n const value =\n Number(match[1]) *\n (suffix.endsWith(\"i\")\n ? 1024 ** ([\"Ki\", \"Mi\", \"Gi\", \"Ti\", \"Pi\", \"Ei\"].indexOf(suffix) + 1)\n : /^[eE][+-]?\\d+$/.test(suffix)\n ? 10 ** Number(suffix.slice(1))\n : (factors[suffix] ?? 1));\n if (!Number.isFinite(value) || (!memory && suffix.endsWith(\"i\"))) throw new Error(\"Invalid metrics quantity\");\n return value;\n}\n\nexport function aggregateMetrics(\n nodes: { usage: { cpu: string; memory: string }; timestamp: string }[],\n now = Date.now(),\n): MonitoringSample {\n if (!nodes.length) throw new Error(\"No node metrics available\");\n let cpu = 0;\n let memory = 0;\n for (const node of nodes) {\n const timestamp = Date.parse(node.timestamp);\n if (!Number.isFinite(timestamp) || now - timestamp > 120_000 || timestamp > now + 30_000)\n throw new Error(\"Node metrics are stale\");\n cpu += parseQuantity(node.usage.cpu);\n memory += parseQuantity(node.usage.memory, true);\n }\n return { time: now, cpu, memory };\n}\n\nexport function formatMonitoringTime(time: number): string {\n return new Date(time).toLocaleTimeString([], {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hourCycle: \"h23\",\n });\n}\n\nfunction ResourceTitle({ metric, label }: { metric: \"cpu\" | \"memory\"; label?: string }) {\n return (\n <strong style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6 }}>\n <span className=\"Icon material\" aria-hidden=\"true\" style={{ \"--size\": \"18px\", fontWeight: 400 } as CSSProperties}>\n <span className=\"icon\">{metric === \"cpu\" ? \"developer_board\" : \"memory\"}</span>\n </span>\n {label ?? (metric === \"cpu\" ? \"CPU\" : \"Memory\")}\n </strong>\n );\n}\n\nexport function MetricChart({\n metric,\n samples,\n status,\n scope = \"node\",\n}: {\n metric: \"cpu\" | \"memory\";\n samples: MonitoringSample[];\n status: string;\n scope?: \"node\" | \"pod\";\n}) {\n const [selectedTime, setSelectedTime] = useState<number>();\n const [pinned, setPinned] = useState(false);\n const previousMetric = useRef(metric);\n useEffect(() => {\n if (\n previousMetric.current !== metric ||\n (selectedTime !== undefined && !samples.some((sample) => sample.time === selectedTime))\n ) {\n setPinned(false);\n setSelectedTime(undefined);\n }\n previousMetric.current = metric;\n }, [metric, samples, selectedTime]);\n useEffect(() => {\n if (!pinned) return;\n const escape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n setPinned(false);\n setSelectedTime(undefined);\n }\n };\n document.addEventListener(\"keydown\", escape, true);\n return () => document.removeEventListener(\"keydown\", escape, true);\n }, [pinned]);\n const chartRef = useRef<SVGSVGElement>(null);\n const [width, setWidth] = useState(600);\n useLayoutEffect(() => {\n const chart = chartRef.current;\n if (!chart) return;\n const measure = () => {\n const measured = chart.getBoundingClientRect().width;\n if (measured > 0) setWidth(measured);\n };\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(chart);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener(\"resize\", measure);\n };\n }, []);\n const plotWidth = Math.max(1, width - 112);\n const last = samples.at(-1);\n const selected = samples.find((sample) => sample.time === selectedTime);\n const label = metric === \"cpu\" ? \"CPU\" : \"Memory\";\n const format = (value: number) =>\n metric === \"cpu\" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;\n const series = [\n { key: \"usage\", label: \"Usage\", color: \"#c93dce\", value: (sample: MonitoringSample) => sample[metric] },\n ...([\"requests\", \"allocatable\", \"capacity\"] as const).map((key) => ({\n key,\n label: { requests: \"Requests\", allocatable: \"Allocatable Capacity\", capacity: \"Capacity\" }[key],\n color: { requests: \"#4caf50\", allocatable: \"#5486b5\", capacity: \"var(--textColorSecondary)\" }[key],\n value: (sample: MonitoringSample) => sample.resources?.[metric][key],\n })),\n ];\n const max = Math.max(...samples.flatMap((sample) => series.map((line) => line.value(sample) ?? 0)), 0.001) * 1.1;\n const end = last?.time ?? Date.now();\n const start = samples[0]?.time ?? end;\n const duration = Math.max(end - start, INTERVAL);\n const x = (sample: MonitoringSample) => 88 + ((sample.time - start) / duration) * plotWidth;\n const y = (sample: MonitoringSample) => 208 - (sample[metric] / max) * 184;\n const selectNearest = (event: React.MouseEvent<SVGSVGElement>) => {\n const bounds = event.currentTarget.getBoundingClientRect();\n if (!bounds.width || !samples.length) return;\n const scale = Math.min(bounds.width / width, (bounds.height || 250) / 250);\n const offset = (bounds.width - width * scale) / 2;\n const position = (event.clientX - bounds.left - offset) / scale;\n const time = start + ((position - 88) / plotWidth) * duration;\n const nearest = samples.reduce((nearest, sample) =>\n Math.abs(sample.time - time) < Math.abs(nearest.time - time) ? sample : nearest,\n ).time;\n setSelectedTime(nearest);\n return nearest;\n };\n return (\n <section\n aria-label={`${label} metrics-server usage`}\n style={{\n padding: \"var(--power-monitoring-trend-top, 16px) 16px 16px\",\n background: \"var(--contentColor)\",\n minWidth: 0,\n minHeight: 280,\n position: \"relative\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 12 }}>\n <ResourceTitle metric={metric} />\n <span>{status ? \"Unavailable\" : last ? format(last[metric]) : \"Collecting…\"}</span>\n </div>\n <svg\n ref={chartRef}\n data-monitoring-trend=\"\"\n viewBox={`0 0 ${width} 250`}\n role=\"img\"\n tabIndex={0}\n aria-label={`${label} usage over collected samples. Use arrow keys to inspect samples. Click or press Enter to pin the tooltip, Escape to dismiss`}\n onMouseMove={(event) => {\n if (!pinned) selectNearest(event);\n }}\n onClick={(event) => {\n if (pinned) setPinned(false);\n else if (samples.length) {\n if (selectNearest(event) === undefined && !selected) setSelectedTime(last?.time);\n setPinned(true);\n }\n }}\n onMouseLeave={() => {\n if (!pinned) setSelectedTime(undefined);\n }}\n onFocus={() => {\n if (!pinned) setSelectedTime(last?.time);\n }}\n onBlur={() => {\n if (!pinned) setSelectedTime(undefined);\n }}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n if (samples.length) {\n if (!selected) setSelectedTime(last?.time);\n setPinned(!pinned);\n }\n return;\n }\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n const index = samples.findIndex((sample) => sample.time === selectedTime);\n setSelectedTime(\n samples[\n Math.max(\n 0,\n Math.min(\n samples.length - 1,\n (index < 0 ? samples.length - 1 : index) + (event.key === \"ArrowLeft\" ? -1 : 1),\n ),\n )\n ]?.time,\n );\n }}\n style={{ width: \"100%\", height: 250, color: \"#c93dce\", display: \"block\" }}\n >\n <title>Click to pin tooltip · click again or Escape to unpin</title>\n {[0, 0.5, 1].map((fraction) => (\n <g key={fraction}>\n <line\n x1=\"88\"\n x2={width - 24}\n y1={208 - fraction * 184}\n y2={208 - fraction * 184}\n stroke=\"var(--borderFaintColor)\"\n />\n <text x=\"80\" y={212 - fraction * 184} textAnchor=\"end\" fill=\"var(--textColorSecondary)\" fontSize=\"11\">\n {format(max * fraction)}\n </text>\n </g>\n ))}\n {series.map((line) => (\n <path\n key={line.key}\n data-monitoring-series={line.key}\n d={samples.reduce((path, sample, index) => {\n const value = line.value(sample);\n if (value === undefined) return path;\n const previous = index ? line.value(samples[index - 1]) : undefined;\n return `${path} ${previous === undefined ? \"M\" : \"L\"}${x(sample)},${208 - (value / max) * 184}`;\n }, \"\")}\n fill=\"none\"\n stroke={line.color}\n strokeWidth=\"2\"\n strokeDasharray={line.key === \"capacity\" || line.key === \"allocatable\" ? \"5 3\" : undefined}\n />\n ))}\n {samples.map((sample) => (\n <circle key={sample.time} cx={x(sample)} cy={y(sample)} r=\"2\" fill=\"currentColor\">\n <title>\n {formatMonitoringTime(sample.time)}: {format(sample[metric])}\n </title>\n </circle>\n ))}\n {samples.length > 0 &&\n [0, 0.5, 1].map((fraction) => (\n <text\n key={fraction}\n x={88 + fraction * plotWidth}\n y=\"235\"\n textAnchor={fraction === 0 ? \"start\" : fraction === 1 ? \"end\" : \"middle\"}\n fill=\"var(--textColorSecondary)\"\n fontSize=\"11\"\n >\n {formatMonitoringTime(start + fraction * duration)}\n </text>\n ))}\n {selected && (\n <line\n data-monitoring-crosshair=\"\"\n x1={x(selected)}\n x2={x(selected)}\n y1=\"24\"\n y2=\"208\"\n stroke=\"var(--textColorSecondary)\"\n strokeDasharray=\"4 3\"\n />\n )}\n </svg>\n <style>{`\n @keyframes freelens-power-metric-tooltip-enter { from { opacity:0; transform:translateY(2px) } to { opacity:1; transform:translateY(0) } }\n [data-monitoring-tooltip] { animation:freelens-power-metric-tooltip-enter 140ms ease-out }\n @media (prefers-reduced-motion:reduce) { [data-monitoring-tooltip] { animation:none } }\n `}</style>\n {selected && (\n <div\n data-monitoring-tooltip=\"\"\n role=\"tooltip\"\n style={{\n position: \"absolute\",\n top: 44,\n right: 24,\n padding: 8,\n background: \"var(--contentColor)\",\n border: \"1px solid var(--borderFaintColor)\",\n pointerEvents: \"none\",\n }}\n >\n <div>\n {formatMonitoringTime(selected.time)}\n {pinned ? \" · Pinned\" : \"\"}\n </div>\n {series.map((line) => (\n <div key={line.key} style={{ color: line.color }}>\n {line.label}: {line.value(selected) === undefined ? \"Unavailable\" : format(line.value(selected)!)}\n </div>\n ))}\n <div style={{ color: \"var(--textColorSecondary)\", marginTop: 4, fontSize: 10 }}>\n {pinned ? \"Click chart or Escape to unpin\" : \"Click or Enter to pin\"}\n </div>\n </div>\n )}\n <div\n aria-label={`${label} chart legend`}\n style={{ display: \"flex\", flexWrap: \"wrap\", gap: \"6px 16px\", marginBottom: 8 }}\n >\n {series.map((line) => (\n <span key={line.key}>\n <span style={{ color: line.color }}>●</span> {line.label}\n </span>\n ))}\n </div>\n {last?.resourceStatus && (\n <div role=\"status\" style={{ color: \"var(--textColorSecondary)\", marginBottom: 8 }}>\n {last.resourceStatus}\n </div>\n )}\n <div\n style={{ fontSize: 11, color: \"var(--textColorSecondary)\", display: \"flex\", alignItems: \"center\", gap: 4 }}\n role=\"status\"\n aria-label={`${scope} metrics status`}\n >\n <sup aria-hidden=\"true\" style={{ fontSize: 12 }}>\n *\n </sup>\n <span>{status || (samples.length ? \"metrics-server data\" : \"Waiting for metrics-server data\")}</span>\n </div>\n </section>\n );\n}\n\nfunction ResourceDonut({\n metric,\n sample,\n status,\n}: {\n metric: \"cpu\" | \"memory\";\n sample?: MonitoringSample;\n status: string;\n}) {\n const [activeRing, setActiveRing] = useState<number>();\n useEffect(() => {\n if (activeRing === undefined) return;\n const dismiss = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n event.stopPropagation();\n setActiveRing(undefined);\n };\n document.addEventListener(\"keydown\", dismiss, true);\n return () => document.removeEventListener(\"keydown\", dismiss, true);\n }, [activeRing]);\n const label = metric === \"cpu\" ? \"CPU\" : \"Memory\";\n const totals = sample?.resources?.[metric];\n const format = (value: number) =>\n metric === \"cpu\" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;\n const available = (value?: number) => (value === undefined ? \"Unavailable\" : format(value));\n const denominator = totals?.allocatable;\n const rings = [\n { label: \"Usage\", color: \"#c93dce\", value: sample?.[metric] },\n { label: \"Requests\", color: \"#4caf50\", value: totals?.requests },\n ];\n const percentage = (value?: number) =>\n value !== undefined && denominator !== undefined && denominator > 0 ? (value / denominator) * 100 : undefined;\n const usagePercent = percentage(sample?.[metric]);\n const unused =\n sample?.completeUsage && denominator !== undefined ? format(denominator - sample[metric]) : \"Unavailable\";\n const unrequested =\n denominator !== undefined && totals?.requests !== undefined ? format(denominator - totals.requests) : \"Unavailable\";\n const allocationDetail = `Allocatable: ${available(denominator)}. Schedulable capacity after system reservations, used as the denominator for both rings. Physical Capacity: ${available(totals?.capacity)}. Unused allocatable (usage): ${unused}. Unrequested: ${unrequested}`;\n const hovered = activeRing === undefined ? undefined : rings[activeRing];\n const tooltipId = `power-monitoring-${metric}-donut-tooltip`;\n return (\n <section\n aria-label={`${label} resource summary`}\n style={{\n padding: \"var(--power-monitoring-summary-top, 16px) 16px 16px\",\n minWidth: 0,\n background: \"var(--contentColor)\",\n position: \"relative\",\n overflow: \"visible\",\n }}\n >\n <ResourceTitle metric={metric} label={metric === \"memory\" ? \"RAM\" : \"CPU\"} />\n <svg\n data-monitoring-donut=\"\"\n viewBox=\"0 0 200 200\"\n role=\"img\"\n aria-label={`${label} usage and requests relative to allocatable capacity`}\n style={{ display: \"block\", width: \"100%\", height: 200 }}\n >\n {rings.map((ring, index) => {\n const radius = 76 - index * 18;\n const percent = percentage(ring.value);\n const detail = `${ring.label}: ${available(ring.value)}${percent === undefined ? \"\" : ` (${percent.toFixed(1)}% of allocatable${percent > 100 ? \", over allocatable\" : \"\"})`}`;\n return (\n <g\n key={ring.label}\n tabIndex={0}\n role=\"img\"\n aria-label={detail}\n aria-describedby={activeRing === index ? tooltipId : undefined}\n onMouseEnter={() => setActiveRing(index)}\n onMouseMove={() => setActiveRing(index)}\n onMouseLeave={() => setActiveRing(undefined)}\n onFocus={() => setActiveRing(index)}\n onBlur={() => setActiveRing(undefined)}\n >\n <circle cx=\"100\" cy=\"100\" r={radius} fill=\"none\" stroke=\"var(--borderFaintColor)\" strokeWidth=\"12\" />\n {percent !== undefined && (\n <circle\n data-monitoring-ring={ring.label}\n cx=\"100\"\n cy=\"100\"\n r={radius}\n fill=\"none\"\n stroke={ring.color}\n strokeWidth=\"12\"\n pathLength=\"100\"\n strokeDasharray={`${Math.min(100, Math.max(0, percent))} 100`}\n transform=\"rotate(-90 100 100)\"\n />\n )}\n </g>\n );\n })}\n <text\n data-monitoring-usage-percentage=\"\"\n x=\"100\"\n y=\"100\"\n textAnchor=\"middle\"\n fill=\"var(--textColorPrimary)\"\n fontSize=\"26\"\n fontWeight=\"600\"\n >\n {usagePercent === undefined ? \"N/A\" : `${usagePercent.toFixed(1)}%`}\n </text>\n <text x=\"100\" y=\"120\" textAnchor=\"middle\" fill=\"var(--textColorPrimary)\" fontSize=\"12\">\n Usage\n </text>\n </svg>\n {hovered && (\n <div\n id={tooltipId}\n data-monitoring-tooltip=\"\"\n role=\"tooltip\"\n style={{\n position: \"absolute\",\n top: 48,\n left: 8,\n right: 8,\n zIndex: 20,\n padding: 8,\n border: \"1px solid var(--borderFaintColor)\",\n background: \"var(--contentColor)\",\n pointerEvents: \"none\",\n overflowWrap: \"anywhere\",\n boxShadow: \"0 3px 12px #0003\",\n }}\n >\n <div style={{ color: hovered.color }}>\n {hovered.label}: {available(hovered.value)}\n </div>\n <div>\n {percentage(hovered.value) === undefined\n ? \"Percentage unavailable\"\n : `${percentage(hovered.value)!.toFixed(1)}% of allocatable${percentage(hovered.value)! > 100 ? \" (over allocatable)\" : \"\"}`}\n </div>\n <div>Allocatable: {available(denominator)}</div>\n </div>\n )}\n <div aria-label={`${label} resource totals`} style={{ display: \"grid\", gap: 6, fontSize: 12 }}>\n {rings.map((ring) => (\n <div\n key={ring.label}\n title={`${ring.label} relative to allocatable capacity. ${ring.label === \"Usage\" ? `Unused allocatable (usage): ${unused}` : `Unrequested: ${unrequested}`}`}\n >\n <span style={{ color: ring.color }}>●</span> {ring.label}: {available(ring.value)}\n {percentage(ring.value) !== undefined && <span> ({percentage(ring.value)!.toFixed(1)}%)</span>}\n </div>\n ))}\n <div title={allocationDetail}>\n <span style={{ color: \"#5486b5\" }}>●</span> Allocatable: {available(denominator)}\n </div>\n </div>\n {(status || sample?.resourceStatus) && (\n <div role=\"status\" style={{ color: \"var(--textColorSecondary)\", fontSize: 11, marginTop: 8 }}>\n {status || sample?.resourceStatus}\n </div>\n )}\n </section>\n );\n}\n\nfunction MonitoringCharts({ samples, status }: { samples: MonitoringSample[]; status: string }) {\n return (\n <div style={{ containerType: \"inline-size\" }}>\n <style>{`\n [data-monitoring-layout] { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(0,1.6fr) minmax(0,1fr) minmax(0,1fr); gap:16px; align-items:stretch }\n [data-monitoring-trends], [data-monitoring-summaries] { display:contents }\n @container (min-width: 961px) {\n [data-monitoring-summaries] > section { --power-monitoring-summary-top:16px }\n }\n @container (max-width: 960px) {\n [data-monitoring-layout] { grid-template-columns:repeat(2,minmax(0,1fr)) }\n .power-ai-overview-actions + [data-testid=\"cluster-overview-page\"] [data-monitoring-trends] > section { --power-monitoring-trend-top:56px }\n }\n @container (max-width: 580px) {\n [data-monitoring-layout] { grid-template-columns:minmax(0,1fr) }\n .power-ai-overview-actions + [data-testid=\"cluster-overview-page\"] [data-monitoring-trends] > section:not(:first-child) { --power-monitoring-trend-top:16px }\n }\n `}</style>\n <div data-monitoring-layout=\"\">\n <div data-monitoring-trends=\"\">\n {([\"cpu\", \"memory\"] as const).map((metric) => (\n <MetricChart key={metric} metric={metric} samples={samples} status={status} />\n ))}\n </div>\n <div data-monitoring-summaries=\"\">\n {([\"cpu\", \"memory\"] as const).map((metric) => (\n <ResourceDonut key={metric} metric={metric} sample={samples.at(-1)} status={status} />\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nexport function startMonitoring(): () => void {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let stopCurrent: (() => void) | undefined;\n const stop = reaction(\n () => preferences.enabled,\n (enabled) => {\n stopCurrent?.();\n stopCurrent = enabled ? mountMonitoring() : undefined;\n },\n { fireImmediately: true },\n );\n return () => {\n stop();\n stopCurrent?.();\n };\n}\n\nfunction mountMonitoring(): () => void {\n const suppression = document.createElement(\"style\");\n suppression.setAttribute(\"data-freelens-power-native-metrics-suppression\", \"\");\n suppression.textContent = `\n [data-testid=\"cluster-overview-page\"] > [class*=\"ClusterMetrics\"],\n [data-testid=\"cluster-overview-page\"] > :has([class*=\"ClusterMetrics\"]),\n [data-testid=\"cluster-overview-page\"] > :is([class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]):not(:has(.PieChart)),\n [data-testid=\"cluster-overview-page\"] > :has([class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]):not(:has(.PieChart)) {\n display:none !important;\n }\n `;\n document.head.append(suppression);\n let host: HTMLElement | undefined;\n let overview: Element | null = null;\n let samples: MonitoringSample[] = [];\n let status = \"\";\n let renderedSamples: MonitoringSample[] | undefined;\n let renderedStatus: string | undefined;\n let cluster: string | undefined;\n let stopped = false;\n let pendingGeneration: number | undefined;\n let generation = 0;\n let nextSample = 0;\n const hidden = new Map<HTMLElement, string | null>();\n const restore = () => {\n for (const [element, original] of hidden) {\n if (original === null) element.removeAttribute(\"style\");\n else element.setAttribute(\"style\", original);\n }\n hidden.clear();\n if (host) {\n ReactDOM.unmountComponentAtNode(host);\n host.remove();\n host = undefined;\n renderedSamples = undefined;\n renderedStatus = undefined;\n }\n };\n const update = async () => {\n const currentCluster = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const currentOverview = document.querySelector('[data-testid=\"cluster-overview-page\"]');\n if (cluster !== currentCluster || overview !== currentOverview) {\n generation++;\n restore();\n samples = [];\n status = \"\";\n nextSample = 0;\n cluster = currentCluster;\n overview = currentOverview;\n }\n const cutoff = Date.now() - MAX_SAMPLES * INTERVAL;\n if (samples.length && samples[0].time < cutoff) samples = samples.filter((sample) => sample.time >= cutoff);\n if (!overview || !cluster || document.visibilityState === \"hidden\") return;\n if (!host) {\n host = document.createElement(\"div\");\n host.setAttribute(\"data-freelens-power-monitoring\", \"\");\n host.style.gridColumn = \"1 / -1\";\n hidden.set(overview as HTMLElement, overview.getAttribute(\"style\"));\n Object.assign((overview as HTMLElement).style, {\n gridTemplateAreas: \"none\",\n gridTemplateColumns: \"minmax(0, 1fr)\",\n gridTemplateRows: \"none\",\n height: \"auto\",\n });\n overview.prepend(host);\n }\n for (const title of overview.querySelectorAll<HTMLElement>(\".PieChart > .chart-title\")) {\n if (title.textContent !== \"CPU\" && title.textContent !== \"Memory\") continue;\n const panel = title.closest(\".PieChart\")?.parentElement;\n if (panel && panel.style.display !== \"none\") {\n if (!hidden.has(panel)) hidden.set(panel, panel.getAttribute(\"style\"));\n panel.style.display = \"none\";\n }\n }\n // native CSS modules retain [ClusterMetrics] in the generated class name\n for (const child of overview.children) {\n if (!(child instanceof HTMLElement) || child === host) continue;\n const nativeMetrics =\n child.matches('[class*=\"ClusterMetrics\"]') || child.querySelector('[class*=\"ClusterMetrics\"]');\n const unavailablePies =\n (child.matches('[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]') ||\n child.querySelector(\n '[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]',\n )) &&\n !child.querySelector(\".PieChart\");\n if ((nativeMetrics || unavailablePies) && child.style.display !== \"none\") {\n if (!hidden.has(child)) hidden.set(child, child.getAttribute(\"style\"));\n child.style.display = \"none\";\n }\n if (!hidden.has(child)) hidden.set(child, child.getAttribute(\"style\"));\n child.style.gridArea = \"auto\";\n }\n const render = () => {\n if (host && (renderedSamples !== samples || renderedStatus !== status)) {\n ReactDOM.render(<MonitoringCharts samples={samples} status={status} />, host);\n renderedSamples = samples;\n renderedStatus = status;\n }\n };\n render();\n if (pendingGeneration === generation || Date.now() < nextSample) return;\n pendingGeneration = generation;\n nextSample = Date.now() + INTERVAL;\n const requestGeneration = generation;\n const isCurrent = () =>\n !stopped &&\n requestGeneration === generation &&\n document.visibilityState !== \"hidden\" &&\n overview?.isConnected === true &&\n Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster;\n try {\n const [metrics, inventory, pods] = await Promise.allSettled([\n Renderer.K8sApi.nodesMetricsApi.list(),\n Promise.resolve().then(() => Renderer.K8sApi.nodesApi.list()),\n Promise.resolve().then(() => Renderer.K8sApi.podsApi.list()),\n ]);\n if (!isCurrent()) return;\n if (metrics.status !== \"fulfilled\") throw new Error(\"Metrics unavailable\");\n const sample = aggregateMetrics(metrics.value ?? []);\n const warnings: string[] = [];\n if (inventory.status === \"fulfilled\") {\n try {\n sample.resources = aggregateResources(\n inventory.value ?? [],\n pods.status === \"fulfilled\" ? (pods.value ?? undefined) : undefined,\n );\n const expected = new Set((inventory.value ?? []).map((node) => node.metadata.name));\n const observed = new Set((metrics.value ?? []).map((node) => node.metadata.name));\n sample.completeUsage =\n expected.size > 0 && expected.size === observed.size && [...expected].every((name) => observed.has(name));\n if (!sample.completeUsage)\n warnings.push(\n \"Partial node metrics coverage: usage is the reported subset; unused allocatable unavailable\",\n );\n } catch {\n warnings.push(\"Resource totals unavailable: incomplete or invalid node/pod data\");\n }\n } else\n warnings.push(\"Node inventory unavailable or access denied; usage coverage unverified, capacity unavailable\");\n if (pods.status !== \"fulfilled\")\n warnings.push(\"Cluster-wide Pods unavailable or access denied; requests unavailable\");\n sample.resourceStatus = warnings.join(\". \");\n samples = [...samples, sample].slice(-MAX_SAMPLES);\n status = \"\";\n } catch {\n if (!isCurrent()) return;\n status = \"Node metrics unavailable, stale or access denied. Last collected samples remain visible.\";\n } finally {\n if (pendingGeneration === requestGeneration) pendingGeneration = undefined;\n }\n if (!stopped && requestGeneration === generation) render();\n };\n const observer = new MutationObserver(() => {\n void update();\n });\n observer.observe(document.body, { childList: true, subtree: true });\n const timer = window.setInterval(() => {\n void update();\n }, 1000);\n void update();\n return () => {\n stopped = true;\n generation++;\n window.clearInterval(timer);\n observer.disconnect();\n suppression.remove();\n restore();\n samples = [];\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
|
|
2
|
+
const require__global_external_mobx = require("./_virtual/_global-external_mobx.js");
|
|
3
|
+
const require_monitoring_preferences_store = require("./src/common/store/monitoring-preferences-store.js");
|
|
4
|
+
const require__global_external_react_dom = require("./_virtual/_global-external_react-dom.js");
|
|
5
|
+
const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
|
|
6
|
+
const require_monitoring = require("./monitoring.js");
|
|
7
|
+
//#region src/renderer/pod-monitoring.tsx
|
|
8
|
+
var INTERVAL = 3e4;
|
|
9
|
+
function aggregatePodMetrics(metrics, identity, now = Date.now()) {
|
|
10
|
+
if (metrics.metadata?.name !== identity.name || metrics.metadata?.namespace !== identity.namespace) throw new Error("Pod metrics identity changed");
|
|
11
|
+
const time = Date.parse(metrics.timestamp);
|
|
12
|
+
if (!Number.isFinite(time) || now - time > 12e4 || time > now + 3e4) throw new Error("Pod metrics are stale");
|
|
13
|
+
if (!metrics.containers?.length) throw new Error("Missing container metrics");
|
|
14
|
+
const names = /* @__PURE__ */ new Set();
|
|
15
|
+
let cpu = 0;
|
|
16
|
+
let memory = 0;
|
|
17
|
+
for (const container of metrics.containers) {
|
|
18
|
+
if (!container.name || names.has(container.name)) throw new Error("Invalid container metrics");
|
|
19
|
+
names.add(container.name);
|
|
20
|
+
cpu += require_monitoring.parseQuantity(container.usage.cpu);
|
|
21
|
+
memory += require_monitoring.parseQuantity(container.usage.memory, true);
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
time,
|
|
25
|
+
cpu,
|
|
26
|
+
memory
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function selectedPod() {
|
|
30
|
+
const match = new URLSearchParams(window.location.search).get("kube-details")?.match(/^\/api\/v1\/namespaces\/([a-z0-9][a-z0-9.-]*)\/pods\/([a-z0-9][a-z0-9.-]*)$/);
|
|
31
|
+
if (match) return {
|
|
32
|
+
namespace: match[1],
|
|
33
|
+
name: match[2]
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function visible(element) {
|
|
37
|
+
for (let node = element; node; node = node.parentElement) {
|
|
38
|
+
const style = getComputedStyle(node);
|
|
39
|
+
if (node.hidden || node.getAttribute("aria-hidden") === "true" || style.display === "none" || style.visibility === "hidden") return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
function startPodMonitoring() {
|
|
44
|
+
const preferences = require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate();
|
|
45
|
+
let dispose;
|
|
46
|
+
const stop = require__global_external_mobx.reaction(() => preferences.enabled, (enabled) => {
|
|
47
|
+
dispose?.();
|
|
48
|
+
dispose = enabled ? mountPodMonitoring() : void 0;
|
|
49
|
+
}, { fireImmediately: true });
|
|
50
|
+
return () => {
|
|
51
|
+
stop();
|
|
52
|
+
dispose?.();
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function mountPodMonitoring() {
|
|
56
|
+
let stopped = false;
|
|
57
|
+
let generation = 0;
|
|
58
|
+
let session = "";
|
|
59
|
+
let samples = [];
|
|
60
|
+
let status = "";
|
|
61
|
+
let resourceUid;
|
|
62
|
+
let nextRead = 0;
|
|
63
|
+
let pending;
|
|
64
|
+
let host;
|
|
65
|
+
let graph;
|
|
66
|
+
let originalStyle = null;
|
|
67
|
+
let renderedSamples;
|
|
68
|
+
let renderedStatus;
|
|
69
|
+
let renderedMetric;
|
|
70
|
+
const restore = () => {
|
|
71
|
+
if (host) {
|
|
72
|
+
require__global_external_react_dom.default.unmountComponentAtNode(host);
|
|
73
|
+
host.remove();
|
|
74
|
+
}
|
|
75
|
+
host = void 0;
|
|
76
|
+
if (graph) if (originalStyle === null) graph.removeAttribute("style");
|
|
77
|
+
else graph.setAttribute("style", originalStyle);
|
|
78
|
+
graph = void 0;
|
|
79
|
+
renderedSamples = void 0;
|
|
80
|
+
};
|
|
81
|
+
const reset = () => {
|
|
82
|
+
generation++;
|
|
83
|
+
session = "";
|
|
84
|
+
samples = [];
|
|
85
|
+
resourceUid = void 0;
|
|
86
|
+
status = "";
|
|
87
|
+
nextRead = 0;
|
|
88
|
+
restore();
|
|
89
|
+
};
|
|
90
|
+
const update = async () => {
|
|
91
|
+
if (stopped) return;
|
|
92
|
+
const identity = selectedPod();
|
|
93
|
+
const cluster = require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid;
|
|
94
|
+
const drawer = document.querySelector(".KubeObjectDetails");
|
|
95
|
+
const title = drawer?.querySelector(".drawer-title-text");
|
|
96
|
+
const titleText = title ? [...title.childNodes].filter((node) => node.nodeType === Node.TEXT_NODE).map((node) => node.textContent).join("").trim() : "";
|
|
97
|
+
if (!identity || !cluster || !drawer || !visible(drawer) || document.visibilityState === "hidden" || titleText !== `Pod: ${identity.name}` || !drawer.querySelector(".PodDetails")) {
|
|
98
|
+
if (session) reset();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const key = JSON.stringify([
|
|
102
|
+
cluster,
|
|
103
|
+
identity.namespace,
|
|
104
|
+
identity.name
|
|
105
|
+
]);
|
|
106
|
+
if (session !== key) {
|
|
107
|
+
reset();
|
|
108
|
+
session = key;
|
|
109
|
+
}
|
|
110
|
+
const metrics = [...drawer.querySelectorAll(".ResourceMetrics")].find((element) => [
|
|
111
|
+
"CPU",
|
|
112
|
+
"Memory",
|
|
113
|
+
"Network",
|
|
114
|
+
"Filesystem"
|
|
115
|
+
].every((label) => [...element.querySelectorAll(".Radio .label")].some((node) => node.textContent === label)));
|
|
116
|
+
const selected = metrics?.querySelector(".Radio.checked .label")?.textContent;
|
|
117
|
+
const nativeGraph = metrics?.querySelector(":scope > .graph");
|
|
118
|
+
if (!metrics || !visible(metrics) || !nativeGraph || selected !== "CPU" && selected !== "Memory") {
|
|
119
|
+
restore();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const retained = samples.filter((sample) => sample.time >= Date.now() - 30 * 6e4);
|
|
123
|
+
if (retained.length !== samples.length) samples = retained;
|
|
124
|
+
const metric = selected === "CPU" ? "cpu" : "memory";
|
|
125
|
+
if (graph !== nativeGraph || !host?.isConnected) {
|
|
126
|
+
restore();
|
|
127
|
+
graph = nativeGraph;
|
|
128
|
+
originalStyle = graph.getAttribute("style");
|
|
129
|
+
graph.style.display = "none";
|
|
130
|
+
host = document.createElement("div");
|
|
131
|
+
host.dataset.powerPodMonitoring = "";
|
|
132
|
+
graph.after(host);
|
|
133
|
+
}
|
|
134
|
+
const render = () => {
|
|
135
|
+
if (host && (renderedSamples !== samples || renderedStatus !== status || renderedMetric !== metric)) {
|
|
136
|
+
require__global_external_react_dom.default.render(/* @__PURE__ */ require_jsx_runtime.jsx(require_monitoring.MetricChart, {
|
|
137
|
+
metric,
|
|
138
|
+
samples,
|
|
139
|
+
status,
|
|
140
|
+
scope: "pod"
|
|
141
|
+
}), host);
|
|
142
|
+
renderedSamples = samples;
|
|
143
|
+
renderedStatus = status;
|
|
144
|
+
renderedMetric = metric;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
render();
|
|
148
|
+
if (pending === generation || Date.now() < nextRead) return;
|
|
149
|
+
const current = generation;
|
|
150
|
+
pending = current;
|
|
151
|
+
nextRead = Date.now() + INTERVAL;
|
|
152
|
+
const isCurrent = () => !stopped && current === generation && session === key && drawer.isConnected && visible(drawer) && document.visibilityState !== "hidden" && require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster && JSON.stringify(selectedPod()) === JSON.stringify(identity);
|
|
153
|
+
try {
|
|
154
|
+
const [usage, pod] = await Promise.allSettled([Promise.resolve().then(() => require_extensions.Renderer.K8sApi.podsMetricsApi.list({ namespace: identity.namespace }, { fieldSelector: `metadata.name=${identity.name}` })), Promise.resolve().then(() => require_extensions.Renderer.K8sApi.podsApi.get(identity))]);
|
|
155
|
+
if (!isCurrent()) return;
|
|
156
|
+
const currentPod = pod.status === "fulfilled" && pod.value?.metadata.name === identity.name && pod.value.metadata.namespace === identity.namespace ? pod.value : void 0;
|
|
157
|
+
if (currentPod) {
|
|
158
|
+
const nextUid = currentPod.metadata.uid;
|
|
159
|
+
if (nextUid && resourceUid && nextUid !== resourceUid) samples = [];
|
|
160
|
+
if (nextUid) resourceUid = nextUid;
|
|
161
|
+
}
|
|
162
|
+
if (usage.status !== "fulfilled" || !Array.isArray(usage.value)) throw new Error("Metrics unavailable");
|
|
163
|
+
const matches = usage.value.filter((item) => item.metadata?.name === identity.name && item.metadata.namespace === identity.namespace);
|
|
164
|
+
if (matches.length !== 1) throw new Error("Metrics unavailable");
|
|
165
|
+
const podMetrics = matches[0];
|
|
166
|
+
const sample = aggregatePodMetrics(podMetrics, identity);
|
|
167
|
+
if (currentPod) {
|
|
168
|
+
const created = currentPod.metadata.creationTimestamp;
|
|
169
|
+
if (created !== void 0) {
|
|
170
|
+
const creationTime = Date.parse(created);
|
|
171
|
+
if (!Number.isFinite(creationTime)) throw new Error("Invalid pod creation time");
|
|
172
|
+
samples = samples.filter((entry) => entry.time >= creationTime);
|
|
173
|
+
if (sample.time < creationTime) throw new Error("Pod metrics predate the current pod");
|
|
174
|
+
}
|
|
175
|
+
if (podMetrics.metadata.uid && resourceUid && podMetrics.metadata.uid !== resourceUid) throw new Error("Pod metrics identity changed");
|
|
176
|
+
try {
|
|
177
|
+
const spec = currentPod;
|
|
178
|
+
sample.resources = Object.fromEntries(["cpu", "memory"].map((resource) => {
|
|
179
|
+
const limits = require_monitoring.effectivePodResource(spec, resource, "limits");
|
|
180
|
+
return [resource, {
|
|
181
|
+
requests: require_monitoring.effectivePodResource(spec, resource, "requests"),
|
|
182
|
+
limits: Number.isFinite(limits) ? limits : void 0,
|
|
183
|
+
unbounded: limits === Infinity
|
|
184
|
+
}];
|
|
185
|
+
}));
|
|
186
|
+
} catch {
|
|
187
|
+
sample.resourceStatus = "Pod requests unavailable: incomplete resource data";
|
|
188
|
+
}
|
|
189
|
+
} else sample.resourceStatus = "Pod requests unavailable or access denied";
|
|
190
|
+
samples = [...samples.filter((entry) => entry.time !== sample.time && entry.time >= Date.now() - 30 * 6e4), sample].sort((a, b) => a.time - b.time).slice(-60);
|
|
191
|
+
status = "";
|
|
192
|
+
} catch {
|
|
193
|
+
if (!isCurrent()) return;
|
|
194
|
+
status = "Pod metrics unavailable, stale or access denied. Last collected samples remain visible.";
|
|
195
|
+
} finally {
|
|
196
|
+
if (pending === current) pending = void 0;
|
|
197
|
+
}
|
|
198
|
+
if (isCurrent()) update();
|
|
199
|
+
};
|
|
200
|
+
const timer = window.setInterval(() => {
|
|
201
|
+
update();
|
|
202
|
+
}, 1e3);
|
|
203
|
+
update();
|
|
204
|
+
return () => {
|
|
205
|
+
stopped = true;
|
|
206
|
+
reset();
|
|
207
|
+
window.clearInterval(timer);
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
211
|
+
exports.aggregatePodMetrics = aggregatePodMetrics;
|
|
212
|
+
exports.startPodMonitoring = startPodMonitoring;
|
|
213
|
+
|
|
214
|
+
//# sourceMappingURL=pod-monitoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pod-monitoring.js","names":[],"sources":["../../src/renderer/pod-monitoring.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport ReactDOM from \"react-dom\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport {\n effectivePodResource,\n MAX_SAMPLES,\n MetricChart,\n type MonitoringPod,\n type MonitoringSample,\n parseQuantity,\n} from \"./monitoring\";\n\ntype PodIdentity = { name: string; namespace: string };\ntype PodMetrics = {\n metadata: PodIdentity & { uid?: string };\n timestamp: string;\n containers: { name: string; usage: { cpu: string; memory: string } }[];\n};\nconst INTERVAL = 30_000;\n\nexport function aggregatePodMetrics(metrics: PodMetrics, identity: PodIdentity, now = Date.now()): MonitoringSample {\n if (metrics.metadata?.name !== identity.name || metrics.metadata?.namespace !== identity.namespace)\n throw new Error(\"Pod metrics identity changed\");\n const time = Date.parse(metrics.timestamp);\n if (!Number.isFinite(time) || now - time > 120_000 || time > now + 30_000) throw new Error(\"Pod metrics are stale\");\n if (!metrics.containers?.length) throw new Error(\"Missing container metrics\");\n const names = new Set<string>();\n let cpu = 0;\n let memory = 0;\n for (const container of metrics.containers) {\n if (!container.name || names.has(container.name)) throw new Error(\"Invalid container metrics\");\n names.add(container.name);\n cpu += parseQuantity(container.usage.cpu);\n memory += parseQuantity(container.usage.memory, true);\n }\n return { time, cpu, memory };\n}\n\nfunction selectedPod(): PodIdentity | undefined {\n const details = new URLSearchParams(window.location.search).get(\"kube-details\");\n const match = details?.match(/^\\/api\\/v1\\/namespaces\\/([a-z0-9][a-z0-9.-]*)\\/pods\\/([a-z0-9][a-z0-9.-]*)$/);\n if (match) return { namespace: match[1], name: match[2] };\n return;\n}\n\nfunction visible(element: HTMLElement): boolean {\n for (let node: HTMLElement | null = element; node; node = node.parentElement) {\n const style = getComputedStyle(node);\n if (\n node.hidden ||\n node.getAttribute(\"aria-hidden\") === \"true\" ||\n style.display === \"none\" ||\n style.visibility === \"hidden\"\n )\n return false;\n }\n return true;\n}\n\nexport function startPodMonitoring(): () => void {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let dispose: (() => void) | undefined;\n const stop = reaction(\n () => preferences.enabled,\n (enabled) => {\n dispose?.();\n dispose = enabled ? mountPodMonitoring() : undefined;\n },\n { fireImmediately: true },\n );\n return () => {\n stop();\n dispose?.();\n };\n}\n\nfunction mountPodMonitoring(): () => void {\n let stopped = false;\n let generation = 0;\n let session = \"\";\n let samples: MonitoringSample[] = [];\n let status = \"\";\n let resourceUid: string | undefined;\n let nextRead = 0;\n let pending: number | undefined;\n let host: HTMLElement | undefined;\n let graph: HTMLElement | undefined;\n let originalStyle: string | null = null;\n let renderedSamples: MonitoringSample[] | undefined;\n let renderedStatus: string | undefined;\n let renderedMetric: string | undefined;\n const restore = () => {\n if (host) {\n ReactDOM.unmountComponentAtNode(host);\n host.remove();\n }\n host = undefined;\n if (graph) {\n if (originalStyle === null) graph.removeAttribute(\"style\");\n else graph.setAttribute(\"style\", originalStyle);\n }\n graph = undefined;\n renderedSamples = undefined;\n };\n const reset = () => {\n generation++;\n session = \"\";\n samples = [];\n resourceUid = undefined;\n status = \"\";\n nextRead = 0;\n restore();\n };\n const update = async () => {\n if (stopped) return;\n const identity = selectedPod();\n const cluster = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const drawer = document.querySelector<HTMLElement>(\".KubeObjectDetails\");\n const title = drawer?.querySelector(\".drawer-title-text\");\n const titleText = title\n ? [...title.childNodes]\n .filter((node) => node.nodeType === Node.TEXT_NODE)\n .map((node) => node.textContent)\n .join(\"\")\n .trim()\n : \"\";\n if (\n !identity ||\n !cluster ||\n !drawer ||\n !visible(drawer) ||\n document.visibilityState === \"hidden\" ||\n titleText !== `Pod: ${identity.name}` ||\n !drawer.querySelector(\".PodDetails\")\n ) {\n if (session) reset();\n return;\n }\n const key = JSON.stringify([cluster, identity.namespace, identity.name]);\n if (session !== key) {\n reset();\n session = key;\n }\n const metrics = [...drawer.querySelectorAll<HTMLElement>(\".ResourceMetrics\")].find((element) =>\n [\"CPU\", \"Memory\", \"Network\", \"Filesystem\"].every((label) =>\n [...element.querySelectorAll(\".Radio .label\")].some((node) => node.textContent === label),\n ),\n );\n const selected = metrics?.querySelector(\".Radio.checked .label\")?.textContent;\n const nativeGraph = metrics?.querySelector<HTMLElement>(\":scope > .graph\");\n if (!metrics || !visible(metrics) || !nativeGraph || (selected !== \"CPU\" && selected !== \"Memory\")) {\n restore();\n return;\n }\n const retained = samples.filter((sample) => sample.time >= Date.now() - 30 * 60_000);\n if (retained.length !== samples.length) samples = retained;\n const metric = selected === \"CPU\" ? \"cpu\" : \"memory\";\n if (graph !== nativeGraph || !host?.isConnected) {\n restore();\n graph = nativeGraph;\n originalStyle = graph.getAttribute(\"style\");\n graph.style.display = \"none\";\n host = document.createElement(\"div\");\n host.dataset.powerPodMonitoring = \"\";\n graph.after(host);\n }\n const render = () => {\n if (host && (renderedSamples !== samples || renderedStatus !== status || renderedMetric !== metric)) {\n ReactDOM.render(<MetricChart metric={metric} samples={samples} status={status} scope=\"pod\" />, host);\n renderedSamples = samples;\n renderedStatus = status;\n renderedMetric = metric;\n }\n };\n render();\n if (pending === generation || Date.now() < nextRead) return;\n const current = generation;\n pending = current;\n nextRead = Date.now() + INTERVAL;\n const isCurrent = () =>\n !stopped &&\n current === generation &&\n session === key &&\n drawer.isConnected &&\n visible(drawer) &&\n document.visibilityState !== \"hidden\" &&\n Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster &&\n JSON.stringify(selectedPod()) === JSON.stringify(identity);\n try {\n const [usage, pod] = await Promise.allSettled([\n Promise.resolve().then(() =>\n Renderer.K8sApi.podsMetricsApi.list(\n { namespace: identity.namespace },\n { fieldSelector: `metadata.name=${identity.name}` },\n ),\n ),\n Promise.resolve().then(() => Renderer.K8sApi.podsApi.get(identity)),\n ]);\n if (!isCurrent()) return;\n const currentPod =\n pod.status === \"fulfilled\" &&\n pod.value?.metadata.name === identity.name &&\n pod.value.metadata.namespace === identity.namespace\n ? pod.value\n : undefined;\n if (currentPod) {\n const nextUid = currentPod.metadata.uid;\n if (nextUid && resourceUid && nextUid !== resourceUid) samples = [];\n if (nextUid) resourceUid = nextUid;\n }\n if (usage.status !== \"fulfilled\" || !Array.isArray(usage.value)) throw new Error(\"Metrics unavailable\");\n const matches = usage.value.filter(\n (item) => item.metadata?.name === identity.name && item.metadata.namespace === identity.namespace,\n );\n if (matches.length !== 1) throw new Error(\"Metrics unavailable\");\n const podMetrics = matches[0];\n const sample = aggregatePodMetrics(podMetrics, identity);\n if (currentPod) {\n const created = currentPod.metadata.creationTimestamp;\n if (created !== undefined) {\n const creationTime = Date.parse(created);\n if (!Number.isFinite(creationTime)) throw new Error(\"Invalid pod creation time\");\n samples = samples.filter((entry) => entry.time >= creationTime);\n if (sample.time < creationTime) throw new Error(\"Pod metrics predate the current pod\");\n }\n if (podMetrics.metadata.uid && resourceUid && podMetrics.metadata.uid !== resourceUid)\n throw new Error(\"Pod metrics identity changed\");\n try {\n const spec: MonitoringPod = currentPod;\n sample.resources = Object.fromEntries(\n ([\"cpu\", \"memory\"] as const).map((resource) => {\n const limits = effectivePodResource(spec, resource, \"limits\");\n return [\n resource,\n {\n requests: effectivePodResource(spec, resource, \"requests\"),\n limits: Number.isFinite(limits) ? limits : undefined,\n unbounded: limits === Infinity,\n },\n ];\n }),\n ) as MonitoringSample[\"resources\"];\n } catch {\n sample.resourceStatus = \"Pod requests unavailable: incomplete resource data\";\n }\n } else sample.resourceStatus = \"Pod requests unavailable or access denied\";\n samples = [\n ...samples.filter((entry) => entry.time !== sample.time && entry.time >= Date.now() - 30 * 60_000),\n sample,\n ]\n .sort((a, b) => a.time - b.time)\n .slice(-MAX_SAMPLES);\n status = \"\";\n } catch {\n if (!isCurrent()) return;\n status = \"Pod metrics unavailable, stale or access denied. Last collected samples remain visible.\";\n } finally {\n if (pending === current) pending = undefined;\n }\n if (isCurrent()) void update();\n };\n const timer = window.setInterval(() => {\n void update();\n }, 1000);\n void update();\n return () => {\n stopped = true;\n reset();\n window.clearInterval(timer);\n };\n}\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ const require__global_external_mobx_react = require("../_virtual/_global-externa
|
|
|
3
3
|
const require_ai_preferences_store = require("../src/common/store/ai-preferences-store.js");
|
|
4
4
|
const require__global_external_react = require("../_virtual/_global-external_react.js");
|
|
5
5
|
const require_jsx_runtime = require("../_virtual/_global-external_react/jsx-runtime.js");
|
|
6
|
-
const
|
|
6
|
+
const require_ai_provider_icon = require("../ai-provider-icon.js");
|
|
7
7
|
const require_ai_models = require("../src/common/ai-models.js");
|
|
8
8
|
const require_ai_shortcuts_preferences = require("./ai-shortcuts-preferences.js");
|
|
9
9
|
let electron = require("electron");
|
|
@@ -111,7 +111,7 @@ var AiPreferences = require__global_external_mobx_react.observer(() => {
|
|
|
111
111
|
alignItems: "center",
|
|
112
112
|
gap: 8
|
|
113
113
|
},
|
|
114
|
-
children: [/* @__PURE__ */ require_jsx_runtime.jsx(
|
|
114
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_provider_icon.AiProviderIcon, { provider: option.value === "claude" ? "claude" : "codex" }), option.label]
|
|
115
115
|
}),
|
|
116
116
|
onChange: (option) => {
|
|
117
117
|
if (option?.value === "codex" || option?.value === "claude") preferences.provider = option.value;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-preferences.js","names":[],"sources":["../../../src/renderer/preferences/ai-preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { ipcRenderer } from \"electron\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useState } from \"react\";\nimport { AI_MODELS_CHANNEL, type AiModel } from \"../../common/ai-models\";\nimport { AiPreferencesStore } from \"../../common/store/ai-preferences-store\";\nimport { AiIcon } from \"../ai-icon\";\nimport { AiShortcutsPreferences } from \"./ai-shortcuts-preferences\";\n\nconst { Select, Switch } = Renderer.Component;\n\nexport const AiPreferences = observer(() => {\n const preferences = AiPreferencesStore.getInstanceOrCreate<AiPreferencesStore>();\n const [catalog, setCatalog] = useState<{ provider: string; models: AiModel[] }>({ provider: \"\", models: [] });\n const models = catalog.provider === preferences.provider ? catalog.models : [];\n useEffect(() => {\n if (!preferences.enabled) {\n setCatalog({ provider: preferences.provider, models: [] });\n return;\n }\n setCatalog({ provider: preferences.provider, models: [] });\n const provider = preferences.provider;\n let active = true;\n void ipcRenderer\n .invoke(AI_MODELS_CHANNEL, preferences.provider)\n .then((value: AiModel[]) => {\n if (active && Array.isArray(value)) setCatalog({ provider, models: value });\n })\n .catch(() => {});\n return () => {\n active = false;\n };\n }, [preferences.enabled, preferences.provider]);\n const isClaude = preferences.provider === \"claude\";\n const providerLabel = isClaude ? \"Claude\" : \"Codex\";\n const currentModel = isClaude ? preferences.claudeModel : preferences.model;\n const selectedModel = JSON.stringify([currentModel, isClaude ? \"\" : preferences.reasoningEffort]);\n const modelOptions = [\n { value: JSON.stringify([\"\", \"\"]), label: `${providerLabel} default` },\n ...models.flatMap((model) => [\n { value: JSON.stringify([model.id, \"\"]), label: `${model.label} · Default` },\n ...model.reasoningEfforts.map((effort) => ({\n value: JSON.stringify([model.id, effort]),\n label: `${model.label} · ${effort}`,\n })),\n ]),\n ];\n if (!modelOptions.some((option) => option.value === selectedModel)) {\n modelOptions.push({\n value: selectedModel,\n label: `${currentModel || `${providerLabel} default`} · ${isClaude ? \"Default\" : preferences.reasoningEffort || \"Default\"}`,\n });\n }\n const controlStyle = { width: 220, maxWidth: \"100%\", flexShrink: 0 };\n const rowStyle = { display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", gap: 20 };\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 20 }}>\n <style>{`.power-ai-section-title { color: var(--textColorAccent); } .power-ai-prompt { width: 100%; min-height: 90px; max-height: 260px; field-sizing: content; overflow-y: auto; margin-top: 8px; box-sizing: border-box; padding: 12px; border: 1px solid var(--borderFaintColor, #666); border-radius: 8px; background: var(--mainBackground, #242424); color: var(--textColorPrimary, white); font: inherit; resize: none; } .power-ai-prompt:focus { border-color: var(--primary, #5896ff); outline: 1px solid var(--primary, #5896ff); }`}</style>\n <div>\n <Switch\n checked={preferences.enabled}\n onChange={(value) => {\n preferences.enabled = value;\n }}\n >\n Enable AI features\n </Switch>\n <div className=\"power-settings-hint\">\n Turn off to hide the chat, AI button and Ask about cluster, and stop active requests. Settings and\n conversations are kept.\n </div>\n </div>\n <div className=\"power-settings-row\" style={rowStyle}>\n <label htmlFor=\"power-ai-cli\">CLI</label>\n <div style={controlStyle}>\n <Select\n id=\"power-ai-cli\"\n value={preferences.provider}\n options={[\n { value: \"codex\", label: \"Codex\" },\n { value: \"claude\", label: \"Claude\" },\n ]}\n formatOptionLabel={(option) => (\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 8 }}>\n <AiIcon material={option.value === \"claude\" ? \"auto_awesome\" : \"code\"} />\n {option.label}\n </span>\n )}\n onChange={(option) => {\n if (option?.value === \"codex\" || option?.value === \"claude\") preferences.provider = option.value;\n }}\n isDisabled={!preferences.enabled}\n isClearable={false}\n isSearchable={false}\n />\n </div>\n </div>\n <div className=\"power-settings-row\" style={rowStyle}>\n <div>\n <label htmlFor=\"power-ai-model\">Model</label>\n <div className=\"power-settings-hint\">\n {isClaude\n ? \"Native Claude model aliases. Availability depends on your CLI account.\"\n : \"Models and reasoning effort from the local Codex catalog.\"}\n </div>\n </div>\n <div style={controlStyle}>\n <Select\n id=\"power-ai-model\"\n value={selectedModel}\n options={modelOptions}\n isDisabled={!preferences.enabled}\n isClearable={false}\n isSearchable={true}\n onChange={(option) => {\n if (!option) return;\n const [model, effort] = JSON.parse(option.value);\n if (isClaude) preferences.claudeModel = model;\n else {\n preferences.model = model;\n preferences.reasoningEffort = effort;\n }\n }}\n />\n </div>\n </div>\n {!isClaude && (\n <div className=\"power-settings-row\" style={rowStyle}>\n <label htmlFor=\"power-ai-speed\">Speed</label>\n <div style={controlStyle}>\n <Select\n id=\"power-ai-speed\"\n value={preferences.speed}\n options={[\n { value: \"default\", label: \"Default\" },\n { value: \"fast\", label: \"Fast\" },\n ]}\n isDisabled={!preferences.enabled}\n isClearable={false}\n isSearchable={false}\n onChange={(option) => {\n if (option) preferences.speed = option.value as \"default\" | \"fast\";\n }}\n />\n </div>\n </div>\n )}\n <div className=\"power-settings-row\" style={rowStyle}>\n <div>\n <label htmlFor=\"power-ai-font-size\">Chat font size</label>\n <div className=\"power-settings-hint\">Messages and input, in pixels (10–24).</div>\n </div>\n <div style={controlStyle}>\n <Select\n id=\"power-ai-font-size\"\n value={preferences.chatFontSize}\n options={Array.from({ length: 15 }, (_, index) => ({ value: index + 10, label: `${index + 10} px` }))}\n isDisabled={!preferences.enabled}\n isSearchable={false}\n isClearable={false}\n onChange={(option) => {\n if (option) preferences.chatFontSize = option.value;\n }}\n />\n </div>\n </div>\n <div>\n <label className=\"power-ai-section-title\" htmlFor=\"power-ai-system-prompt\">\n System prompt\n </label>\n <textarea\n id=\"power-ai-system-prompt\"\n className=\"power-ai-prompt\"\n disabled={!preferences.enabled}\n value={preferences.systemPrompt}\n onChange={(event) => {\n preferences.systemPrompt = event.target.value;\n }}\n />\n </div>\n <div>\n <label className=\"power-ai-section-title\" htmlFor=\"power-ai-cluster-prompt\">\n Cluster prompt\n </label>\n <div className=\"power-settings-hint\">Additional instructions attached to cluster conversations.</div>\n <textarea\n id=\"power-ai-cluster-prompt\"\n className=\"power-ai-prompt\"\n disabled={!preferences.enabled}\n value={preferences.clusterPrompt}\n onChange={(event) => {\n preferences.clusterPrompt = event.target.value;\n }}\n />\n </div>\n <AiShortcutsPreferences disabled={!preferences.enabled} />\n </div>\n );\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-preferences.js","names":[],"sources":["../../../src/renderer/preferences/ai-preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { ipcRenderer } from \"electron\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useState } from \"react\";\nimport { AI_MODELS_CHANNEL, type AiModel } from \"../../common/ai-models\";\nimport { AiPreferencesStore } from \"../../common/store/ai-preferences-store\";\nimport { AiProviderIcon } from \"../ai-provider-icon\";\nimport { AiShortcutsPreferences } from \"./ai-shortcuts-preferences\";\n\nconst { Select, Switch } = Renderer.Component;\n\nexport const AiPreferences = observer(() => {\n const preferences = AiPreferencesStore.getInstanceOrCreate<AiPreferencesStore>();\n const [catalog, setCatalog] = useState<{ provider: string; models: AiModel[] }>({ provider: \"\", models: [] });\n const models = catalog.provider === preferences.provider ? catalog.models : [];\n useEffect(() => {\n if (!preferences.enabled) {\n setCatalog({ provider: preferences.provider, models: [] });\n return;\n }\n setCatalog({ provider: preferences.provider, models: [] });\n const provider = preferences.provider;\n let active = true;\n void ipcRenderer\n .invoke(AI_MODELS_CHANNEL, preferences.provider)\n .then((value: AiModel[]) => {\n if (active && Array.isArray(value)) setCatalog({ provider, models: value });\n })\n .catch(() => {});\n return () => {\n active = false;\n };\n }, [preferences.enabled, preferences.provider]);\n const isClaude = preferences.provider === \"claude\";\n const providerLabel = isClaude ? \"Claude\" : \"Codex\";\n const currentModel = isClaude ? preferences.claudeModel : preferences.model;\n const selectedModel = JSON.stringify([currentModel, isClaude ? \"\" : preferences.reasoningEffort]);\n const modelOptions = [\n { value: JSON.stringify([\"\", \"\"]), label: `${providerLabel} default` },\n ...models.flatMap((model) => [\n { value: JSON.stringify([model.id, \"\"]), label: `${model.label} · Default` },\n ...model.reasoningEfforts.map((effort) => ({\n value: JSON.stringify([model.id, effort]),\n label: `${model.label} · ${effort}`,\n })),\n ]),\n ];\n if (!modelOptions.some((option) => option.value === selectedModel)) {\n modelOptions.push({\n value: selectedModel,\n label: `${currentModel || `${providerLabel} default`} · ${isClaude ? \"Default\" : preferences.reasoningEffort || \"Default\"}`,\n });\n }\n const controlStyle = { width: 220, maxWidth: \"100%\", flexShrink: 0 };\n const rowStyle = { display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", gap: 20 };\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 20 }}>\n <style>{`.power-ai-section-title { color: var(--textColorAccent); } .power-ai-prompt { width: 100%; min-height: 90px; max-height: 260px; field-sizing: content; overflow-y: auto; margin-top: 8px; box-sizing: border-box; padding: 12px; border: 1px solid var(--borderFaintColor, #666); border-radius: 8px; background: var(--mainBackground, #242424); color: var(--textColorPrimary, white); font: inherit; resize: none; } .power-ai-prompt:focus { border-color: var(--primary, #5896ff); outline: 1px solid var(--primary, #5896ff); }`}</style>\n <div>\n <Switch\n checked={preferences.enabled}\n onChange={(value) => {\n preferences.enabled = value;\n }}\n >\n Enable AI features\n </Switch>\n <div className=\"power-settings-hint\">\n Turn off to hide the chat, AI button and Ask about cluster, and stop active requests. Settings and\n conversations are kept.\n </div>\n </div>\n <div className=\"power-settings-row\" style={rowStyle}>\n <label htmlFor=\"power-ai-cli\">CLI</label>\n <div style={controlStyle}>\n <Select\n id=\"power-ai-cli\"\n value={preferences.provider}\n options={[\n { value: \"codex\", label: \"Codex\" },\n { value: \"claude\", label: \"Claude\" },\n ]}\n formatOptionLabel={(option) => (\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 8 }}>\n <AiProviderIcon provider={option.value === \"claude\" ? \"claude\" : \"codex\"} />\n {option.label}\n </span>\n )}\n onChange={(option) => {\n if (option?.value === \"codex\" || option?.value === \"claude\") preferences.provider = option.value;\n }}\n isDisabled={!preferences.enabled}\n isClearable={false}\n isSearchable={false}\n />\n </div>\n </div>\n <div className=\"power-settings-row\" style={rowStyle}>\n <div>\n <label htmlFor=\"power-ai-model\">Model</label>\n <div className=\"power-settings-hint\">\n {isClaude\n ? \"Native Claude model aliases. Availability depends on your CLI account.\"\n : \"Models and reasoning effort from the local Codex catalog.\"}\n </div>\n </div>\n <div style={controlStyle}>\n <Select\n id=\"power-ai-model\"\n value={selectedModel}\n options={modelOptions}\n isDisabled={!preferences.enabled}\n isClearable={false}\n isSearchable={true}\n onChange={(option) => {\n if (!option) return;\n const [model, effort] = JSON.parse(option.value);\n if (isClaude) preferences.claudeModel = model;\n else {\n preferences.model = model;\n preferences.reasoningEffort = effort;\n }\n }}\n />\n </div>\n </div>\n {!isClaude && (\n <div className=\"power-settings-row\" style={rowStyle}>\n <label htmlFor=\"power-ai-speed\">Speed</label>\n <div style={controlStyle}>\n <Select\n id=\"power-ai-speed\"\n value={preferences.speed}\n options={[\n { value: \"default\", label: \"Default\" },\n { value: \"fast\", label: \"Fast\" },\n ]}\n isDisabled={!preferences.enabled}\n isClearable={false}\n isSearchable={false}\n onChange={(option) => {\n if (option) preferences.speed = option.value as \"default\" | \"fast\";\n }}\n />\n </div>\n </div>\n )}\n <div className=\"power-settings-row\" style={rowStyle}>\n <div>\n <label htmlFor=\"power-ai-font-size\">Chat font size</label>\n <div className=\"power-settings-hint\">Messages and input, in pixels (10–24).</div>\n </div>\n <div style={controlStyle}>\n <Select\n id=\"power-ai-font-size\"\n value={preferences.chatFontSize}\n options={Array.from({ length: 15 }, (_, index) => ({ value: index + 10, label: `${index + 10} px` }))}\n isDisabled={!preferences.enabled}\n isSearchable={false}\n isClearable={false}\n onChange={(option) => {\n if (option) preferences.chatFontSize = option.value;\n }}\n />\n </div>\n </div>\n <div>\n <label className=\"power-ai-section-title\" htmlFor=\"power-ai-system-prompt\">\n System prompt\n </label>\n <textarea\n id=\"power-ai-system-prompt\"\n className=\"power-ai-prompt\"\n disabled={!preferences.enabled}\n value={preferences.systemPrompt}\n onChange={(event) => {\n preferences.systemPrompt = event.target.value;\n }}\n />\n </div>\n <div>\n <label className=\"power-ai-section-title\" htmlFor=\"power-ai-cluster-prompt\">\n Cluster prompt\n </label>\n <div className=\"power-settings-hint\">Additional instructions attached to cluster conversations.</div>\n <textarea\n id=\"power-ai-cluster-prompt\"\n className=\"power-ai-prompt\"\n disabled={!preferences.enabled}\n value={preferences.clusterPrompt}\n onChange={(event) => {\n preferences.clusterPrompt = event.target.value;\n }}\n />\n </div>\n <AiShortcutsPreferences disabled={!preferences.enabled} />\n </div>\n );\n});\n"],"mappings":"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"}
|
|
@@ -103,8 +103,7 @@ function CardEditor({ card, onSave, onClose }) {
|
|
|
103
103
|
value: selector,
|
|
104
104
|
onChange: setSelector,
|
|
105
105
|
placeholder: "main .chart",
|
|
106
|
-
"aria-invalid": selectorInvalid
|
|
107
|
-
"aria-describedby": `${id}-selector-hint`
|
|
106
|
+
"aria-invalid": selectorInvalid
|
|
108
107
|
}),
|
|
109
108
|
selector && /* @__PURE__ */ require_jsx_runtime.jsx(require_settings_icon_action.SettingsIconAction, {
|
|
110
109
|
material: "close",
|
|
@@ -139,12 +138,6 @@ function CardEditor({ card, onSave, onClose }) {
|
|
|
139
138
|
" characters without control characters."
|
|
140
139
|
]
|
|
141
140
|
}),
|
|
142
|
-
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
143
|
-
id: `${id}-selector-hint`,
|
|
144
|
-
className: "power-settings-hint",
|
|
145
|
-
style: { marginTop: 0 },
|
|
146
|
-
children: "Leave empty to show the whole website."
|
|
147
|
-
}),
|
|
148
141
|
pickerError && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
149
142
|
role: "alert",
|
|
150
143
|
children: "Could not select an element. Try again or enter a CSS selector."
|