feat: Add Kubernetes documentation and mobile apps download section; update navigation and icons

This commit is contained in:
Nawaz Dhandala
2026-03-17 19:40:30 +00:00
parent 6d98893b8e
commit e6861a2abe
19 changed files with 450 additions and 57 deletions

View File

@@ -151,8 +151,8 @@ const DashboardNavbar: FunctionComponent<ComponentProps> = (
RouteMap[PageMap.KUBERNETES_CLUSTERS] as Route,
),
activeRoute: RouteMap[PageMap.KUBERNETES_CLUSTERS],
icon: IconProp.Cube,
iconColor: "teal",
icon: IconProp.Kubernetes,
iconColor: "blue",
category: "Observability",
},
// Automation & Analytics

View File

@@ -1,7 +1,8 @@
import PageComponentProps from "../PageComponentProps";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import React, { FunctionComponent, ReactElement } from "react";
import React, { Fragment, FunctionComponent, ReactElement } from "react";
import ExceptionsTable from "../../Components/Exceptions/ExceptionsTable";
import TelemetryDocumentation from "../../Components/Telemetry/Documentation";
const ArchivedExceptionsPage: FunctionComponent<PageComponentProps> = (
props: PageComponentProps,
@@ -16,13 +17,16 @@ const ArchivedExceptionsPage: FunctionComponent<PageComponentProps> = (
}
return (
<ExceptionsTable
query={{
isArchived: true,
}}
title="Archived Exceptions"
description="All the exceptions that have been archived. You will not be notified about these exceptions."
/>
<Fragment>
<ExceptionsTable
query={{
isArchived: true,
}}
title="Archived Exceptions"
description="All the exceptions that have been archived. You will not be notified about these exceptions."
/>
<TelemetryDocumentation />
</Fragment>
);
};

View File

@@ -1,7 +1,8 @@
import PageComponentProps from "../PageComponentProps";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import React, { FunctionComponent, ReactElement } from "react";
import React, { Fragment, FunctionComponent, ReactElement } from "react";
import ExceptionsTable from "../../Components/Exceptions/ExceptionsTable";
import TelemetryDocumentation from "../../Components/Telemetry/Documentation";
const ResolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
props: PageComponentProps,
@@ -16,14 +17,17 @@ const ResolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
}
return (
<ExceptionsTable
query={{
isResolved: true,
isArchived: false,
}}
title="Resolved Exceptions"
description="All the exceptions that have been resolved."
/>
<Fragment>
<ExceptionsTable
query={{
isResolved: true,
isArchived: false,
}}
title="Resolved Exceptions"
description="All the exceptions that have been resolved."
/>
<TelemetryDocumentation />
</Fragment>
);
};

View File

@@ -1,7 +1,8 @@
import PageComponentProps from "../PageComponentProps";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import React, { FunctionComponent, ReactElement } from "react";
import React, { Fragment, FunctionComponent, ReactElement } from "react";
import ExceptionsTable from "../../Components/Exceptions/ExceptionsTable";
import TelemetryDocumentation from "../../Components/Telemetry/Documentation";
const UnresolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
props: PageComponentProps,
@@ -16,14 +17,17 @@ const UnresolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
}
return (
<ExceptionsTable
query={{
isResolved: false,
isArchived: false,
}}
title="Unresolved Exceptions"
description="All the exceptions that have not been resolved."
/>
<Fragment>
<ExceptionsTable
query={{
isResolved: false,
isArchived: false,
}}
title="Unresolved Exceptions"
description="All the exceptions that have not been resolved."
/>
<TelemetryDocumentation />
</Fragment>
);
};

View File

