mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-04-30 10:35:17 +02:00
Add cracker dropin interface
This commit is contained in:
542
frontend/dist/assets/index-B-xot-vl.js
vendored
542
frontend/dist/assets/index-B-xot-vl.js
vendored
File diff suppressed because one or more lines are too long
542
frontend/dist/assets/index-BKnk_LMx.js
vendored
Normal file
542
frontend/dist/assets/index-BKnk_LMx.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index-BdHgsPJQ.css
vendored
Normal file
1
frontend/dist/assets/index-BdHgsPJQ.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/index-DlaqriQ9.css
vendored
1
frontend/dist/assets/index-DlaqriQ9.css
vendored
File diff suppressed because one or more lines are too long
4
frontend/dist/index.html
vendored
4
frontend/dist/index.html
vendored
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>RemoteTerm for MeshCore</title>
|
||||
<script type="module" crossorigin src="/assets/index-B-xot-vl.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DlaqriQ9.css">
|
||||
<script type="module" crossorigin src="/assets/index-BKnk_LMx.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BdHgsPJQ.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
type ConversationTimes,
|
||||
} from './utils/conversationState';
|
||||
import { pubkeysMatch, getContactDisplayName } from './utils/pubkey';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type {
|
||||
AppSettings,
|
||||
AppSettingsUpdate,
|
||||
@@ -59,6 +60,8 @@ export function App() {
|
||||
const [showConfig, setShowConfig] = useState(false);
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const [undecryptedCount, setUndecryptedCount] = useState(0);
|
||||
const [showCracker, setShowCracker] = useState(false);
|
||||
const [crackerRunning, setCrackerRunning] = useState(false);
|
||||
// Track last message times (persisted in localStorage, used for sorting)
|
||||
const [lastMessageTimes, setLastMessageTimes] = useState<ConversationTimes>(getLastMessageTimes);
|
||||
// Track unread counts (calculated on load and incremented during session)
|
||||
@@ -634,6 +637,9 @@ export function App() {
|
||||
}}
|
||||
lastMessageTimes={lastMessageTimes}
|
||||
unreadCounts={unreadCounts}
|
||||
showCracker={showCracker}
|
||||
crackerRunning={crackerRunning}
|
||||
onToggleCracker={() => setShowCracker((prev) => !prev)}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -670,28 +676,8 @@ export function App() {
|
||||
activeConversation.type === 'raw' ? (
|
||||
<>
|
||||
<div className="flex justify-between items-center px-4 py-3 border-b border-border font-medium">Raw Packet Feed</div>
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
<div className="flex-1 overflow-hidden min-h-0">
|
||||
<RawPacketList packets={rawPackets} />
|
||||
</div>
|
||||
<div className="h-[280px] flex-shrink-0 border-t border-border overflow-hidden">
|
||||
<CrackerPanel
|
||||
packets={rawPackets}
|
||||
channels={channels}
|
||||
onChannelCreate={async (name, key) => {
|
||||
// Create channel without navigating to it
|
||||
const created = await api.createChannel(name, key);
|
||||
const data = await api.getChannels();
|
||||
setChannels(data);
|
||||
// Try to decrypt historical packets with this key
|
||||
await api.decryptHistoricalPackets({
|
||||
key_type: 'channel',
|
||||
channel_key: created.key,
|
||||
});
|
||||
fetchUndecryptedCount();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<RawPacketList packets={rawPackets} />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
@@ -750,6 +736,30 @@ export function App() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Global Cracker Panel - always rendered to maintain state */}
|
||||
<div
|
||||
className={cn(
|
||||
"border-t border-border bg-background transition-all duration-200 overflow-hidden",
|
||||
showCracker ? "h-[275px]" : "h-0"
|
||||
)}
|
||||
>
|
||||
<CrackerPanel
|
||||
packets={rawPackets}
|
||||
channels={channels}
|
||||
onChannelCreate={async (name, key) => {
|
||||
const created = await api.createChannel(name, key);
|
||||
const data = await api.getChannels();
|
||||
setChannels(data);
|
||||
await api.decryptHistoricalPackets({
|
||||
key_type: 'channel',
|
||||
channel_key: created.key,
|
||||
});
|
||||
fetchUndecryptedCount();
|
||||
}}
|
||||
onRunningChange={setCrackerRunning}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<NewMessageModal
|
||||
open={showNewMessage}
|
||||
contacts={contacts}
|
||||
|
||||
@@ -23,9 +23,10 @@ interface CrackerPanelProps {
|
||||
packets: RawPacket[];
|
||||
channels: Channel[];
|
||||
onChannelCreate: (name: string, key: string) => Promise<void>;
|
||||
onRunningChange?: (running: boolean) => void;
|
||||
}
|
||||
|
||||
export function CrackerPanel({ packets, channels, onChannelCreate }: CrackerPanelProps) {
|
||||
export function CrackerPanel({ packets, channels, onChannelCreate, onRunningChange }: CrackerPanelProps) {
|
||||
const [isRunning, setIsRunning] = useState(false);
|
||||
const [maxLength, setMaxLength] = useState(6);
|
||||
const [retryFailedAtNextLength, setRetryFailedAtNextLength] = useState(false);
|
||||
@@ -113,6 +114,11 @@ export function CrackerPanel({ packets, channels, onChannelCreate }: CrackerPane
|
||||
maxLengthRef.current = maxLength;
|
||||
}, [maxLength]);
|
||||
|
||||
// Notify parent of running state changes
|
||||
useEffect(() => {
|
||||
onRunningChange?.(isRunning);
|
||||
}, [isRunning, onRunningChange]);
|
||||
|
||||
// Stats (cracking count is implicit - if progress is shown, we're cracking one)
|
||||
const pendingCount = Array.from(queue.values()).filter(q => q.status === 'pending').length;
|
||||
const crackedCount = Array.from(queue.values()).filter(q => q.status === 'cracked').length;
|
||||
|
||||
@@ -18,6 +18,9 @@ interface SidebarProps {
|
||||
onNewMessage: () => void;
|
||||
lastMessageTimes: ConversationTimes;
|
||||
unreadCounts: Record<string, number>;
|
||||
showCracker: boolean;
|
||||
crackerRunning: boolean;
|
||||
onToggleCracker: () => void;
|
||||
}
|
||||
|
||||
// Load sort preference from localStorage
|
||||
@@ -47,6 +50,9 @@ export function Sidebar({
|
||||
onNewMessage,
|
||||
lastMessageTimes,
|
||||
unreadCounts,
|
||||
showCracker,
|
||||
crackerRunning,
|
||||
onToggleCracker,
|
||||
}: SidebarProps) {
|
||||
const [sortOrder, setSortOrder] = useState<SortOrder>(loadSortOrder);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
@@ -219,6 +225,28 @@ export function Sidebar({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Cracker Toggle */}
|
||||
{!query && (
|
||||
<div
|
||||
className={cn(
|
||||
"px-3 py-2.5 cursor-pointer flex items-center gap-2 border-l-2 border-transparent hover:bg-accent",
|
||||
showCracker && "bg-accent border-l-primary"
|
||||
)}
|
||||
onClick={onToggleCracker}
|
||||
>
|
||||
<span className="text-muted-foreground text-xs">🔓</span>
|
||||
<span className="flex-1 truncate">
|
||||
{showCracker ? 'Hide' : 'Show'} Cracker
|
||||
<span className={cn(
|
||||
"ml-1 text-xs",
|
||||
crackerRunning ? "text-green-500" : "text-muted-foreground"
|
||||
)}>
|
||||
({crackerRunning ? 'running' : 'stopped'})
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Channels */}
|
||||
{filteredChannels.length > 0 && (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user