Shard up frontend variables

This commit is contained in:
Jack Kingsman
2026-03-03 20:07:45 -08:00
parent e0e71180b2
commit 813a47ee14
26 changed files with 144 additions and 66 deletions
+1 -1
View File
@@ -112,7 +112,7 @@ export function ChannelInfoPane({
>
{isFavorite(favorites, 'channel', channel.key) ? (
<>
<span className="text-amber-400 text-lg">&#9733;</span>
<span className="text-favorite text-lg">&#9733;</span>
<span>Remove from favorites</span>
</>
) : (
+1 -1
View File
@@ -142,7 +142,7 @@ export function ChatHeader({
}
>
{isFavorite(favorites, conversation.type as 'channel' | 'contact', conversation.id) ? (
<span className="text-amber-400">&#9733;</span>
<span className="text-favorite">&#9733;</span>
) : (
<span className="text-muted-foreground">&#9734;</span>
)}
+1 -1
View File
@@ -197,7 +197,7 @@ export function ContactInfoPane({
>
{isFavorite(favorites, 'contact', contact.public_key) ? (
<>
<span className="text-amber-400 text-lg">&#9733;</span>
<span className="text-favorite text-lg">&#9733;</span>
<span>Remove from favorites</span>
</>
) : (
+3 -3
View File
@@ -548,7 +548,7 @@ export function CrackerPanel({
Pending: <span className="text-foreground font-medium">{pendingCount}</span>
</span>
<span className="text-muted-foreground">
Cracked: <span className="text-green-400 font-medium">{crackedCount}</span>
Cracked: <span className="text-success font-medium">{crackedCount}</span>
</span>
<span className="text-muted-foreground">
Failed: <span className="text-destructive font-medium">{failedCount}</span>
@@ -613,9 +613,9 @@ export function CrackerPanel({
{crackedRooms.map((room, i) => (
<div
key={i}
className="text-sm bg-green-950/30 border border-green-900/50 rounded px-2 py-1"
className="text-sm bg-success/10 border border-success/20 rounded px-2 py-1"
>
<span className="text-green-400 font-medium">#{room.roomName}</span>
<span className="text-success font-medium">#{room.roomName}</span>
<span className="text-muted-foreground ml-2 text-xs">
"{room.message.slice(0, 50)}
{room.message.length > 50 ? '...' : ''}"
+6 -6
View File
@@ -186,9 +186,9 @@ export const MessageInput = forwardRef<MessageInputHandle, MessageInputProps>(fu
className={cn(
'tabular-nums',
limitState === 'error' || limitState === 'danger'
? 'text-red-400 font-medium'
? 'text-destructive font-medium'
: limitState === 'warning'
? 'text-yellow-500'
? 'text-warning'
: 'text-muted-foreground'
)}
>
@@ -196,7 +196,7 @@ export const MessageInput = forwardRef<MessageInputHandle, MessageInputProps>(fu
{remaining < 0 && ` (${remaining})`}
</span>
{warningMessage && (
<span className={cn(limitState === 'error' ? 'text-red-400' : 'text-yellow-500')}>
<span className={cn(limitState === 'error' ? 'text-destructive' : 'text-warning')}>
{warningMessage}
</span>
)}
@@ -209,9 +209,9 @@ export const MessageInput = forwardRef<MessageInputHandle, MessageInputProps>(fu
className={cn(
'tabular-nums',
limitState === 'error' || limitState === 'danger'
? 'text-red-400 font-medium'
? 'text-destructive font-medium'
: limitState === 'warning'
? 'text-yellow-500'
? 'text-warning'
: 'text-muted-foreground'
)}
>
@@ -220,7 +220,7 @@ export const MessageInput = forwardRef<MessageInputHandle, MessageInputProps>(fu
</span>
)}
{warningMessage && (
<span className={cn(limitState === 'error' ? 'text-red-400' : 'text-yellow-500')}>
<span className={cn(limitState === 'error' ? 'text-destructive' : 'text-warning')}>
{warningMessage}
</span>
)}
+1 -1
View File
@@ -360,7 +360,7 @@ function HopNode({ hop, hopNumber, prevLocation }: HopNodeProps) {
<div className="text-sm font-semibold">
<span className="text-foreground/80">Hop {hopNumber}:</span>{' '}
<span className="text-primary font-mono">{hop.prefix}</span>
{isAmbiguous && <span className="text-yellow-500 ml-1 font-normal">(ambiguous)</span>}
{isAmbiguous && <span className="text-warning ml-1 font-normal">(ambiguous)</span>}
</div>
{isUnknown ? (
+2 -2
View File
@@ -149,9 +149,9 @@ function decodePacketSummary(
function getRouteTypeColor(routeType: string): string {
switch (routeType) {
case 'Flood':
return 'bg-blue-500/20 text-blue-400';
return 'bg-info/20 text-info';
case 'Direct':
return 'bg-green-500/20 text-green-400';
return 'bg-success/20 text-success';
case 'Transport Flood':
return 'bg-purple-500/20 text-purple-400';
case 'Transport Direct':
@@ -95,7 +95,7 @@ export function RepeaterDashboard({
size="sm"
onClick={loadAll}
disabled={anyLoading}
className="text-xs border-green-400 text-green-400 hover:bg-green-400/10 hover:text-green-400"
className="text-xs border-success text-success hover:bg-success/10 hover:text-success"
>
{anyLoading ? 'Loading...' : 'Load All'}
</Button>
@@ -115,7 +115,7 @@ export function RepeaterDashboard({
aria-label={isFav ? 'Remove from favorites' : 'Add to favorites'}
>
{isFav ? (
<span className="text-amber-400">&#9733;</span>
<span className="text-favorite">&#9733;</span>
) : (
<span className="text-muted-foreground">&#9734;</span>
)}
+2 -2
View File
@@ -411,8 +411,8 @@ export function Sidebar({
className={cn(
'text-[10px] font-semibold px-1.5 py-0.5 rounded-full min-w-[18px] text-center',
row.isMention
? 'bg-destructive text-destructive-foreground'
: 'bg-primary/90 text-primary-foreground'
? 'bg-badge-mention text-badge-mention-foreground'
: 'bg-badge-unread/90 text-badge-unread-foreground'
)}
aria-label={`${row.unreadCount} unread message${row.unreadCount !== 1 ? 's' : ''}`}
>
+3 -3
View File
@@ -76,8 +76,8 @@ export function StatusBar({
className={cn(
'w-2 h-2 rounded-full transition-colors',
connected
? 'bg-primary shadow-[0_0_6px_hsl(var(--primary)/0.5)]'
: 'bg-muted-foreground'
? 'bg-status-connected shadow-[0_0_6px_hsl(var(--status-connected)/0.5)]'
: 'bg-status-disconnected'
)}
aria-hidden="true"
/>
@@ -109,7 +109,7 @@ export function StatusBar({
<button
onClick={handleReconnect}
disabled={reconnecting}
className="px-3 py-1 bg-amber-500/10 border border-amber-500/20 text-amber-400 rounded-md text-xs cursor-pointer hover:bg-amber-500/15 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
className="px-3 py-1 bg-warning/10 border border-warning/20 text-warning rounded-md text-xs cursor-pointer hover:bg-warning/15 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
{reconnecting ? 'Reconnecting...' : 'Reconnect'}
</button>
@@ -15,9 +15,9 @@ export function AclPane({
}) {
const permColor: Record<number, string> = {
0: 'bg-muted text-muted-foreground',
1: 'bg-blue-500/10 text-blue-500',
2: 'bg-green-400/10 text-green-400',
3: 'bg-amber-500/10 text-amber-500',
1: 'bg-info/10 text-info',
2: 'bg-success/10 text-success',
3: 'bg-warning/10 text-warning',
};
return (
@@ -39,7 +39,7 @@ export function ConsolePane({
</div>
<div
ref={outputRef}
className="h-48 overflow-y-auto p-3 font-mono text-xs bg-black/50 text-green-400 space-y-1"
className="h-48 overflow-y-auto p-3 font-mono text-xs bg-console-bg/50 text-console space-y-1"
aria-live="polite"
aria-relevant="additions"
>
@@ -48,11 +48,11 @@ export function ConsolePane({
)}
{history.map((entry, i) =>
entry.outgoing ? (
<div key={i} className="text-green-300">
<div key={i} className="text-console-command">
&gt; {entry.command}
</div>
) : (
<div key={i} className="text-green-400/80 whitespace-pre-wrap">
<div key={i} className="text-console/80 whitespace-pre-wrap">
{entry.response}
</div>
)
@@ -101,7 +101,7 @@ export function NeighborsPane({
const dist = n.distance;
const snrStr = n.snr >= 0 ? `+${n.snr.toFixed(1)}` : n.snr.toFixed(1);
const snrColor =
n.snr >= 6 ? 'text-green-400' : n.snr >= 0 ? 'text-yellow-500' : 'text-red-400';
n.snr >= 6 ? 'text-success' : n.snr >= 0 ? 'text-warning' : 'text-destructive';
return (
<tr key={i} className="border-t border-border/50">
<td className="py-1">{n.name || n.pubkey_prefix}</td>
@@ -66,7 +66,7 @@ export function RadioSettingsPane({
<span
className={cn(
'ml-2 text-xs',
clockDrift.isLarge ? 'text-red-400' : 'text-muted-foreground'
clockDrift.isLarge ? 'text-destructive' : 'text-muted-foreground'
)}
>
(drift: {clockDrift.text})
@@ -88,7 +88,7 @@ export function RadioSettingsPane({
'p-1 rounded transition-colors disabled:opacity-50',
disabled || advertState.loading
? 'text-muted-foreground'
: 'text-green-500 hover:bg-accent hover:text-green-400'
: 'text-success hover:bg-accent hover:text-success'
)}
title="Refresh Advert Intervals"
>
@@ -112,7 +112,7 @@ export function RepeaterPane({
'p-1 rounded transition-colors disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
disabled || state.loading
? 'text-muted-foreground'
: 'text-green-500 hover:bg-accent hover:text-green-400'
: 'text-success hover:bg-accent hover:text-success'
)}
title="Refresh"
aria-label={`Refresh ${title}`}
@@ -151,23 +151,23 @@ export function SettingsBotSection({
return (
<div className={className}>
<div className="p-3 bg-red-500/10 border border-red-500/30 rounded-md">
<p className="text-sm text-red-400">
<div className="p-3 bg-destructive/10 border border-destructive/30 rounded-md">
<p className="text-sm text-destructive">
<strong>Experimental:</strong> This is an alpha feature and introduces automated message
sending to your radio; unexpected behavior may occur. Use with caution, and please report
any bugs!
</p>
</div>
<div className="p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-md">
<p className="text-sm text-yellow-500">
<div className="p-3 bg-warning/10 border border-warning/30 rounded-md">
<p className="text-sm text-warning">
<strong>Security Warning:</strong> This feature executes arbitrary Python code on the
server. Only run trusted code, and be cautious of arbitrary usage of message parameters.
</p>
</div>
<div className="p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-md">
<p className="text-sm text-yellow-500">
<div className="p-3 bg-warning/10 border border-warning/30 rounded-md">
<p className="text-sm text-warning">
<strong>Don&apos;t wreck the mesh!</strong> Bots process ALL messages, including their
own. Be careful of creating infinite loops!
</p>
@@ -286,7 +286,7 @@ export function SettingsBotSection({
</div>
<Suspense
fallback={
<div className="h-64 md:h-96 rounded-md border border-input bg-[#282c34] flex items-center justify-center text-muted-foreground">
<div className="h-64 md:h-96 rounded-md border border-input bg-code-editor-bg flex items-center justify-center text-muted-foreground">
Loading editor...
</div>
}
@@ -82,14 +82,14 @@ export function SettingsConnectivitySection({
<Label>Connection</Label>
{health?.connection_info ? (
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-green-500" />
<div className="w-2 h-2 rounded-full bg-status-connected" />
<code className="px-2 py-1 bg-muted rounded text-foreground text-sm">
{health.connection_info}
</code>
</div>
) : (
<div className="flex items-center gap-2 text-muted-foreground">
<div className="w-2 h-2 rounded-full bg-gray-500" />
<div className="w-2 h-2 rounded-full bg-status-disconnected" />
<span>Not connected</span>
</div>
)}
@@ -123,7 +123,7 @@ export function SettingsConnectivitySection({
variant="outline"
onClick={handleReboot}
disabled={rebooting || busy}
className="w-full border-red-500/50 text-red-400 hover:bg-red-500/10"
className="w-full border-destructive/50 text-destructive hover:bg-destructive/10"
>
{rebooting ? 'Rebooting...' : 'Reboot Radio'}
</Button>
@@ -171,7 +171,7 @@ export function SettingsDatabaseSection({
variant="outline"
onClick={handleCleanup}
disabled={cleaning}
className="border-red-500/50 text-red-400 hover:bg-red-500/10"
className="border-destructive/50 text-destructive hover:bg-destructive/10"
>
{cleaning ? 'Deleting...' : 'Permanently Delete'}
</Button>
@@ -194,7 +194,7 @@ export function SettingsDatabaseSection({
variant="outline"
onClick={handlePurgeDecryptedRawPackets}
disabled={purgingDecryptedRaw}
className="w-full border-yellow-500/50 text-yellow-400 hover:bg-yellow-500/10"
className="w-full border-warning/50 text-warning hover:bg-warning/10"
>
{purgingDecryptedRaw ? 'Purging Archival Raw Packets...' : 'Purge Archival Raw Packets'}
</Button>
@@ -175,7 +175,7 @@ export function SettingsIdentitySection({
<Button
onClick={handleAdvertise}
disabled={advertising || !health?.radio_connected}
className="w-full bg-yellow-600 hover:bg-yellow-700 text-white"
className="w-full bg-warning hover:bg-warning/90 text-warning-foreground"
>
{advertising ? 'Sending...' : 'Send Advertisement'}
</Button>
@@ -90,21 +90,21 @@ export function SettingsMqttSection({
return (
<div className={className}>
<div className="rounded-md border border-yellow-600/50 bg-yellow-950/30 px-4 py-3 text-sm text-yellow-200">
<div className="rounded-md border border-warning/50 bg-warning/10 px-4 py-3 text-sm text-warning">
MQTT support is an experimental feature in open beta. All publishing uses QoS 0
(at-most-once delivery). Please report any bugs on the{' '}
<a
href="https://github.com/jkingsman/Remote-Terminal-for-MeshCore/issues"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-yellow-100"
className="underline hover:text-warning-foreground"
>
GitHub issues page
</a>
.
</div>
<div className="rounded-md border border-blue-600/50 bg-blue-950/30 px-4 py-3 text-sm text-blue-200">
<div className="rounded-md border border-info/50 bg-info/10 px-4 py-3 text-sm text-info">
Outgoing messages (DMs and group messages) will be reported to private MQTT brokers in
decrypted/plaintext form. The raw outgoing packets will NOT be reported to any MQTT broker,
private or community. This means that{' '}
@@ -138,7 +138,7 @@ export function SettingsMqttSection({
className={cn(
'w-2 h-2 rounded-full transition-colors',
health?.mqtt_status === 'connected'
? 'bg-primary shadow-[0_0_6px_hsl(var(--primary)/0.5)]'
? 'bg-status-connected shadow-[0_0_6px_hsl(var(--status-connected)/0.5)]'
: 'bg-muted-foreground'
)}
/>
@@ -323,7 +323,7 @@ export function SettingsMqttSection({
className={cn(
'w-2 h-2 rounded-full transition-colors',
health?.community_mqtt_status === 'connected'
? 'bg-primary shadow-[0_0_6px_hsl(var(--primary)/0.5)]'
? 'bg-status-connected shadow-[0_0_6px_hsl(var(--status-connected)/0.5)]'
: 'bg-muted-foreground'
)}
/>
@@ -88,12 +88,12 @@ export function SettingsStatisticsSection({ className }: { className?: string })
<span className="font-medium">{stats.total_packets}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-green-400">Decrypted</span>
<span className="font-medium text-green-400">{stats.decrypted_packets}</span>
<span className="text-sm text-success">Decrypted</span>
<span className="font-medium text-success">{stats.decrypted_packets}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-yellow-500">Undecrypted</span>
<span className="font-medium text-yellow-500">{stats.undecrypted_packets}</span>
<span className="text-sm text-warning">Undecrypted</span>
<span className="font-medium text-warning">{stats.undecrypted_packets}</span>
</div>
</div>
</div>
+1 -1
View File
@@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
'fixed inset-0 z-50 bg-overlay/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className
)}
{...props}
+1 -1
View File
@@ -21,7 +21,7 @@ const SheetOverlay = React.forwardRef<
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
'fixed inset-0 z-50 bg-overlay/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className
)}
{...props}
+1 -1
View File
@@ -16,7 +16,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
// Muted error style - dark red-tinted background with readable text
error:
'group-[.toaster]:bg-[#2a1a1a] group-[.toaster]:text-[#e8a0a0] group-[.toaster]:border-[#4a2a2a] [&_[data-description]]:text-[#e8b0b0]',
'group-[.toaster]:bg-toast-error group-[.toaster]:text-toast-error-foreground group-[.toaster]:border-toast-error-border [&_[data-description]]:text-toast-error-foreground',
},
}}
{...props}
+50 -9
View File
@@ -28,6 +28,52 @@
/* Semantic colors for specific contexts */
--msg-outgoing: 152 30% 14%;
--msg-incoming: 224 12% 15%;
/* Status indicator pips */
--status-connected: 142 71% 45%;
--status-disconnected: 220 9% 46%;
/* Semantic feedback colors */
--warning: 38 92% 50%;
--warning-foreground: 0 0% 100%;
--success: 142 71% 45%;
--success-foreground: 0 0% 100%;
--info: 217 91% 60%;
--info-foreground: 0 0% 100%;
/* Favorites */
--favorite: 43 96% 56%;
/* Console / terminal */
--console: 142 69% 58%;
--console-command: 142 76% 73%;
--console-bg: 0 0% 0%;
/* Unread badges */
--badge-unread: var(--primary);
--badge-unread-foreground: var(--primary-foreground);
--badge-mention: var(--destructive);
--badge-mention-foreground: var(--destructive-foreground);
/* Error toast */
--toast-error: 0 30% 14%;
--toast-error-foreground: 0 56% 77%;
--toast-error-border: 0 27% 23%;
/* Code editor */
--code-editor-bg: 220 13% 18%;
/* Overlay / backdrop */
--overlay: 0 0% 0%;
/* Typography */
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-mono:
ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
/* Scrollbar */
--scrollbar: 224 11% 22%;
--scrollbar-hover: 224 11% 28%;
}
}
@@ -37,12 +83,7 @@
}
body {
@apply bg-background text-foreground;
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
sans-serif;
font-family: var(--font-sans);
font-size: 14px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@@ -58,17 +99,17 @@
background: transparent;
}
::-webkit-scrollbar-thumb {
background: hsl(224 11% 22%);
background: hsl(var(--scrollbar));
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: hsl(224 11% 28%);
background: hsl(var(--scrollbar-hover));
}
/* Firefox scrollbar */
* {
scrollbar-width: thin;
scrollbar-color: hsl(224 11% 22%) transparent;
scrollbar-color: hsl(var(--scrollbar)) transparent;
}
/* Message highlight animation for jump-to-message */
+37
View File
@@ -43,6 +43,43 @@ export default {
},
"msg-outgoing": "hsl(var(--msg-outgoing))",
"msg-incoming": "hsl(var(--msg-incoming))",
"status-connected": "hsl(var(--status-connected))",
"status-disconnected": "hsl(var(--status-disconnected))",
warning: {
DEFAULT: "hsl(var(--warning))",
foreground: "hsl(var(--warning-foreground))",
},
success: {
DEFAULT: "hsl(var(--success))",
foreground: "hsl(var(--success-foreground))",
},
info: {
DEFAULT: "hsl(var(--info))",
foreground: "hsl(var(--info-foreground))",
},
favorite: "hsl(var(--favorite))",
console: "hsl(var(--console))",
"console-command": "hsl(var(--console-command))",
"console-bg": "hsl(var(--console-bg))",
"code-editor-bg": "hsl(var(--code-editor-bg))",
overlay: "hsl(var(--overlay))",
"badge-unread": {
DEFAULT: "hsl(var(--badge-unread))",
foreground: "hsl(var(--badge-unread-foreground))",
},
"badge-mention": {
DEFAULT: "hsl(var(--badge-mention))",
foreground: "hsl(var(--badge-mention-foreground))",
},
"toast-error": {
DEFAULT: "hsl(var(--toast-error))",
foreground: "hsl(var(--toast-error-foreground))",
border: "hsl(var(--toast-error-border))",
},
},
fontFamily: {
sans: ["var(--font-sans)"],
mono: ["var(--font-mono)"],
},
borderRadius: {
lg: "var(--radius)",