@@ -0,0 +1,217 @@
import PageComponentProps from "../../PageComponentProps";
import ObjectID from "Common/Types/ObjectID";
import Navigation from "Common/UI/Utils/Navigation";
import KubernetesCluster from "Common/Models/DatabaseModels/KubernetesCluster";
import Card from "Common/UI/Components/Card/Card";
import React, {
Fragment,
FunctionComponent,
ReactElement,
useEffect,
useState,
} from "react";
import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI";
import API from "Common/UI/Utils/API/API";
import PageLoader from "Common/UI/Components/Loader/PageLoader";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
import MarkdownViewer from "Common/UI/Components/Markdown.tsx/MarkdownViewer";
const KubernetesClusterDocumentation: FunctionComponent<
PageComponentProps
> = (): ReactElement => {
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
const [cluster, setCluster] = useState<KubernetesCluster | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [error, setError] = useState<string>("");
const fetchCluster: PromiseVoidFunction = async (): Promise<void> => {
setIsLoading(true);
try {
const item: KubernetesCluster | null = await ModelAPI.getItem({
modelType: KubernetesCluster,
id: modelId,
select: {
name: true,
clusterIdentifier: true,
},
});
setCluster(item);
} catch (err) {
setError(API.getFriendlyMessage(err));
}
setIsLoading(false);
};
useEffect(() => {
fetchCluster().catch((err: Error) => {
setError(API.getFriendlyMessage(err));
});
}, []);
if (isLoading) {
return <PageLoader isVisible={true} />;
}
if (error) {
return <ErrorMessage message={error} />;
}
if (!cluster) {
return <ErrorMessage message="Cluster not found." />;
}
const clusterName: string = cluster.clusterIdentifier || cluster.name || "my-cluster";
const installationMarkdown: string = `
## Prerequisites
- A running Kubernetes cluster (v1.23+)
- \`kubectl\` configured to access your cluster
- \`helm\` v3 installed
- A OneUptime project API key (found in **Project Settings > API Keys**)
## Step 1: Add the OneUptime Helm Repository
\`\`\`bash
helm repo add oneuptime https://helm.oneuptime.com
helm repo update
\`\`\`
## Step 2: Install the Kubernetes Agent
\`\`\`bash
helm install kubernetes-agent oneuptime/kubernetes-agent \\
--namespace oneuptime-agent \\
--create-namespace \\
--set oneuptime.url="YOUR_ONEUPTIME_URL" \\
--set oneuptime.apiKey="YOUR_API_KEY" \\
--set clusterName="${clusterName}"
\`\`\`
Replace the following values:
- **YOUR_ONEUPTIME_URL**: The URL of your OneUptime instance (e.g., \`https://oneuptime.example.com\`)
- **YOUR_API_KEY**: Your project API key from OneUptime
## Step 3: Verify the Installation
Check that the agent pods are running:
\`\`\`bash
kubectl get pods -n oneuptime-agent
\`\`\`
You should see a **Deployment** pod (for metrics and events collection) and **DaemonSet** pods (one per node, for log collection):
\`\`\`
NAME READY STATUS RESTARTS AGE
kubernetes-agent-deployment-xxxxx-xxxxx 1/1 Running 0 1m
kubernetes-agent-daemonset-xxxxx 1/1 Running 0 1m
\`\`\`
## Configuration Options
### Namespace Filtering
By default, \`kube-system\` is excluded. To monitor only specific namespaces:
\`\`\`bash
helm install kubernetes-agent oneuptime/kubernetes-agent \\
--namespace oneuptime-agent \\
--create-namespace \\
--set oneuptime.url="YOUR_ONEUPTIME_URL" \\
--set oneuptime.apiKey="YOUR_API_KEY" \\
--set clusterName="${clusterName}" \\
--set "namespaceFilters.include={default,production,staging}"
\`\`\`
### Disable Log Collection
If you only need metrics and events (no pod logs):
\`\`\`bash
helm install kubernetes-agent oneuptime/kubernetes-agent \\
--namespace oneuptime-agent \\
--create-namespace \\
--set oneuptime.url="YOUR_ONEUPTIME_URL" \\
--set oneuptime.apiKey="YOUR_API_KEY" \\
--set clusterName="${clusterName}" \\
--set logs.enabled=false
\`\`\`
### Enable Control Plane Monitoring
For self-managed clusters (not EKS/GKE/AKS), you can enable control plane metrics:
\`\`\`bash
helm install kubernetes-agent oneuptime/kubernetes-agent \\
--namespace oneuptime-agent \\
--create-namespace \\
--set oneuptime.url="YOUR_ONEUPTIME_URL" \\
--set oneuptime.apiKey="YOUR_API_KEY" \\
--set clusterName="${clusterName}" \\
--set controlPlane.enabled=true
\`\`\`
> **Note:** Managed Kubernetes services (EKS, GKE, AKS) typically do not expose control plane metrics. Only enable this for self-managed clusters.
## Upgrading the Agent
\`\`\`bash
helm repo update
helm upgrade kubernetes-agent oneuptime/kubernetes-agent \\
--namespace oneuptime-agent
\`\`\`
## Uninstalling the Agent
\`\`\`bash
helm uninstall kubernetes-agent --namespace oneuptime-agent
kubectl delete namespace oneuptime-agent
\`\`\`
## What Gets Collected
The OneUptime Kubernetes Agent collects:
| Category | Data |
|----------|------|
| **Node Metrics** | CPU utilization, memory usage, filesystem usage, network I/O |
| **Pod Metrics** | CPU usage, memory usage, network I/O, restarts |
| **Container Metrics** | CPU usage, memory usage per container |
| **Cluster Metrics** | Node conditions, allocatable resources, pod counts |
| **Kubernetes Events** | Warnings, errors, scheduling events |
| **Pod Logs** | stdout/stderr logs from all containers (via DaemonSet) |
## Troubleshooting
### Agent shows "Disconnected"
1. Check that the agent pods are running: \`kubectl get pods -n oneuptime-agent\`
2. Check the agent logs: \`kubectl logs -n oneuptime-agent deployment/kubernetes-agent-deployment\`
3. Verify your OneUptime URL and API key are correct
4. Ensure your cluster can reach the OneUptime instance over the network
### No metrics appearing
1. Check that the cluster identifier matches: this cluster uses **\`${clusterName}\`**
2. Verify the RBAC permissions: \`kubectl get clusterrolebinding | grep kubernetes-agent\`
3. Check the OTel collector logs for export errors
`;
return (
<Fragment>
<Card
title="Agent Installation Guide"
description="Follow these steps to install the OneUptime Kubernetes Agent on your cluster."
>
<div className="px-2 pb-4">
<MarkdownViewer text={installationMarkdown} />
</div>
</Card>
</Fragment>
);
};
export default KubernetesClusterDocumentation;

