chore: replace Unicode/emoji icons with Lucide React
Adds lucide-react and replaces all HTML entity and emoji icons across the UI with proper SVG icon components. Tree-shaken — only imported icons are bundled. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
ui/package-lock.json
generated
10
ui/package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cameleer/design-system": "^0.1.17",
|
"@cameleer/design-system": "^0.1.17",
|
||||||
"@tanstack/react-query": "^5.90.21",
|
"@tanstack/react-query": "^5.90.21",
|
||||||
|
"lucide-react": "^1.7.0",
|
||||||
"openapi-fetch": "^0.17.0",
|
"openapi-fetch": "^0.17.0",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
@@ -2583,6 +2584,15 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lucide-react": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.1.5",
|
"version": "3.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cameleer/design-system": "^0.1.17",
|
"@cameleer/design-system": "^0.1.17",
|
||||||
"@tanstack/react-query": "^5.90.21",
|
"@tanstack/react-query": "^5.90.21",
|
||||||
|
"lucide-react": "^1.7.0",
|
||||||
"openapi-fetch": "^0.17.0",
|
"openapi-fetch": "^0.17.0",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
|
|||||||
@@ -133,31 +133,19 @@ export function DiagramNode({
|
|||||||
{isCompleted && (
|
{isCompleted && (
|
||||||
<>
|
<>
|
||||||
<circle cx={w - 10} cy={TOP_BAR_HEIGHT + 8} r={6} fill="#3D7C47" />
|
<circle cx={w - 10} cy={TOP_BAR_HEIGHT + 8} r={6} fill="#3D7C47" />
|
||||||
<text
|
<path
|
||||||
x={w - 10}
|
d={`M${w - 13} ${TOP_BAR_HEIGHT + 8} l2 2 4-4`}
|
||||||
y={TOP_BAR_HEIGHT + 11}
|
fill="none" stroke="white" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round"
|
||||||
textAnchor="middle"
|
/>
|
||||||
fill="white"
|
|
||||||
fontSize={9}
|
|
||||||
fontWeight={700}
|
|
||||||
>
|
|
||||||
✓
|
|
||||||
</text>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{isFailed && (
|
{isFailed && (
|
||||||
<>
|
<>
|
||||||
<circle cx={w - 10} cy={TOP_BAR_HEIGHT + 8} r={6} fill="#C0392B" />
|
<circle cx={w - 10} cy={TOP_BAR_HEIGHT + 8} r={6} fill="#C0392B" />
|
||||||
<text
|
<path
|
||||||
x={w - 10}
|
d={`M${w - 10} ${TOP_BAR_HEIGHT + 5} v4 M${w - 10} ${TOP_BAR_HEIGHT + 10.5} v0.5`}
|
||||||
y={TOP_BAR_HEIGHT + 11}
|
fill="none" stroke="white" strokeWidth={1.5} strokeLinecap="round"
|
||||||
textAnchor="middle"
|
/>
|
||||||
fill="white"
|
|
||||||
fontSize={9}
|
|
||||||
fontWeight={700}
|
|
||||||
>
|
|
||||||
!
|
|
||||||
</text>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -177,15 +165,13 @@ export function DiagramNode({
|
|||||||
|
|
||||||
{/* Sub-route failure: drill-down arrow at bottom-left */}
|
{/* Sub-route failure: drill-down arrow at bottom-left */}
|
||||||
{isFailed && executionState?.subRouteFailed && (
|
{isFailed && executionState?.subRouteFailed && (
|
||||||
<text
|
<g transform={`translate(4, ${h - 14})`}>
|
||||||
x={6}
|
<path
|
||||||
y={h - 4}
|
d="M2 2 v5 a3 3 0 003 3 h5"
|
||||||
fill="#C0392B"
|
fill="none" stroke="#C0392B" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round"
|
||||||
fontSize={11}
|
/>
|
||||||
fontWeight={700}
|
<path d="M8 8 l2 2 -2 2" fill="none" stroke="#C0392B" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" />
|
||||||
>
|
</g>
|
||||||
↳
|
|
||||||
</text>
|
|
||||||
)}
|
)}
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { useCallback, useRef, useState } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
|
import { Search, Footprints, Droplets, Ellipsis } from 'lucide-react';
|
||||||
|
import type { LucideIcon } from 'lucide-react';
|
||||||
import type { NodeAction } from './types';
|
import type { NodeAction } from './types';
|
||||||
import styles from './ProcessDiagram.module.css';
|
import styles from './ProcessDiagram.module.css';
|
||||||
|
|
||||||
@@ -14,11 +16,11 @@ interface NodeToolbarProps {
|
|||||||
onMouseLeave: () => void;
|
onMouseLeave: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ACTIONS: { icon: string; action: NodeAction; title: string }[] = [
|
const ACTIONS: { Icon: LucideIcon; action: NodeAction; title: string }[] = [
|
||||||
{ icon: '\uD83D\uDD0D', action: 'inspect', title: 'Inspect' },
|
{ Icon: Search, action: 'inspect', title: 'Inspect' },
|
||||||
{ icon: '\uD83D\uDC63', action: 'toggle-trace', title: 'Toggle tracing' },
|
{ Icon: Footprints, action: 'toggle-trace', title: 'Toggle tracing' },
|
||||||
{ icon: '\uD83D\uDEB0', action: 'configure-tap', title: 'Configure tap' },
|
{ Icon: Droplets, action: 'configure-tap', title: 'Configure tap' },
|
||||||
{ icon: '\u22EF', action: 'copy-id', title: 'Copy ID' },
|
{ Icon: Ellipsis, action: 'copy-id', title: 'Copy ID' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export function NodeToolbar({
|
export function NodeToolbar({
|
||||||
@@ -41,7 +43,7 @@ export function NodeToolbar({
|
|||||||
onAction(nodeId, a.action);
|
onAction(nodeId, a.action);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{a.icon}
|
<a.Icon size={14} />
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useState, useMemo } from 'react';
|
import { useEffect, useState, useMemo } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router';
|
import { useParams, useNavigate } from 'react-router';
|
||||||
|
import { ArrowLeft, Pencil, X } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
Button, SectionHeader, MonoText, Badge, DataTable, Spinner, Toggle, useToast,
|
Button, SectionHeader, MonoText, Badge, DataTable, Spinner, Toggle, useToast,
|
||||||
} from '@cameleer/design-system';
|
} from '@cameleer/design-system';
|
||||||
@@ -248,7 +249,7 @@ export default function AppConfigDetailPage() {
|
|||||||
if (row.captureMode === null) return null;
|
if (row.captureMode === null) return null;
|
||||||
return (
|
return (
|
||||||
<button className={styles.removeBtn} title="Remove" onClick={() => updateTracedProcessor(row.processorId, 'REMOVE')}>
|
<button className={styles.removeBtn} title="Remove" onClick={() => updateTracedProcessor(row.processorId, 'REMOVE')}>
|
||||||
×
|
<X size={14} />
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -303,7 +304,7 @@ export default function AppConfigDetailPage() {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.page}>
|
<div className={styles.page}>
|
||||||
<div className={styles.toolbar}>
|
<div className={styles.toolbar}>
|
||||||
<button className={styles.backBtn} onClick={() => navigate('/admin/appconfig')}>← Back</button>
|
<button className={styles.backBtn} onClick={() => navigate('/admin/appconfig')}><ArrowLeft size={14} /> Back</button>
|
||||||
{editing ? (
|
{editing ? (
|
||||||
<div className={styles.toolbarActions}>
|
<div className={styles.toolbarActions}>
|
||||||
<Button onClick={handleSave} disabled={updateConfig.isPending}>
|
<Button onClick={handleSave} disabled={updateConfig.isPending}>
|
||||||
@@ -312,7 +313,7 @@ export default function AppConfigDetailPage() {
|
|||||||
<button className={styles.cancelBtn} onClick={cancelEditing}>Cancel</button>
|
<button className={styles.cancelBtn} onClick={cancelEditing}>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<button className={styles.editBtn} onClick={startEditing}>✎ Edit</button>
|
<button className={styles.editBtn} onClick={startEditing}><Pencil size={14} /> Edit</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useMemo, useEffect } from 'react';
|
import { useState, useMemo, useEffect } from 'react';
|
||||||
import { useNavigate, useSearchParams } from 'react-router';
|
import { useNavigate, useSearchParams } from 'react-router';
|
||||||
|
import { Pencil, X } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
DataTable, Badge, MonoText, DetailPanel, SectionHeader, Button, Toggle, Spinner, useToast,
|
DataTable, Badge, MonoText, DetailPanel, SectionHeader, Button, Toggle, Spinner, useToast,
|
||||||
} from '@cameleer/design-system';
|
} from '@cameleer/design-system';
|
||||||
@@ -197,7 +198,7 @@ function AppConfigDetail({ appId, onClose }: { appId: string; onClose: () => voi
|
|||||||
...(editing ? [{
|
...(editing ? [{
|
||||||
key: '_remove' as const, header: '', width: '36px',
|
key: '_remove' as const, header: '', width: '36px',
|
||||||
render: (_v: unknown, row: TracedTapRow) => row.captureMode === null ? null : (
|
render: (_v: unknown, row: TracedTapRow) => row.captureMode === null ? null : (
|
||||||
<button className={styles.removeBtn} title="Remove" onClick={() => updateTracedProcessor(row.processorId, 'REMOVE')}>×</button>
|
<button className={styles.removeBtn} title="Remove" onClick={() => updateTracedProcessor(row.processorId, 'REMOVE')}><X size={14} /></button>
|
||||||
),
|
),
|
||||||
}] : []),
|
}] : []),
|
||||||
], [editing, processorToRoute]);
|
], [editing, processorToRoute]);
|
||||||
@@ -233,7 +234,7 @@ function AppConfigDetail({ appId, onClose }: { appId: string; onClose: () => voi
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<button className={styles.editBtn} onClick={startEditing} title="Edit configuration">✎</button>
|
<button className={styles.editBtn} onClick={startEditing} title="Edit configuration"><Pencil size={14} /></button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useMemo, useCallback } from 'react';
|
import { useState, useMemo, useCallback } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router';
|
import { useParams, useNavigate } from 'react-router';
|
||||||
|
import { ExternalLink, RefreshCw, Pencil } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
StatCard, StatusDot, Badge, MonoText, ProgressBar,
|
StatCard, StatusDot, Badge, MonoText, ProgressBar,
|
||||||
GroupCard, DataTable, LineChart, EventFeed, DetailPanel,
|
GroupCard, DataTable, LineChart, EventFeed, DetailPanel,
|
||||||
@@ -357,7 +358,7 @@ export default function AgentHealth() {
|
|||||||
navigate(`/agents/${row.application}/${row.id}`);
|
navigate(`/agents/${row.application}/${row.id}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
↗
|
<ExternalLink size={14} />
|
||||||
</button>
|
</button>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -621,7 +622,7 @@ export default function AgentHealth() {
|
|||||||
<span className={styles.configLabel}>Metrics</span>
|
<span className={styles.configLabel}>Metrics</span>
|
||||||
<Badge label={appConfig.metricsEnabled ? 'On' : 'Off'} color={appConfig.metricsEnabled ? 'success' : 'error'} variant="filled" />
|
<Badge label={appConfig.metricsEnabled ? 'On' : 'Off'} color={appConfig.metricsEnabled ? 'success' : 'error'} variant="filled" />
|
||||||
</div>
|
</div>
|
||||||
<button className={styles.configEditBtn} title="Edit config" onClick={startConfigEdit}>✎</button>
|
<button className={styles.configEditBtn} title="Edit config" onClick={startConfigEdit}><Pencil size={14} /></button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -695,7 +696,7 @@ export default function AgentHealth() {
|
|||||||
{logSortAsc ? '\u2191' : '\u2193'}
|
{logSortAsc ? '\u2191' : '\u2193'}
|
||||||
</button>
|
</button>
|
||||||
<button className={styles.refreshBtn} onClick={() => setLogRefreshTo(new Date().toISOString())} title="Refresh">
|
<button className={styles.refreshBtn} onClick={() => setLogRefreshTo(new Date().toISOString())} title="Refresh">
|
||||||
↻
|
<RefreshCw size={14} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -745,7 +746,7 @@ export default function AgentHealth() {
|
|||||||
{eventSortAsc ? '\u2191' : '\u2193'}
|
{eventSortAsc ? '\u2191' : '\u2193'}
|
||||||
</button>
|
</button>
|
||||||
<button className={styles.refreshBtn} onClick={() => setEventRefreshTo(new Date().toISOString())} title="Refresh">
|
<button className={styles.refreshBtn} onClick={() => setEventRefreshTo(new Date().toISOString())} title="Refresh">
|
||||||
↻
|
<RefreshCw size={14} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useParams, Link } from 'react-router';
|
import { useParams, Link } from 'react-router';
|
||||||
|
import { RefreshCw, ChevronRight } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
StatCard, StatusDot, Badge, LineChart, AreaChart, BarChart,
|
StatCard, StatusDot, Badge, LineChart, AreaChart, BarChart,
|
||||||
EventFeed, Spinner, EmptyState, SectionHeader, MonoText,
|
EventFeed, Spinner, EmptyState, SectionHeader, MonoText,
|
||||||
@@ -230,11 +231,11 @@ export default function AgentInstance() {
|
|||||||
<Link to="/agents" className={styles.scopeLink}>
|
<Link to="/agents" className={styles.scopeLink}>
|
||||||
All Agents
|
All Agents
|
||||||
</Link>
|
</Link>
|
||||||
<span className={styles.scopeSep}>▸</span>
|
<span className={styles.scopeSep}><ChevronRight size={12} /></span>
|
||||||
<Link to={`/agents/${appId}`} className={styles.scopeLink}>
|
<Link to={`/agents/${appId}`} className={styles.scopeLink}>
|
||||||
{appId}
|
{appId}
|
||||||
</Link>
|
</Link>
|
||||||
<span className={styles.scopeSep}>▸</span>
|
<span className={styles.scopeSep}><ChevronRight size={12} /></span>
|
||||||
<span className={styles.scopeCurrent}>{agent.name}</span>
|
<span className={styles.scopeCurrent}>{agent.name}</span>
|
||||||
<StatusDot variant={statusVariant} />
|
<StatusDot variant={statusVariant} />
|
||||||
<Badge label={agent.status} color={statusColor} />
|
<Badge label={agent.status} color={statusColor} />
|
||||||
@@ -407,7 +408,7 @@ export default function AgentInstance() {
|
|||||||
{logSortAsc ? '\u2191' : '\u2193'}
|
{logSortAsc ? '\u2191' : '\u2193'}
|
||||||
</button>
|
</button>
|
||||||
<button className={styles.refreshBtn} onClick={() => setLogRefreshTo(new Date().toISOString())} title="Refresh">
|
<button className={styles.refreshBtn} onClick={() => setLogRefreshTo(new Date().toISOString())} title="Refresh">
|
||||||
↻
|
<RefreshCw size={14} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -457,7 +458,7 @@ export default function AgentInstance() {
|
|||||||
{eventSortAsc ? '\u2191' : '\u2193'}
|
{eventSortAsc ? '\u2191' : '\u2193'}
|
||||||
</button>
|
</button>
|
||||||
<button className={styles.refreshBtn} onClick={() => setEventRefreshTo(new Date().toISOString())} title="Refresh">
|
<button className={styles.refreshBtn} onClick={() => setEventRefreshTo(new Date().toISOString())} title="Refresh">
|
||||||
↻
|
<RefreshCw size={14} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useMemo, useCallback } from 'react'
|
import { useState, useMemo, useCallback } from 'react'
|
||||||
import { useParams, useNavigate } from 'react-router'
|
import { useParams, useNavigate } from 'react-router'
|
||||||
|
import { ExternalLink, AlertTriangle } from 'lucide-react'
|
||||||
import {
|
import {
|
||||||
DataTable,
|
DataTable,
|
||||||
DetailPanel,
|
DetailPanel,
|
||||||
@@ -344,7 +345,7 @@ export default function Dashboard() {
|
|||||||
navigate(`/exchanges/${row.executionId}`)
|
navigate(`/exchanges/${row.executionId}`)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
↗
|
<ExternalLink size={14} />
|
||||||
</button>
|
</button>
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -418,7 +419,7 @@ export default function Dashboard() {
|
|||||||
expandedContent={(row: Row) =>
|
expandedContent={(row: Row) =>
|
||||||
row.errorMessage ? (
|
row.errorMessage ? (
|
||||||
<div className={styles.inlineError}>
|
<div className={styles.inlineError}>
|
||||||
<span className={styles.inlineErrorIcon}>{'\u26A0'}</span>
|
<span className={styles.inlineErrorIcon}><AlertTriangle size={14} /></span>
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.inlineErrorText}>{row.errorMessage}</div>
|
<div className={styles.inlineErrorText}>{row.errorMessage}</div>
|
||||||
<div className={styles.inlineErrorHint}>Click to view full stack trace</div>
|
<div className={styles.inlineErrorHint}>Click to view full stack trace</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useMemo, useCallback } from 'react';
|
import { useState, useMemo, useCallback } from 'react';
|
||||||
import { useParams, useNavigate, useSearchParams, Link } from 'react-router';
|
import { useParams, useNavigate, useSearchParams, Link } from 'react-router';
|
||||||
|
import { Pencil, Trash2 } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
KpiStrip,
|
KpiStrip,
|
||||||
Badge,
|
Badge,
|
||||||
@@ -613,8 +614,8 @@ export default function RouteDetail() {
|
|||||||
width: '80px',
|
width: '80px',
|
||||||
render: (_, row) => (
|
render: (_, row) => (
|
||||||
<div className={styles.tapActions}>
|
<div className={styles.tapActions}>
|
||||||
<button className={styles.tapActionBtn} title="Edit" onClick={() => openTapModal(row)}>✎</button>
|
<button className={styles.tapActionBtn} title="Edit" onClick={() => openTapModal(row)}><Pencil size={14} /></button>
|
||||||
<button className={styles.tapActionBtn} title="Delete" onClick={() => setDeletingTap(row)}>🗑</button>
|
<button className={styles.tapActionBtn} title="Delete" onClick={() => setDeletingTap(row)}><Trash2 size={14} /></button>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user