21 changed files with 835 additions and 9437 deletions
File diff suppressed because it is too large
@ -0,0 +1,66 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { MenuIcon, PaperAirplaneIcon } from '@heroicons/react/outline'; |
||||
|
import type { IHTTPConnection } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
import type { languageTemplate } from '../App'; |
||||
|
|
||||
|
export interface MessageBoxProps { |
||||
|
translations: languageTemplate; |
||||
|
setSettingsModalOpen: React.Dispatch<React.SetStateAction<boolean>>; |
||||
|
connection: IHTTPConnection; |
||||
|
isReady: boolean; |
||||
|
} |
||||
|
|
||||
|
const MessageBox = (props: MessageBoxProps) => { |
||||
|
const [currentMessage, setCurrentMessage] = React.useState(''); |
||||
|
const sendMessage = () => { |
||||
|
if (props.isReady) { |
||||
|
props.connection.sendText(currentMessage, undefined, true); |
||||
|
setCurrentMessage(''); |
||||
|
} |
||||
|
}; |
||||
|
return ( |
||||
|
<div className="flex text-lg font-medium border rounded-md space-x-2 md:space-x-0 w-full"> |
||||
|
<div |
||||
|
className="flex p-3 text-xl hover:text-gray-500 text-gray-400 rounded-md shadow-md focus:outline-none cursor-pointer md:hidden" |
||||
|
onClick={() => { |
||||
|
props.setSettingsModalOpen(true); |
||||
|
}} |
||||
|
> |
||||
|
<MenuIcon className="m-auto h-6 2-6" /> |
||||
|
</div> |
||||
|
<form |
||||
|
className="flex flex-wrap relative w-full" |
||||
|
onSubmit={(e) => { |
||||
|
e.preventDefault(); |
||||
|
sendMessage(); |
||||
|
}} |
||||
|
> |
||||
|
{props.isReady} |
||||
|
<input |
||||
|
type="text" |
||||
|
placeholder={`${props.translations.no_message_placeholder}...`} |
||||
|
disabled={!props.isReady} |
||||
|
value={currentMessage} |
||||
|
onChange={(e) => { |
||||
|
setCurrentMessage(e.target.value); |
||||
|
}} |
||||
|
className={`p-3 placeholder-gray-400 text-gray-700 relative rounded-md shadow-md focus:outline-none w-full pr-10 ${ |
||||
|
props.isReady ? 'cursor-text' : 'cursor-not-allowed' |
||||
|
}`}
|
||||
|
/> |
||||
|
<span className="flex z-10 h-full text-gray-400 absolute w-8 right-0"> |
||||
|
<PaperAirplaneIcon |
||||
|
onClick={sendMessage} |
||||
|
className={`text-xl hover:text-gray-500 h-6 w-6 my-auto ${ |
||||
|
props.isReady ? 'cursor-pointer' : 'cursor-not-allowed' |
||||
|
}`}
|
||||
|
/> |
||||
|
</span> |
||||
|
</form> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default MessageBox; |
||||
@ -1,67 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
|
|
||||
import { ChevronDownIcon, ChevronRightIcon } from '@heroicons/react/outline'; |
|
||||
|
|
||||
interface NavItemProps { |
|
||||
isDropdown: boolean; |
|
||||
open: boolean; |
|
||||
isNested: boolean; |
|
||||
titleContent: React.ReactNode; |
|
||||
dropdownContent?: React.ReactNode; |
|
||||
onClick?: Function; |
|
||||
isLoading?: boolean; |
|
||||
} |
|
||||
|
|
||||
const NavItem = (props: NavItemProps) => { |
|
||||
React.useEffect(() => { |
|
||||
if (props.open) { |
|
||||
setNavItemOpen(props.open); |
|
||||
} |
|
||||
}, []); |
|
||||
const [navItemOpen, setNavItemOpen] = React.useState(false); |
|
||||
return ( |
|
||||
<> |
|
||||
<div |
|
||||
className={`flex w-full text-lg font-medium justify-between ${ |
|
||||
navItemOpen && props.isNested ? 'bg-gray-100' : null |
|
||||
} ${props.isNested ? 'border-b px-3 py-1' : 'p-3'} ${ |
|
||||
props.isDropdown && navItemOpen ? 'shadow-md' : 'border-b' |
|
||||
} ${ |
|
||||
props.isDropdown || props.isNested |
|
||||
? 'hover:bg-gray-200 cursor-pointer' |
|
||||
: null |
|
||||
}`}
|
|
||||
onClick={() => { |
|
||||
if (props.isDropdown) setNavItemOpen(!navItemOpen); |
|
||||
if (props.onClick) { |
|
||||
props.onClick(); |
|
||||
} |
|
||||
}} |
|
||||
> |
|
||||
{props.titleContent} |
|
||||
{props.isDropdown && !props.isLoading ? ( |
|
||||
navItemOpen ? ( |
|
||||
<ChevronDownIcon className="my-auto group-hover:text-gray-700 w-5 h-5" /> |
|
||||
) : ( |
|
||||
<ChevronRightIcon className="my-auto group-hover:text-gray-700 w-5 h-5" /> |
|
||||
) |
|
||||
) : null} |
|
||||
{props.isLoading ? ( |
|
||||
// <FaSpinner className="animate-spin my-auto" />
|
|
||||
<div>loading</div> |
|
||||
) : null} |
|
||||
</div> |
|
||||
{props.isDropdown ? ( |
|
||||
<div |
|
||||
className={`duration-200 ease-in-out transition-all overflow-hidden max-h-0 border-l-8 ${ |
|
||||
props.isNested ? 'border-gray-500' : 'border-gray-300' |
|
||||
} ${navItemOpen ? 'max-h-full' : null}`}
|
|
||||
> |
|
||||
{props.dropdownContent} |
|
||||
</div> |
|
||||
) : null} |
|
||||
</> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export default NavItem; |
|
||||
@ -0,0 +1,110 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Disclosure } from '@headlessui/react'; |
||||
|
import { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export interface ChannelProps { |
||||
|
channel: Protobuf.Channel; |
||||
|
} |
||||
|
|
||||
|
const Channel = (props: ChannelProps) => { |
||||
|
return ( |
||||
|
<Disclosure> |
||||
|
{({ open }) => ( |
||||
|
<> |
||||
|
<Disclosure.Button className="flex w-full text-lg font-medium justify-between p-3 border-b hover:bg-gray-200 cursor-pointer"> |
||||
|
<div className="flex"> |
||||
|
{props.channel.index} -{' '} |
||||
|
{Protobuf.Channel_Role[props.channel.role]} |
||||
|
</div> |
||||
|
</Disclosure.Button> |
||||
|
<Disclosure.Panel> |
||||
|
<div className="w-full"> |
||||
|
<div className="flex justify-between border-b hover:bg-gray-200"> |
||||
|
<p>Bandwidth:</p> |
||||
|
<code className="bg-gray-200 rounded-full px-2"> |
||||
|
{props.channel.settings?.bandwidth} |
||||
|
</code> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex justify-between border-b hover:bg-gray-200"> |
||||
|
<p>Channel Number:</p> |
||||
|
<code className="bg-gray-200 rounded-full px-2"> |
||||
|
{props.channel.settings?.channelNum} |
||||
|
</code> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex justify-between border-b hover:bg-gray-200"> |
||||
|
<p>Coding Rate:</p> |
||||
|
<code className="bg-gray-200 rounded-full px-2"> |
||||
|
{props.channel.settings?.codingRate} |
||||
|
</code> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex justify-between border-b hover:bg-gray-200"> |
||||
|
<p>ID:</p> |
||||
|
<code className="bg-gray-200 rounded-full px-2"> |
||||
|
{props.channel.settings?.id} |
||||
|
</code> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex justify-between border-b hover:bg-gray-200"> |
||||
|
<p>Modem Config:</p> |
||||
|
<code className="bg-gray-200 rounded-full px-2"> |
||||
|
{props.channel.settings?.modemConfig |
||||
|
? Protobuf.ChannelSettings_ModemConfig[ |
||||
|
props.channel.settings.modemConfig |
||||
|
] |
||||
|
: null} |
||||
|
</code> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex justify-between border-b hover:bg-gray-200"> |
||||
|
<p>Name:</p> |
||||
|
<code className="bg-gray-200 rounded-full px-2"> |
||||
|
{props.channel.settings?.name} |
||||
|
</code> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex justify-between border-b hover:bg-gray-200"> |
||||
|
<p>PSK:</p> |
||||
|
<code className="bg-gray-200 rounded-full px-2"> |
||||
|
{props.channel.settings?.psk.toLocaleString()} |
||||
|
</code> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex justify-between border-b hover:bg-gray-200"> |
||||
|
<p>Spread Factor:</p> |
||||
|
<code className="bg-gray-200 rounded-full px-2"> |
||||
|
{props.channel.settings?.spreadFactor} |
||||
|
</code> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex justify-between border-b hover:bg-gray-200"> |
||||
|
<p>Tx Power:</p> |
||||
|
<code className="bg-gray-200 rounded-full px-2"> |
||||
|
{props.channel.settings?.txPower} |
||||
|
</code> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex justify-between border-b hover:bg-gray-200"> |
||||
|
<p>Uplink:</p> |
||||
|
<code className="bg-gray-200 rounded-full px-2"> |
||||
|
{props.channel.settings?.uplinkEnabled ? 'true' : 'false'} |
||||
|
</code> |
||||
|
</div> |
||||
|
<div className="flex justify-between border-b hover:bg-gray-200"> |
||||
|
<p>Downlink:</p> |
||||
|
<code className="bg-gray-200 rounded-full px-2"> |
||||
|
{props.channel.settings?.downlinkEnabled ? 'true' : 'false'} |
||||
|
</code> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Disclosure.Panel> |
||||
|
</> |
||||
|
)} |
||||
|
</Disclosure> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default Channel; |
||||
@ -0,0 +1,50 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Disclosure } from '@headlessui/react'; |
||||
|
import { |
||||
|
ChevronDownIcon, |
||||
|
ChevronRightIcon, |
||||
|
HashtagIcon, |
||||
|
} from '@heroicons/react/outline'; |
||||
|
import { Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
import type { languageTemplate } from '../../../App'; |
||||
|
import Channel from './Channel'; |
||||
|
|
||||
|
export interface ChannelsProps { |
||||
|
isReady: boolean; |
||||
|
channels: Protobuf.Channel[]; |
||||
|
translations: languageTemplate; |
||||
|
} |
||||
|
|
||||
|
const Channels = (props: ChannelsProps) => { |
||||
|
return ( |
||||
|
<Disclosure> |
||||
|
{({ open }) => ( |
||||
|
<> |
||||
|
<Disclosure.Button className="flex w-full text-lg font-medium justify-between p-3 border-b hover:bg-gray-200 cursor-pointer"> |
||||
|
<div className="flex"> |
||||
|
<HashtagIcon className="my-auto mr-2 2-5 h-5" /> |
||||
|
{props.translations.device_channels_title} |
||||
|
</div> |
||||
|
{open ? ( |
||||
|
<ChevronDownIcon className="my-auto group-hover:text-gray-700 w-5 h-5" /> |
||||
|
) : ( |
||||
|
<ChevronRightIcon className="my-auto group-hover:text-gray-700 w-5 h-5" /> |
||||
|
)} |
||||
|
</Disclosure.Button> |
||||
|
<Disclosure.Panel> |
||||
|
<> |
||||
|
{props.channels.map((channel, index) => { |
||||
|
if (channel.role !== Protobuf.Channel_Role.DISABLED) |
||||
|
return <Channel channel={channel} />; |
||||
|
})} |
||||
|
</> |
||||
|
</Disclosure.Panel> |
||||
|
</> |
||||
|
)} |
||||
|
</Disclosure> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default Channels; |
||||
@ -0,0 +1,124 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Disclosure } from '@headlessui/react'; |
||||
|
import { |
||||
|
AdjustmentsIcon, |
||||
|
ChevronDownIcon, |
||||
|
ChevronRightIcon, |
||||
|
SaveIcon, |
||||
|
} from '@heroicons/react/outline'; |
||||
|
import { IHTTPConnection, Protobuf } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
import type { languageTemplate } from '../../../App'; |
||||
|
|
||||
|
interface DeviceProps { |
||||
|
isReady: boolean; |
||||
|
preferences: Protobuf.RadioConfig_UserPreferences; |
||||
|
connection: IHTTPConnection; |
||||
|
translations: languageTemplate; |
||||
|
} |
||||
|
|
||||
|
const Device = (props: DeviceProps) => { |
||||
|
return ( |
||||
|
<Disclosure> |
||||
|
{({ open }) => ( |
||||
|
<> |
||||
|
<Disclosure.Button className="flex w-full text-lg font-medium justify-between p-3 border-b hover:bg-gray-200 cursor-pointer"> |
||||
|
<div className="flex"> |
||||
|
<AdjustmentsIcon className="my-auto mr-2 w-5 h-5" /> |
||||
|
{props.translations.device_settings_title} |
||||
|
</div> |
||||
|
{open ? ( |
||||
|
<ChevronDownIcon className="my-auto group-hover:text-gray-700 w-5 h-5" /> |
||||
|
) : ( |
||||
|
<ChevronRightIcon className="my-auto group-hover:text-gray-700 w-5 h-5" /> |
||||
|
)} |
||||
|
</Disclosure.Button> |
||||
|
<Disclosure.Panel> |
||||
|
<> |
||||
|
<div className="flex whitespace-nowrap p-3 justify-between border-b"> |
||||
|
<div className="my-auto"> |
||||
|
{props.translations.device_region_title} |
||||
|
</div> |
||||
|
<div className="flex shadow-md rounded-md ml-2"> |
||||
|
<select |
||||
|
value={ |
||||
|
props.preferences?.region ?? Protobuf.RegionCode.Unset |
||||
|
} |
||||
|
onChange={(e) => { |
||||
|
props.preferences.region = parseInt(e.target.value); |
||||
|
}} |
||||
|
> |
||||
|
<option value={Protobuf.RegionCode.ANZ}> |
||||
|
{Protobuf.RegionCode[Protobuf.RegionCode.ANZ]} |
||||
|
</option> |
||||
|
<option value={Protobuf.RegionCode.CN}> |
||||
|
{Protobuf.RegionCode[Protobuf.RegionCode.CN]} |
||||
|
</option> |
||||
|
<option value={Protobuf.RegionCode.EU433}> |
||||
|
{Protobuf.RegionCode[Protobuf.RegionCode.EU433]} |
||||
|
</option> |
||||
|
<option value={Protobuf.RegionCode.EU865}> |
||||
|
{Protobuf.RegionCode[Protobuf.RegionCode.EU865]} |
||||
|
</option> |
||||
|
<option value={Protobuf.RegionCode.JP}> |
||||
|
{Protobuf.RegionCode[Protobuf.RegionCode.JP]} |
||||
|
</option> |
||||
|
<option value={Protobuf.RegionCode.KR}> |
||||
|
{Protobuf.RegionCode[Protobuf.RegionCode.KR]} |
||||
|
</option> |
||||
|
<option value={Protobuf.RegionCode.TW}> |
||||
|
{Protobuf.RegionCode[Protobuf.RegionCode.TW]} |
||||
|
</option> |
||||
|
<option value={Protobuf.RegionCode.US}> |
||||
|
{Protobuf.RegionCode[Protobuf.RegionCode.US]} |
||||
|
</option> |
||||
|
<option value={Protobuf.RegionCode.Unset}> |
||||
|
{Protobuf.RegionCode[Protobuf.RegionCode.Unset]} |
||||
|
</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className="flex whitespace-nowrap p-3 justify-between border-b"> |
||||
|
<div className="my-auto"> |
||||
|
{props.translations.device_wifi_ssid} |
||||
|
</div> |
||||
|
<div className="flex shadow-md rounded-md ml-2"> |
||||
|
<input |
||||
|
onChange={() => {}} |
||||
|
type="text" |
||||
|
value={props.preferences.wifiSsid} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className="flex whitespace-nowrap p-3 justify-between border-b"> |
||||
|
<div className="my-auto"> |
||||
|
{props.translations.device_wifi_psk} |
||||
|
</div> |
||||
|
<div className="flex shadow-md rounded-md ml-2"> |
||||
|
<input |
||||
|
type="password" |
||||
|
value={props.preferences.wifiPassword} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className="flex group p-1 bg-gray-100 cursor-pointer hover:bg-gray-200 border-b"> |
||||
|
<div |
||||
|
className="flex m-auto font-medium group-hover:text-gray-700" |
||||
|
onClick={() => { |
||||
|
props.connection.setPreferences(props.preferences); |
||||
|
}} |
||||
|
> |
||||
|
<SaveIcon className="m-auto mr-2 group-hover:text-gray-700 w-5 h-5" /> |
||||
|
{props.translations.save_changes_button} |
||||
|
</div> |
||||
|
</div> |
||||
|
</> |
||||
|
</Disclosure.Panel> |
||||
|
</> |
||||
|
)} |
||||
|
</Disclosure> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default Device; |
||||
@ -0,0 +1,56 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Disclosure } from '@headlessui/react'; |
||||
|
import { |
||||
|
ChevronDownIcon, |
||||
|
ChevronRightIcon, |
||||
|
UsersIcon, |
||||
|
} from '@heroicons/react/outline'; |
||||
|
import type { Types } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
import type { languageTemplate } from '../../../App'; |
||||
|
import Node from './Node'; |
||||
|
|
||||
|
interface NodesProps { |
||||
|
isReady: boolean; |
||||
|
nodes: Types.NodeInfoPacket[]; |
||||
|
translations: languageTemplate; |
||||
|
myId: number; |
||||
|
} |
||||
|
|
||||
|
const Nodes = (props: NodesProps) => { |
||||
|
return ( |
||||
|
<Disclosure> |
||||
|
{({ open }) => ( |
||||
|
<> |
||||
|
<Disclosure.Button className="flex rounded-t-md w-full text-lg font-medium justify-between p-3 border-b hover:bg-gray-200 cursor-pointer"> |
||||
|
<div className="flex"> |
||||
|
<UsersIcon className="my-auto mr-2 w-5 h-5" /> |
||||
|
{props.translations.nodes_title} |
||||
|
</div> |
||||
|
{open ? ( |
||||
|
<ChevronDownIcon className="my-auto group-hover:text-gray-700 w-5 h-5" /> |
||||
|
) : ( |
||||
|
<ChevronRightIcon className="my-auto group-hover:text-gray-700 w-5 h-5" /> |
||||
|
)} |
||||
|
</Disclosure.Button> |
||||
|
<Disclosure.Panel className="shadow-inner"> |
||||
|
{props.nodes.length ? ( |
||||
|
props.nodes.map((node, index) => ( |
||||
|
<Node key={index} node={node} myId={props.myId} /> |
||||
|
)) |
||||
|
) : ( |
||||
|
<div className="flex border-b border-gray-300"> |
||||
|
<div className="m-auto p-3 text-gray-500"> |
||||
|
{props.translations.no_nodes_message} |
||||
|
</div> |
||||
|
</div> |
||||
|
)} |
||||
|
</Disclosure.Panel> |
||||
|
</> |
||||
|
)} |
||||
|
</Disclosure> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default Nodes; |
||||
@ -0,0 +1,82 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Disclosure } from '@headlessui/react'; |
||||
|
import { |
||||
|
ClockIcon, |
||||
|
DesktopComputerIcon, |
||||
|
FlagIcon, |
||||
|
GlobeIcon, |
||||
|
LightningBoltIcon, |
||||
|
} from '@heroicons/react/outline'; |
||||
|
import type { Types } from '@meshtastic/meshtasticjs'; |
||||
|
|
||||
|
export interface NodeProps { |
||||
|
node: Types.NodeInfoPacket; |
||||
|
myId: number; |
||||
|
} |
||||
|
|
||||
|
const Node = (props: NodeProps) => { |
||||
|
return ( |
||||
|
<Disclosure> |
||||
|
{({ open }) => ( |
||||
|
<> |
||||
|
<Disclosure.Button className="flex w-full text-lg font-medium justify-between p-3 border-b hover:bg-gray-200 cursor-pointer"> |
||||
|
<div className="flex"> |
||||
|
{props.node.data.num === props.myId ? ( |
||||
|
<FlagIcon className="text-yellow-500 my-auto mr-2 w-5 h-5" /> |
||||
|
) : ( |
||||
|
<DesktopComputerIcon className="my-auto mr-2 w-5 h-5" /> |
||||
|
)} |
||||
|
<div className="m-auto">{props.node.data.user?.longName}</div> |
||||
|
</div> |
||||
|
</Disclosure.Button> |
||||
|
<Disclosure.Panel> |
||||
|
<div> |
||||
|
<p> |
||||
|
SNR:{' '} |
||||
|
{props.node.packet?.rxSnr ? props.node.packet.rxSnr : 'Unknown'} |
||||
|
</p> |
||||
|
<p> |
||||
|
RSSI:{' '} |
||||
|
{props.node.packet?.rxRssi |
||||
|
? props.node.packet.rxRssi |
||||
|
: 'Unknown'} |
||||
|
</p> |
||||
|
<p> |
||||
|
{`Last heard: ${ |
||||
|
props.node.data?.lastHeard |
||||
|
? new Date(props.node.data.lastHeard).toLocaleString() |
||||
|
: 'Unknown' |
||||
|
}`}{' '}
|
||||
|
{} |
||||
|
</p> |
||||
|
<div className="flex"> |
||||
|
<GlobeIcon className="my-auto mr-2 w-5 h-5" /> |
||||
|
<p> |
||||
|
{props.node.data.position?.latitudeI}, |
||||
|
{props.node.data.position?.longitudeI} |
||||
|
</p> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex"> |
||||
|
<GlobeIcon className="my-auto mr-2 w-5 h-5" /> |
||||
|
<p>{props.node.data.position?.altitude}</p> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex"> |
||||
|
<ClockIcon className="my-auto mr-2 w-5 h-5" /> |
||||
|
<p>{props.node.data.position?.time}</p> |
||||
|
</div> |
||||
|
<div className="flex"> |
||||
|
<LightningBoltIcon className="my-auto mr-2 w-5 h-5" /> |
||||
|
<p>{props.node.data.position?.batteryLevel}</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Disclosure.Panel> |
||||
|
</> |
||||
|
)} |
||||
|
</Disclosure> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default Node; |
||||
@ -1,146 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
|
|
||||
import { HashtagIcon } from '@heroicons/react/outline'; |
|
||||
import { Protobuf } from '@meshtastic/meshtasticjs'; |
|
||||
|
|
||||
import type { languageTemplate } from '../../App'; |
|
||||
import NavItem from '../NavItem'; |
|
||||
|
|
||||
interface SidebarChannelsProps { |
|
||||
IsReady: boolean; |
|
||||
Channels: Protobuf.Channel[]; |
|
||||
Translations: languageTemplate; |
|
||||
} |
|
||||
|
|
||||
const SidebarChannels = (props: SidebarChannelsProps) => { |
|
||||
return ( |
|
||||
<NavItem |
|
||||
isDropdown={true} |
|
||||
open={false} |
|
||||
isNested={false} |
|
||||
titleContent={ |
|
||||
<div className="flex"> |
|
||||
<HashtagIcon className="my-auto mr-2 2-5 h-5" /> |
|
||||
{props.Translations.device_channels_title} |
|
||||
</div> |
|
||||
} |
|
||||
isLoading={!props.IsReady} |
|
||||
dropdownContent={ |
|
||||
<> |
|
||||
{props.Channels.map((channel, index) => { |
|
||||
if (channel.role !== Protobuf.Channel_Role.DISABLED) |
|
||||
return ( |
|
||||
<NavItem |
|
||||
key={index} |
|
||||
isDropdown={true} |
|
||||
isNested={true} |
|
||||
open={false} |
|
||||
titleContent={ |
|
||||
<div className="flex"> |
|
||||
{channel.index} - {Protobuf.Channel_Role[channel.role]} |
|
||||
</div> |
|
||||
} |
|
||||
dropdownContent={ |
|
||||
<NavItem |
|
||||
isDropdown={false} |
|
||||
isNested={false} |
|
||||
open={false} |
|
||||
titleContent={ |
|
||||
<div className="w-full"> |
|
||||
<div className="flex justify-between border-b hover:bg-gray-200"> |
|
||||
<p>Bandwidth:</p> |
|
||||
<code className="bg-gray-200 rounded-full px-2"> |
|
||||
{channel.settings?.bandwidth} |
|
||||
</code> |
|
||||
</div> |
|
||||
|
|
||||
<div className="flex justify-between border-b hover:bg-gray-200"> |
|
||||
<p>Channel Number:</p> |
|
||||
<code className="bg-gray-200 rounded-full px-2"> |
|
||||
{channel.settings?.channelNum} |
|
||||
</code> |
|
||||
</div> |
|
||||
|
|
||||
<div className="flex justify-between border-b hover:bg-gray-200"> |
|
||||
<p>Coding Rate:</p> |
|
||||
<code className="bg-gray-200 rounded-full px-2"> |
|
||||
{channel.settings?.codingRate} |
|
||||
</code> |
|
||||
</div> |
|
||||
|
|
||||
<div className="flex justify-between border-b hover:bg-gray-200"> |
|
||||
<p>ID:</p> |
|
||||
<code className="bg-gray-200 rounded-full px-2"> |
|
||||
{channel.settings?.id} |
|
||||
</code> |
|
||||
</div> |
|
||||
|
|
||||
<div className="flex justify-between border-b hover:bg-gray-200"> |
|
||||
<p>Modem Config:</p> |
|
||||
<code className="bg-gray-200 rounded-full px-2"> |
|
||||
{channel.settings?.modemConfig |
|
||||
? Protobuf.ChannelSettings_ModemConfig[ |
|
||||
channel.settings.modemConfig |
|
||||
] |
|
||||
: null} |
|
||||
</code> |
|
||||
</div> |
|
||||
|
|
||||
<div className="flex justify-between border-b hover:bg-gray-200"> |
|
||||
<p>Name:</p> |
|
||||
<code className="bg-gray-200 rounded-full px-2"> |
|
||||
{channel.settings?.name} |
|
||||
</code> |
|
||||
</div> |
|
||||
|
|
||||
<div className="flex justify-between border-b hover:bg-gray-200"> |
|
||||
<p>PSK:</p> |
|
||||
<code className="bg-gray-200 rounded-full px-2"> |
|
||||
{channel.settings?.psk.toLocaleString()} |
|
||||
</code> |
|
||||
</div> |
|
||||
|
|
||||
<div className="flex justify-between border-b hover:bg-gray-200"> |
|
||||
<p>Spread Factor:</p> |
|
||||
<code className="bg-gray-200 rounded-full px-2"> |
|
||||
{channel.settings?.spreadFactor} |
|
||||
</code> |
|
||||
</div> |
|
||||
|
|
||||
<div className="flex justify-between border-b hover:bg-gray-200"> |
|
||||
<p>Tx Power:</p> |
|
||||
<code className="bg-gray-200 rounded-full px-2"> |
|
||||
{channel.settings?.txPower} |
|
||||
</code> |
|
||||
</div> |
|
||||
|
|
||||
<div className="flex justify-between border-b hover:bg-gray-200"> |
|
||||
<p>Uplink:</p> |
|
||||
<code className="bg-gray-200 rounded-full px-2"> |
|
||||
{channel.settings?.uplinkEnabled |
|
||||
? 'true' |
|
||||
: 'false'} |
|
||||
</code> |
|
||||
</div> |
|
||||
<div className="flex justify-between border-b hover:bg-gray-200"> |
|
||||
<p>Downlink:</p> |
|
||||
<code className="bg-gray-200 rounded-full px-2"> |
|
||||
{channel.settings?.downlinkEnabled |
|
||||
? 'true' |
|
||||
: 'false'} |
|
||||
</code> |
|
||||
</div> |
|
||||
</div> |
|
||||
} |
|
||||
/> |
|
||||
} |
|
||||
/> |
|
||||
); |
|
||||
})} |
|
||||
</> |
|
||||
} |
|
||||
/> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export default SidebarChannels; |
|
||||
@ -1,105 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
|
|
||||
import { AdjustmentsIcon, SaveIcon } from '@heroicons/react/outline'; |
|
||||
import { IHTTPConnection, Protobuf } from '@meshtastic/meshtasticjs'; |
|
||||
|
|
||||
import type { languageTemplate } from '../../App'; |
|
||||
import NavItem from '../NavItem'; |
|
||||
|
|
||||
interface SidebarDeviceSettingsProps { |
|
||||
IsReady: boolean; |
|
||||
Preferences: Protobuf.RadioConfig_UserPreferences; |
|
||||
Connection: IHTTPConnection; |
|
||||
Translations: languageTemplate; |
|
||||
} |
|
||||
|
|
||||
const SidebarDeviceSettings = (props: SidebarDeviceSettingsProps) => { |
|
||||
return ( |
|
||||
<NavItem |
|
||||
isDropdown={true} |
|
||||
open={false} |
|
||||
isNested={false} |
|
||||
titleContent={ |
|
||||
<div className="flex"> |
|
||||
<AdjustmentsIcon className="my-auto mr-2 w-5 h-5" /> |
|
||||
{props.Translations.device_settings_title} |
|
||||
</div> |
|
||||
} |
|
||||
isLoading={!props.IsReady} |
|
||||
dropdownContent={ |
|
||||
<> |
|
||||
<div className="flex whitespace-nowrap p-3 justify-between border-b"> |
|
||||
<div className="my-auto"> |
|
||||
{props.Translations.device_region_title} |
|
||||
</div> |
|
||||
<div className="flex shadow-md rounded-md ml-2"> |
|
||||
<select |
|
||||
value={props.Preferences?.region ?? Protobuf.RegionCode.Unset} |
|
||||
onChange={(e) => { |
|
||||
props.Preferences.region = parseInt(e.target.value); |
|
||||
}} |
|
||||
> |
|
||||
<option value={Protobuf.RegionCode.ANZ}> |
|
||||
{Protobuf.RegionCode[Protobuf.RegionCode.ANZ]} |
|
||||
</option> |
|
||||
<option value={Protobuf.RegionCode.CN}> |
|
||||
{Protobuf.RegionCode[Protobuf.RegionCode.CN]} |
|
||||
</option> |
|
||||
<option value={Protobuf.RegionCode.EU433}> |
|
||||
{Protobuf.RegionCode[Protobuf.RegionCode.EU433]} |
|
||||
</option> |
|
||||
<option value={Protobuf.RegionCode.EU865}> |
|
||||
{Protobuf.RegionCode[Protobuf.RegionCode.EU865]} |
|
||||
</option> |
|
||||
<option value={Protobuf.RegionCode.JP}> |
|
||||
{Protobuf.RegionCode[Protobuf.RegionCode.JP]} |
|
||||
</option> |
|
||||
<option value={Protobuf.RegionCode.KR}> |
|
||||
{Protobuf.RegionCode[Protobuf.RegionCode.KR]} |
|
||||
</option> |
|
||||
<option value={Protobuf.RegionCode.TW}> |
|
||||
{Protobuf.RegionCode[Protobuf.RegionCode.TW]} |
|
||||
</option> |
|
||||
<option value={Protobuf.RegionCode.US}> |
|
||||
{Protobuf.RegionCode[Protobuf.RegionCode.US]} |
|
||||
</option> |
|
||||
<option value={Protobuf.RegionCode.Unset}> |
|
||||
{Protobuf.RegionCode[Protobuf.RegionCode.Unset]} |
|
||||
</option> |
|
||||
</select> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div className="flex whitespace-nowrap p-3 justify-between border-b"> |
|
||||
<div className="my-auto">{props.Translations.device_wifi_ssid}</div> |
|
||||
<div className="flex shadow-md rounded-md ml-2"> |
|
||||
<input |
|
||||
onChange={() => {}} |
|
||||
type="text" |
|
||||
value={props.Preferences.wifiSsid} |
|
||||
/> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div className="flex whitespace-nowrap p-3 justify-between border-b"> |
|
||||
<div className="my-auto">{props.Translations.device_wifi_psk}</div> |
|
||||
<div className="flex shadow-md rounded-md ml-2"> |
|
||||
<input type="password" value={props.Preferences.wifiPassword} /> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div className="flex group p-1 bg-gray-100 cursor-pointer hover:bg-gray-200 border-b"> |
|
||||
<div |
|
||||
className="flex m-auto font-medium group-hover:text-gray-700" |
|
||||
onClick={() => { |
|
||||
props.Connection.setPreferences(props.Preferences); |
|
||||
}} |
|
||||
> |
|
||||
<SaveIcon className="m-auto mr-2 group-hover:text-gray-700 w-5 h-5" /> |
|
||||
{props.Translations.save_changes_button} |
|
||||
</div> |
|
||||
</div> |
|
||||
</> |
|
||||
} |
|
||||
/> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export default SidebarDeviceSettings; |
|
||||
@ -1,119 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
|
|
||||
import { |
|
||||
ClockIcon, |
|
||||
DesktopComputerIcon, |
|
||||
FlagIcon, |
|
||||
GlobeIcon, |
|
||||
LightningBoltIcon, |
|
||||
UsersIcon, |
|
||||
} from '@heroicons/react/outline'; |
|
||||
import type { Types } from '@meshtastic/meshtasticjs'; |
|
||||
|
|
||||
import type { languageTemplate } from '../../App'; |
|
||||
import NavItem from '../NavItem'; |
|
||||
|
|
||||
interface sidebarNodesProps { |
|
||||
IsReady: boolean; |
|
||||
Nodes: Types.NodeInfoPacket[]; |
|
||||
Translations: languageTemplate; |
|
||||
myId: number; |
|
||||
} |
|
||||
|
|
||||
const SidebarNodes = (props: sidebarNodesProps) => { |
|
||||
return ( |
|
||||
<NavItem |
|
||||
isDropdown={true} |
|
||||
open={false} |
|
||||
isNested={false} |
|
||||
titleContent={ |
|
||||
<div className="flex"> |
|
||||
<UsersIcon className="my-auto mr-2 w-5 h-5" /> |
|
||||
{props.Translations.nodes_title} |
|
||||
<div className="flex m-auto rounded-full bg-gray-300 w-6 h-6 text-sm ml-2"> |
|
||||
<div className="m-auto">{props.Nodes.length ?? 0}</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
} |
|
||||
isLoading={!props.IsReady} |
|
||||
dropdownContent={ |
|
||||
props.Nodes.length ? ( |
|
||||
props.Nodes.map((node, index) => ( |
|
||||
<NavItem |
|
||||
key={index} |
|
||||
isDropdown={true} |
|
||||
isNested={true} |
|
||||
open={false} |
|
||||
titleContent={ |
|
||||
<div key={index} className="flex"> |
|
||||
{node.data.num === props.myId ? ( |
|
||||
<FlagIcon className="text-yellow-500 my-auto mr-2 w-5 h-5" /> |
|
||||
) : ( |
|
||||
<DesktopComputerIcon className="my-auto mr-2 w-5 h-5" /> |
|
||||
)} |
|
||||
<div className="m-auto">{node.data.user?.longName}</div> |
|
||||
</div> |
|
||||
} |
|
||||
dropdownContent={ |
|
||||
<NavItem |
|
||||
isDropdown={false} |
|
||||
isNested={true} |
|
||||
open={false} |
|
||||
titleContent={ |
|
||||
<div> |
|
||||
<p> |
|
||||
SNR:{' '} |
|
||||
{node.packet?.rxSnr ? node.packet.rxSnr : 'Unknown'} |
|
||||
</p> |
|
||||
<p> |
|
||||
RSSI:{' '} |
|
||||
{node.packet?.rxRssi ? node.packet.rxRssi : 'Unknown'} |
|
||||
</p> |
|
||||
<p> |
|
||||
{`Last heard: ${ |
|
||||
node.data?.lastHeard |
|
||||
? new Date(node.data.lastHeard).toLocaleString() |
|
||||
: 'Unknown' |
|
||||
}`}{' '}
|
|
||||
{} |
|
||||
</p> |
|
||||
<div className="flex"> |
|
||||
<GlobeIcon className="my-auto mr-2 w-5 h-5" /> |
|
||||
<p> |
|
||||
{node.data.position?.latitudeI}, |
|
||||
{node.data.position?.longitudeI} |
|
||||
</p> |
|
||||
</div> |
|
||||
|
|
||||
<div className="flex"> |
|
||||
<GlobeIcon className="my-auto mr-2 w-5 h-5" /> |
|
||||
<p>{node.data.position?.altitude}</p> |
|
||||
</div> |
|
||||
|
|
||||
<div className="flex"> |
|
||||
<ClockIcon className="my-auto mr-2 w-5 h-5" /> |
|
||||
<p>{node.data.position?.time}</p> |
|
||||
</div> |
|
||||
<div className="flex"> |
|
||||
<LightningBoltIcon className="my-auto mr-2 w-5 h-5" /> |
|
||||
<p>{node.data.position?.batteryLevel}</p> |
|
||||
</div> |
|
||||
</div> |
|
||||
} |
|
||||
/> |
|
||||
} |
|
||||
/> |
|
||||
)) |
|
||||
) : ( |
|
||||
<div className="flex border-b border-gray-300"> |
|
||||
<div className="m-auto p-3 text-gray-500"> |
|
||||
{props.Translations.no_nodes_message} |
|
||||
</div> |
|
||||
</div> |
|
||||
) |
|
||||
} |
|
||||
/> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export default SidebarNodes; |
|
||||
@ -1,132 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
|
|
||||
import { Br, Jp, Us } from 'react-flags-select'; |
|
||||
|
|
||||
import { CogIcon } from '@heroicons/react/outline'; |
|
||||
|
|
||||
import type { languageTemplate } from '../../App'; |
|
||||
import { LanguageEnum } from '../../App'; |
|
||||
import ToggleSwitch from '../basic/ToggleSwitch'; |
|
||||
import NavItem from '../NavItem'; |
|
||||
|
|
||||
interface SidebarUISettingsProps { |
|
||||
Language: LanguageEnum; |
|
||||
SetLanguage: React.Dispatch<React.SetStateAction<LanguageEnum>>; |
|
||||
Translations: languageTemplate; |
|
||||
} |
|
||||
|
|
||||
const SidebarUISettings = (props: SidebarUISettingsProps) => { |
|
||||
return ( |
|
||||
<NavItem |
|
||||
isDropdown={true} |
|
||||
isNested={false} |
|
||||
open={false} |
|
||||
titleContent={ |
|
||||
<div className="flex"> |
|
||||
<CogIcon className="my-auto mr-2 w-5 h-5" /> |
|
||||
{props.Translations.ui_settings_title} |
|
||||
</div> |
|
||||
} |
|
||||
dropdownContent={ |
|
||||
<> |
|
||||
<NavItem |
|
||||
isDropdown={false} |
|
||||
isNested={true} |
|
||||
open={false} |
|
||||
titleContent={ |
|
||||
<> |
|
||||
<div className="my-auto"> |
|
||||
{props.Translations.color_scheme_title} |
|
||||
</div> |
|
||||
<div className="flex shadow-md rounded-md ml-2"> |
|
||||
{/* <div className="bg-gray-200 flex group p-2 rounded-l-md border border-gray-300 hover:bg-gray-200 cursor-pointer"> |
|
||||
<FaSun className="m-auto group-hover:text-gray-700" /> |
|
||||
</div> |
|
||||
<div className="flex group p-2 border border-gray-300 hover:bg-gray-200 cursor-pointer"> |
|
||||
<FaMoon className="m-auto group-hover:text-gray-700" /> |
|
||||
</div> |
|
||||
<div className="flex group p-2 rounded-r-md border border-gray-300 hover:bg-gray-200 cursor-pointer"> |
|
||||
<FaLaptop className="m-auto group-hover:text-gray-700" /> |
|
||||
</div> */} |
|
||||
</div> |
|
||||
</> |
|
||||
} |
|
||||
/> |
|
||||
<NavItem |
|
||||
isDropdown={true} |
|
||||
isNested={true} |
|
||||
open={false} |
|
||||
titleContent={ |
|
||||
<div className="flex my-auto"> |
|
||||
{props.Translations.language_title} |
|
||||
<div className="my-auto"> |
|
||||
{props.Language === LanguageEnum.ENGLISH ? ( |
|
||||
<Us className="ml-2 w-8 shadow-md" /> |
|
||||
) : props.Language === LanguageEnum.JAPANESE ? ( |
|
||||
<Jp className="ml-2 w-8 shadow-md" /> |
|
||||
) : null} |
|
||||
</div> |
|
||||
</div> |
|
||||
} |
|
||||
dropdownContent={ |
|
||||
<> |
|
||||
<NavItem |
|
||||
onClick={() => { |
|
||||
props.SetLanguage(LanguageEnum.ENGLISH); |
|
||||
}} |
|
||||
open={false} |
|
||||
isDropdown={false} |
|
||||
isNested={true} |
|
||||
titleContent={ |
|
||||
<> |
|
||||
English <Us className="w-8 shadow-md" /> |
|
||||
</> |
|
||||
} |
|
||||
/> |
|
||||
<NavItem |
|
||||
onClick={() => { |
|
||||
props.SetLanguage(LanguageEnum.PORTUGUESE); |
|
||||
}} |
|
||||
open={false} |
|
||||
isDropdown={false} |
|
||||
isNested={true} |
|
||||
titleContent={ |
|
||||
<> |
|
||||
Português <Br className="w-8 shadow-md" /> |
|
||||
</> |
|
||||
} |
|
||||
/> |
|
||||
<NavItem |
|
||||
onClick={() => { |
|
||||
props.SetLanguage(LanguageEnum.JAPANESE); |
|
||||
}} |
|
||||
open={false} |
|
||||
isDropdown={false} |
|
||||
isNested={true} |
|
||||
titleContent={ |
|
||||
<> |
|
||||
日本語 <Jp className="w-8 shadow-md" /> |
|
||||
</> |
|
||||
} |
|
||||
/> |
|
||||
</> |
|
||||
} |
|
||||
/> |
|
||||
<NavItem |
|
||||
isDropdown={false} |
|
||||
isNested={true} |
|
||||
open={false} |
|
||||
titleContent={ |
|
||||
<> |
|
||||
<div className="">Test</div> |
|
||||
<ToggleSwitch active={true} /> |
|
||||
</> |
|
||||
} |
|
||||
/> |
|
||||
</> |
|
||||
} |
|
||||
/> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export default SidebarUISettings; |
|
||||
@ -0,0 +1,50 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Disclosure } from '@headlessui/react'; |
||||
|
import { |
||||
|
ChevronDownIcon, |
||||
|
ChevronRightIcon, |
||||
|
CogIcon, |
||||
|
} from '@heroicons/react/outline'; |
||||
|
|
||||
|
import type { LanguageEnum, languageTemplate } from '../../../App'; |
||||
|
import Translations from './Translations'; |
||||
|
|
||||
|
interface UIProps { |
||||
|
language: LanguageEnum; |
||||
|
setLanguage: React.Dispatch<React.SetStateAction<LanguageEnum>>; |
||||
|
translations: languageTemplate; |
||||
|
darkmode: boolean; |
||||
|
setDarkmode: React.Dispatch<React.SetStateAction<boolean>>; |
||||
|
} |
||||
|
|
||||
|
const UI = (props: UIProps) => { |
||||
|
return ( |
||||
|
<Disclosure> |
||||
|
{({ open }) => ( |
||||
|
<> |
||||
|
<Disclosure.Button className="flex w-full text-lg font-medium justify-between p-3 border-b hover:bg-gray-200 cursor-pointer"> |
||||
|
<div className="flex"> |
||||
|
<CogIcon className="my-auto mr-2 w-5 h-5" /> |
||||
|
{props.translations.ui_settings_title} |
||||
|
</div> |
||||
|
{open ? ( |
||||
|
<ChevronDownIcon className="my-auto group-hover:text-gray-700 w-5 h-5" /> |
||||
|
) : ( |
||||
|
<ChevronRightIcon className="my-auto group-hover:text-gray-700 w-5 h-5" /> |
||||
|
)} |
||||
|
</Disclosure.Button> |
||||
|
<Disclosure.Panel> |
||||
|
<Translations |
||||
|
language={props.language} |
||||
|
setLanguage={props.setLanguage} |
||||
|
translations={props.translations} |
||||
|
/> |
||||
|
</Disclosure.Panel> |
||||
|
</> |
||||
|
)} |
||||
|
</Disclosure> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default UI; |
||||
@ -0,0 +1,49 @@ |
|||||
|
import React from 'react'; |
||||
|
|
||||
|
import { Br, Jp, Us } from 'react-flags-select'; |
||||
|
|
||||
|
import { Disclosure } from '@headlessui/react'; |
||||
|
|
||||
|
import { LanguageEnum, languageTemplate } from '../../../App'; |
||||
|
|
||||
|
export interface TranslationsProps { |
||||
|
language: LanguageEnum; |
||||
|
setLanguage: React.Dispatch<React.SetStateAction<LanguageEnum>>; |
||||
|
translations: languageTemplate; |
||||
|
} |
||||
|
|
||||
|
const Translations = (props: TranslationsProps) => { |
||||
|
return ( |
||||
|
<Disclosure> |
||||
|
{({ open }) => ( |
||||
|
<> |
||||
|
<Disclosure.Button className="flex w-full text-lg font-medium justify-between p-3 border-b hover:bg-gray-200 cursor-pointer"> |
||||
|
<div className="flex my-auto"> |
||||
|
{props.translations.language_title} |
||||
|
<div className="my-auto"> |
||||
|
{props.language === LanguageEnum.ENGLISH ? ( |
||||
|
<Us className="ml-2 w-8 shadow-md" /> |
||||
|
) : props.language === LanguageEnum.JAPANESE ? ( |
||||
|
<Jp className="ml-2 w-8 shadow-md" /> |
||||
|
) : null} |
||||
|
</div> |
||||
|
</div> |
||||
|
</Disclosure.Button> |
||||
|
<Disclosure.Panel> |
||||
|
<div> |
||||
|
English <Us className="w-8 shadow-md" /> |
||||
|
</div> |
||||
|
<div> |
||||
|
Português <Br className="w-8 shadow-md" /> |
||||
|
</div> |
||||
|
<div> |
||||
|
日本語 <Jp className="w-8 shadow-md" /> |
||||
|
</div> |
||||
|
</Disclosure.Panel> |
||||
|
</> |
||||
|
)} |
||||
|
</Disclosure> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default Translations; |
||||
@ -1,56 +0,0 @@ |
|||||
Arguments: |
|
||||
/usr/bin/node /usr/share/yarn/bin/yarn.js add -D tailwindcss@latest postcss@latest autoprefixer@latest |
|
||||
|
|
||||
PATH: |
|
||||
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Program Files/AdoptOpenJDK/jdk-11.0.10.9-hotspot/bin:/mnt/c/Program Files (x86)/Common Files/Oracle/Java/javapath:/mnt/c/Windows/system32:/mnt/c/Windows:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0/:/mnt/c/Windows/System32/OpenSSH/:/mnt/c/Program Files (x86)/NVIDIA Corporation/PhysX/Common:/mnt/c/Program Files/Git/cmd:/mnt/c/Program Files/nodejs/:/mnt/c/Users/sacha/AppData/Local/Programs/Python/Python39/Scripts/:/mnt/c/Users/sacha/AppData/Local/Programs/Python/Python39/:/mnt/c/Users/sacha/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/sacha/AppData/Local/Programs/Microsoft VS Code/bin:/mnt/c/Users/sacha/AppData/Roaming/npm:/home/sachaw/.yarn/bin |
|
||||
|
|
||||
Yarn version: |
|
||||
1.22.5 |
|
||||
|
|
||||
Node version: |
|
||||
15.12.0 |
|
||||
|
|
||||
Platform: |
|
||||
linux x64 |
|
||||
|
|
||||
Trace: |
|
||||
Error: getaddrinfo EAI_AGAIN registry.yarnpkg.com |
|
||||
at GetAddrInfoReqWrap.onlookup [as oncomplete] (node:dns:69:26) |
|
||||
|
|
||||
npm manifest: |
|
||||
{ |
|
||||
"scripts": { |
|
||||
"start": "snowpack dev", |
|
||||
"build": "snowpack build", |
|
||||
"test": "web-test-runner \"src/**/*.test.tsx\"", |
|
||||
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"", |
|
||||
"lint": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"" |
|
||||
}, |
|
||||
"dependencies": { |
|
||||
"react": "^17.0.0", |
|
||||
"react-dom": "^17.0.0" |
|
||||
}, |
|
||||
"devDependencies": { |
|
||||
"@snowpack/plugin-dotenv": "^2.0.5", |
|
||||
"@snowpack/plugin-react-refresh": "^2.4.0", |
|
||||
"@snowpack/plugin-typescript": "^1.2.0", |
|
||||
"@snowpack/web-test-runner-plugin": "^0.2.0", |
|
||||
"@testing-library/react": "^11.0.0", |
|
||||
"@types/chai": "^4.2.13", |
|
||||
"@types/mocha": "^8.2.0", |
|
||||
"@types/react": "^17.0.0", |
|
||||
"@types/react-dom": "^17.0.0", |
|
||||
"@types/snowpack-env": "^2.3.2", |
|
||||
"@web/test-runner": "^0.12.0", |
|
||||
"chai": "^4.2.0", |
|
||||
"prettier": "^2.0.5", |
|
||||
"snowpack": "^3.0.1", |
|
||||
"typescript": "^4.0.0" |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
yarn manifest: |
|
||||
No manifest |
|
||||
|
|
||||
Lockfile: |
|
||||
No lockfile |
|
||||
Loading…
Reference in new issue