View File

@@ -30,24 +30,15 @@ const KubernetesClusterSideMenu: FunctionComponent<ComponentProps> = (
/>
<SideMenuItem
link={{
title: "Settings",
title: "Documentation",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_SETTINGS] as Route,
RouteMap[
PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION
] as Route,
{ modelId: props.modelId },
),
}}
icon={IconProp.Settings}
/>
<SideMenuItem
link={{
title: "Delete Cluster",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_DELETE] as Route,
{ modelId: props.modelId },
),
}}
icon={IconProp.Trash}
className="danger-on-hover"
icon={IconProp.Book}
/>
</SideMenuSection>
@@ -89,12 +80,38 @@ const KubernetesClusterSideMenu: FunctionComponent<ComponentProps> = (
link={{
title: "Control Plane",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_CONTROL_PLANE] as Route,
RouteMap[
PageMap.KUBERNETES_CLUSTER_VIEW_CONTROL_PLANE
] as Route,
{ modelId: props.modelId },
),
}}
icon={IconProp.Activity}
/>
</SideMenuSection>
<SideMenuSection title="Advanced">
<SideMenuItem
link={{
title: "Settings",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_SETTINGS] as Route,
{ modelId: props.modelId },
),
}}
icon={IconProp.Settings}
/>
<SideMenuItem
link={{
title: "Delete Cluster",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_DELETE] as Route,
{ modelId: props.modelId },
),
}}
icon={IconProp.Trash}
className="danger-on-hover"
/>
</SideMenuSection>
</SideMenu>
);

View File

@@ -1,7 +1,8 @@
import PageComponentProps from "../PageComponentProps";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import React, { FunctionComponent, ReactElement } from "react";
import React, { Fragment, FunctionComponent, ReactElement } from "react";
import DashboardLogsViewer from "../../Components/Logs/LogsViewer";
import TelemetryDocumentation from "../../Components/Telemetry/Documentation";
const LogsPage: FunctionComponent<PageComponentProps> = (
props: PageComponentProps,
@@ -16,13 +17,16 @@ const LogsPage: FunctionComponent<PageComponentProps> = (
}
return (
<DashboardLogsViewer
showFilters={true}
serviceIds={[]}
limit={100}
enableRealtime={true}
id="logs"
/>
<Fragment>
<DashboardLogsViewer
showFilters={true}
serviceIds={[]}
limit={100}
enableRealtime={true}
id="logs"
/>
<TelemetryDocumentation />
</Fragment>
);
};

