Skip to content

Commit

Permalink
Add some basic runtime icons (#670)
Browse files Browse the repository at this point in the history
There's more work to do here, but currently I cannot simply load in
PlatformIcon due to compatibility

getsentry/platformicons#191

At the very least this will help with things like Next.js to grok if its
server/client.


![image](https://github.com/user-attachments/assets/cb27b5dd-72c1-4e34-ac61-69b7a48331e2)

---------

Co-authored-by: Burak Yigit Kaya <[email protected]>
  • Loading branch information
dcramer and BYK authored Jan 24, 2025
1 parent 61230ef commit 83eaaa0
Show file tree
Hide file tree
Showing 6 changed files with 4,518 additions and 2,693 deletions.
5 changes: 5 additions & 0 deletions .changeset/yellow-rockets-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@spotlightjs/overlay': minor
---

Add more platform icons and some browser icons
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"getsentry",
"iife",
"imagetools",
"laravel",
"microlink",
"nextjs",
"notarytool",
Expand All @@ -25,6 +26,7 @@
"rcodesign",
"spotlightjs",
"svgr",
"symfony",
"tailwindcss",
"TEAMID",
"treeshake",
Expand Down
2 changes: 1 addition & 1 deletion packages/overlay/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"eslint-plugin-react-refresh": "^0.4.12",
"happy-dom": "^15.10.2",
"magic-string": "^0.30.11",
"platformicons": "^5.10.10",
"platformicons": "^7.0.4",
"postcss": "^8.4.47",
"react": "^18.3.1",
"react-diff-viewer-continued": "^3.4.0",
Expand Down
148 changes: 114 additions & 34 deletions packages/overlay/src/integrations/sentry/components/PlatformIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,141 @@
import { ReactComponent as AstroIcon } from 'platformicons/svg/astro.svg';
import { ReactComponent as DefaultIcon } from 'platformicons/svg/default.svg';
import { ReactComponent as DotNetIcon } from 'platformicons/svg/dotnet.svg';
import { ReactComponent as FirefoxIcon } from 'platformicons/svg/firefox.svg';
import { ReactComponent as ChromeIcon } from 'platformicons/svg/google.svg';
import { ReactComponent as JavaScriptIcon } from 'platformicons/svg/javascript.svg';
import { ReactComponent as PhpLaravelIcon } from 'platformicons/svg/laravel.svg';
import { ReactComponent as DotNetMauiIcon } from 'platformicons/svg/maui.svg';
import { ReactComponent as NestJsIcon } from 'platformicons/svg/nestjs.svg';
import { ReactComponent as NextJsIcon } from 'platformicons/svg/nextjs.svg';
import { ReactComponent as NodeIcon } from 'platformicons/svg/nodejs.svg';
import { ReactComponent as PhpIcon } from 'platformicons/svg/php.svg';
import { ReactComponent as PythonIcon } from 'platformicons/svg/python.svg';
import { ReactComponent as RemixIcon } from 'platformicons/svg/remix.svg';
import { ReactComponent as RubyIcon } from 'platformicons/svg/ruby.svg';
import { ReactComponent as SafariIcon } from 'platformicons/svg/safari.svg';
import { ReactComponent as PhpSymfonyIcon } from 'platformicons/svg/symfony.svg';
import type { SentryEvent } from '../types';

import { SentryEvent } from '../types';

import { ComponentPropsWithoutRef } from 'react';
import type { ComponentPropsWithoutRef } from 'react';

type Platform = 'python' | 'javascript' | 'node' | 'ruby' | 'csharp' | string;

export default function PlatformIcon({
platform,
type PlatformIconProps = ComponentPropsWithoutRef<'svg'> & {
size?: number;
platform?: Platform;
event?: SentryEvent;
height?: number;
width?: number;
title?: string;
};

type IconMap = Record<
string,
React.FunctionComponent<
React.SVGProps<SVGSVGElement> & {
title?: string;
}
>
>;

const BROWSER_ICON_MAP: IconMap = {
Safari: SafariIcon,
Chrome: ChromeIcon,
Firefox: FirefoxIcon,
} as const;

const DefaultSDKIcon = DefaultIcon;
const SDK_ICON_MAP: IconMap = {
'sentry.javascript.nextjs': NextJsIcon,
'sentry.javascript.astro': AstroIcon,
'sentry.javascript.remix': RemixIcon,
'sentry.javascript.nestjs': NestJsIcon,
ruby: RubyIcon,
python: PythonIcon,
javascript: JavaScriptIcon,
node: NodeIcon,
php: PhpIcon,
'php.laravel': PhpLaravelIcon,
'php.symfony': PhpSymfonyIcon,
dotnet: DotNetIcon,
'dotnet.maui': DotNetMauiIcon,
csharp: DotNetIcon,
} as const;

export default function PlatformIcon({ platform, event, size = 42, title, ...props }: PlatformIconProps) {
return (
<WrappedIcon platform={platform} event={event} size={size} title={title} {...props}>
<CorePlatformIcon platform={platform} event={event} size={size} title={title} {...props} />
</WrappedIcon>
);
}

function WrappedIcon({ event, size = 42, ...props }: PlatformIconProps) {
const wrappedWidth = size / 3;
const wrappedHeight = size / 3;

return (
<div className="relative">
{props.children}
<RuntimeIcon
event={event}
size={size}
width={wrappedWidth}
height={wrappedHeight}
{...props}
className="absolute bottom-1 right-1"
/>
</div>
);
}

function RuntimeIcon({
event,
size,
width = 42,
height = 42,
title,
size = 42,
...props
}: ComponentPropsWithoutRef<'svg'> & {
size?: number;
platform?: Platform;
event?: SentryEvent;
height?: number;
width?: number;
title?: string;
}) {
const name = platform || event?.platform || 'unknown';
switch (name) {
case 'ruby':
return <RubyIcon title={title} width={size ?? width} height={size ?? height} {...props} />;
case 'python':
return <PythonIcon title={title} width={size ?? width} height={size ?? height} {...props} />;
case 'javascript.astro':
return <AstroIcon title={title} width={size ?? width} height={size ?? height} {...props} />;
case 'javascript':
return <JavaScriptIcon title={title} width={size ?? width} height={size ?? height} {...props} />;
const runtimeName = `${event?.contexts?.runtime?.name || ''}`;
if (!runtimeName) return null;

const runtimeTitle = `${runtimeName} ${event?.contexts?.runtime?.version}`;
switch (runtimeName) {
case 'node':
return <NodeIcon title={title} width={size ?? width} height={size ?? height} {...props} />;
case 'php':
return <PhpIcon title={title} width={size ?? width} height={size ?? height} {...props} />;
case 'php.laravel':
return <PhpLaravelIcon title={title} width={size ?? width} height={size ?? height} {...props} />;
case 'php.symfony':
return <PhpSymfonyIcon title={title} width={size ?? width} height={size ?? height} {...props} />;
case 'dotnet':
case 'csharp': // event.platform is 'csharp'
return <DotNetIcon title={title} width={size ?? width} height={size ?? height} {...props} />;
case 'dotnet.maui':
return <DotNetMauiIcon title={title} width={size ?? width} height={size ?? height} {...props} />;
default:
return <DefaultIcon title={title} width={size ?? width} height={size ?? height} {...props} />;
return <NodeIcon title={runtimeTitle} width={size} height={size} {...props} />;
}

const browserName = `${event?.contexts?.browser?.name || ''}`;
const browserTitle = `${browserName} ${event?.contexts?.browser?.version}`;

const iconKey = Object.keys(BROWSER_ICON_MAP).find(browser => browserName.includes(browser));
if (iconKey) {
const Icon = BROWSER_ICON_MAP[iconKey];
return <Icon title={browserTitle} width={size} height={size} {...props} />;
}

return null;
}

function CorePlatformIcon({ platform, event, size = 42, title, ...props }: PlatformIconProps) {
const name = platform || event?.platform || 'unknown';
const sdk = event?.sdk?.name || '';
const newTitle = title ?? name;

const iconName = Object.keys(SDK_ICON_MAP).find(name => sdk.startsWith(name)) as
| keyof typeof SDK_ICON_MAP
| undefined;

if (iconName) {
const Icon = SDK_ICON_MAP[iconName];
return <Icon title={newTitle} width={size} height={size} {...props} />;
}

const Icon = SDK_ICON_MAP[name] ?? DefaultSDKIcon;
return <Icon title={newTitle} width={size} height={size} {...props} />;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Client, Envelope, Event, Integration } from '@sentry/types';
import { serializeEnvelope } from '@sentry/core';
import type { Client, Envelope, Event, Integration } from '@sentry/types';
import { trigger } from '../../lib/eventTarget';
import { log } from '../../lib/logger';
import sentryDataCache from './data/sentryDataCache';
Expand Down
Loading

0 comments on commit 83eaaa0

Please sign in to comment.