View File

@@ -1,7 +1,8 @@
import PageComponentProps from "../PageComponentProps";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import React, { FunctionComponent, ReactElement } from "react";
import React, { Fragment, FunctionComponent, ReactElement } from "react";
import MetricsTable from "../../Components/Metrics/MetricsTable";
import TelemetryDocumentation from "../../Components/Telemetry/Documentation";
const MetricsPage: FunctionComponent<PageComponentProps> = (
props: PageComponentProps,
@@ -15,7 +16,12 @@ const MetricsPage: FunctionComponent<PageComponentProps> = (
);
}
return <MetricsTable />;
return (
<Fragment>
<MetricsTable />
<TelemetryDocumentation />
</Fragment>
);
};
export default MetricsPage;

View File

@@ -0,0 +1,56 @@
import PageComponentProps from "../PageComponentProps";
import IconProp from "Common/Types/Icon/IconProp";
import Card from "Common/UI/Components/Card/Card";
import React, { Fragment, FunctionComponent, ReactElement } from "react";
const MobileApps: FunctionComponent<PageComponentProps> = (): ReactElement => {
return (
<Fragment>
<Card
title="OneUptime On-Call - iOS"
description="Download the OneUptime On-Call app for iOS to receive push notifications and manage on-call schedules from your iPhone or iPad."
buttons={[
{
title: "Download on the App Store",
icon: IconProp.Download,
onClick: () => {
window.open(
"https://apps.apple.com/us/app/oneuptime-on-call/id6759615391",
"_blank",
);
},
},
]}
/>
<Card
title="OneUptime On-Call - Android"
description="Download the OneUptime On-Call app for Android to receive push notifications and manage on-call schedules from your Android device."
buttons={[
{
title: "Download on Google Play",
icon: IconProp.Download,
onClick: () => {
window.open(
"https://play.google.com/store/apps/details?id=com.oneuptime.oncall",
"_blank",
);
},
},
{
title: "Download APK",
icon: IconProp.Download,
onClick: () => {
window.open(
"https://github.com/OneUptime/oneuptime/releases/latest/download/oneuptime-on-call-android-app.apk",
"_blank",
);
},
},
]}
/>
</Fragment>
);
};
export default MobileApps;

View File

@@ -147,6 +147,15 @@ const DashboardSideMenu: () => JSX.Element = (): ReactElement => {
},
icon: IconProp.Bell,
},
{
link: {
title: "Mobile Apps",
to: RouteUtil.populateRouteParams(
RouteMap[PageMap.SETTINGS_MOBILE_APPS] as Route,
),
},
icon: IconProp.DevicePhoneMobile,
},
],
},
{

View File

@@ -1,7 +1,8 @@
import PageComponentProps from "../PageComponentProps";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import React, { FunctionComponent, ReactElement } from "react";
import React, { Fragment, FunctionComponent, ReactElement } from "react";
import TraceTable from "../../Components/Traces/TraceTable";
import TelemetryDocumentation from "../../Components/Telemetry/Documentation";
const TracesPage: FunctionComponent<PageComponentProps> = (
props: PageComponentProps,
@@ -15,7 +16,12 @@ const TracesPage: FunctionComponent<PageComponentProps> = (
);
}
return <TraceTable />;
return (
<Fragment>
<TraceTable />
<TelemetryDocumentation />
</Fragment>
);
};
export default TracesPage;

View File

@@ -18,6 +18,7 @@ import KubernetesClusterViewEvents from "../Pages/Kubernetes/View/Events";
import KubernetesClusterViewControlPlane from "../Pages/Kubernetes/View/ControlPlane";
import KubernetesClusterViewDelete from "../Pages/Kubernetes/View/Delete";
import KubernetesClusterViewSettings from "../Pages/Kubernetes/View/Settings";
import KubernetesClusterViewDocumentation from "../Pages/Kubernetes/View/Documentation";
const KubernetesRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -129,6 +130,16 @@ const KubernetesRoutes: FunctionComponent<ComponentProps> = (
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION)}
element={
<KubernetesClusterViewDocumentation
{...props}
pageRoute={RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION] as Route}
/>
}
/>
</PageRoute>
</Routes>
);

View File

@@ -64,6 +64,7 @@ import SettingsLogPipelineView from "../Pages/Settings/LogPipelineView";
import SettingsLogDropFilters from "../Pages/Settings/LogDropFilters";
import SettingsLogDropFilterView from "../Pages/Settings/LogDropFilterView";
import SettingsLogScrubRules from "../Pages/Settings/LogScrubRules";
import SettingsMobileApps from "../Pages/Settings/MobileApps";
const SettingsRoutes: FunctionComponent<ComponentProps> = (
props: ComponentProps,
@@ -92,6 +93,15 @@ const SettingsRoutes: FunctionComponent<ComponentProps> = (
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_MOBILE_APPS)}
element={
<SettingsMobileApps
{...props}
pageRoute={RouteMap[PageMap.SETTINGS_MOBILE_APPS] as Route}
/>
}
/>
<PageRoute
path={RouteUtil.getLastPathForKey(PageMap.SETTINGS_AI_LOGS)}
element={

View File

@@ -59,6 +59,10 @@ export function getKubernetesBreadcrumbs(
"View Cluster",
"Settings",
]),
...BuildBreadcrumbLinksByTitles(
PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION,
["Project", "Kubernetes", "View Cluster", "Documentation"],
),
};
return breadcrumpLinksMap[path];
}

View File

@@ -226,6 +226,7 @@ enum PageMap {
KUBERNETES_CLUSTER_VIEW_CONTROL_PLANE = "KUBERNETES_CLUSTER_VIEW_CONTROL_PLANE",
KUBERNETES_CLUSTER_VIEW_DELETE = "KUBERNETES_CLUSTER_VIEW_DELETE",
KUBERNETES_CLUSTER_VIEW_SETTINGS = "KUBERNETES_CLUSTER_VIEW_SETTINGS",
KUBERNETES_CLUSTER_VIEW_DOCUMENTATION = "KUBERNETES_CLUSTER_VIEW_DOCUMENTATION",
// Code Repository
CODE_REPOSITORY_ROOT = "CODE_REPOSITORY_ROOT",
@@ -445,6 +446,9 @@ enum PageMap {
SETTINGS_NOTIFICATION_LOGS = "SETTINGS_NOTIFICATION_LOGS",
// Mobile Apps
SETTINGS_MOBILE_APPS = "SETTINGS_MOBILE_APPS",
// AI Logs
SETTINGS_AI_LOGS = "SETTINGS_AI_LOGS",

View File

@@ -69,6 +69,7 @@ export const KubernetesRoutePath: Dictionary<string> = {
[PageMap.KUBERNETES_CLUSTER_VIEW_CONTROL_PLANE]: `${RouteParams.ModelID}/control-plane`,
[PageMap.KUBERNETES_CLUSTER_VIEW_DELETE]: `${RouteParams.ModelID}/delete`,
[PageMap.KUBERNETES_CLUSTER_VIEW_SETTINGS]: `${RouteParams.ModelID}/settings`,
[PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION]: `${RouteParams.ModelID}/documentation`,
};
export const WorkflowRoutePath: Dictionary<string> = {
@@ -311,6 +312,7 @@ export const SettingsRoutePath: Dictionary<string> = {
[PageMap.SETTINGS_DANGERZONE]: "danger-zone",
[PageMap.SETTINGS_NOTIFICATION_SETTINGS]: "notification-settings",
[PageMap.SETTINGS_NOTIFICATION_LOGS]: "notification-logs",
[PageMap.SETTINGS_MOBILE_APPS]: "mobile-apps",
[PageMap.SETTINGS_AI_LOGS]: "ai-logs",
[PageMap.SETTINGS_APIKEYS]: `api-keys`,
[PageMap.SETTINGS_APIKEY_VIEW]: `api-keys/${RouteParams.ModelID}`,
@@ -1541,6 +1543,12 @@ const RouteMap: Dictionary<Route> = {
}`,
),
[PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION]: new Route(
`/dashboard/${RouteParams.ProjectID}/kubernetes/${
KubernetesRoutePath[PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION]
}`,
),
// Dashboards
[PageMap.DASHBOARDS_ROOT]: new Route(
@@ -2181,6 +2189,12 @@ const RouteMap: Dictionary<Route> = {
}`,
),
[PageMap.SETTINGS_MOBILE_APPS]: new Route(
`/dashboard/${RouteParams.ProjectID}/settings/${
SettingsRoutePath[PageMap.SETTINGS_MOBILE_APPS]
}`,
),
[PageMap.SETTINGS_AI_LOGS]: new Route(
`/dashboard/${RouteParams.ProjectID}/settings/${
SettingsRoutePath[PageMap.SETTINGS_AI_LOGS]

View File

@@ -320,6 +320,7 @@ enum IconProp {
TableCellsIcon = "TableCellsIcon",
UserIcon = "UserIcon",
XCircle = "XCircle",
Kubernetes = "Kubernetes",
}
export default IconProp;

View File

@@ -2733,6 +2733,28 @@ const Icon: FunctionComponent<ComponentProps> = ({
d="m15 11.25 1.5 1.5.75-.75V8.758l2.276-.61a3 3 0 1 0-3.675-3.675l-.61 2.277H12l-.75.75 1.5 1.5M15 11.25l-8.47 8.47c-.34.34-.8.53-1.28.53s-.94-.19-1.28-.53a1.818 1.818 0 0 1 0-2.56l8.47-8.47M15 11.25 12 8.25"
/>,
);
} else if (icon === IconProp.Kubernetes) {
// Kubernetes helm wheel icon (heptagon outline + inner wheel spokes)
return getSvgWrapper(
<>
{/* Heptagonal border */}
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 2l5.09 2.33 3.41 4.67.5 5.5-2.09 5.17L14.5 22h-5l-4.41-2.33L3 14.5l.5-5.5L6.91 4.33Z"
/>
{/* Center circle */}
<circle cx="12" cy="12" r="2" strokeWidth="1.5" fill="none" stroke="currentColor" />
{/* Spokes radiating from center - 7 directions */}
<line x1="12" y1="10" x2="12" y2="5" strokeWidth="1.5" stroke="currentColor" strokeLinecap="round" />
<line x1="13.9" y1="10.7" x2="16.5" y2="6.5" strokeWidth="1.5" stroke="currentColor" strokeLinecap="round" />
<line x1="13.9" y1="13.3" x2="17.5" y2="14" strokeWidth="1.5" stroke="currentColor" strokeLinecap="round" />
<line x1="12" y1="14" x2="14.5" y2="18" strokeWidth="1.5" stroke="currentColor" strokeLinecap="round" />
<line x1="10.1" y1="13.3" x2="6.5" y2="14" strokeWidth="1.5" stroke="currentColor" strokeLinecap="round" />
<line x1="10.1" y1="10.7" x2="7.5" y2="6.5" strokeWidth="1.5" stroke="currentColor" strokeLinecap="round" />
<line x1="12" y1="14" x2="9.5" y2="18" strokeWidth="1.5" stroke="currentColor" strokeLinecap="round" />
</>,
);
}
return <></>;

View File

@@ -208,7 +208,7 @@ const CodeBlock: FunctionComponent<{
(language ? language.charAt(0).toUpperCase() + language.slice(1) : "");
return (
<div className="relative rounded-lg mt-4 mb-4 overflow-hidden border border-gray-700">
<div className="relative rounded-md mt-3 mb-3 overflow-hidden border border-gray-700">
{/* Header bar */}
<div className="flex items-center justify-between px-3 py-1.5 bg-gray-800/60 border-b border-gray-700/60">
<span className="text-[11px] font-medium uppercase tracking-wider text-gray-400 select-none">
@@ -261,7 +261,7 @@ const CodeBlock: FunctionComponent<{
children={content}
language={language}
style={vscDarkPlus}
className="!rounded-none !mt-0 !mb-0 !bg-gray-900 !pt-6 !pb-4 !px-4 text-sm !border-0"
className="!rounded-none !mt-0 !mb-0 !bg-gray-900 !pt-3 !pb-3 !px-4 text-sm !border-0"
codeTagProps={{ className: "font-mono" }}
/>
</div>