Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 25 additions & 23 deletions app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,55 @@

<section id="main" class="text-gray-400 body-font px-5">
<transition name="flash" mode="out-in">
<div class="container py-1 mx-auto transition duration-900 ease-in-out" v-show="!serialMonitorStore.isConnected">
<div class="container mx-auto transition duration-900 ease-in-out" v-show="!serialMonitorStore.isConnected">
<div class="flex flex-col content-center justify-center">
<div class="flex flex-wrap sm:flex-row flex-col py-1">
<div class="flex flex-wrap sm:flex-row flex-col py-2">
<LogoHeader />
</div>
<hr class="w-full mx-auto mb-4 border-gray-600" />
<hr class="w-full mb-4 border-gray-600" />
</div>
<div class="flex flex-wrap sm:-m-4 -mx-4 -mb-10 -mt-4">
<div class="p-4 md:w-1/3 sm:mb-0 mb-6">
<div class="rounded-lg h-80 overflow-hidden flex flex-col items-center display-inline">
<img :src="selectedDeviceImage" class="h-64 w-64 mb-6 mx-auto" :alt="$t('device.title')" />
<div class="flex max-md:flex-col gap-6 *:md:w-1/3 [&_h2]:text-white [&_h2]:text-xl [&_p]:leading-relaxed">
<div>
<div class="h-80 flex flex-col items-center">
<img :src="selectedDeviceImage" class="size-64 mb-6" :alt="$t('device.title')" />
<Device />
</div>
<h2 class="text-xl font-medium title-font text-white mt-5">{{ $t('device.title') }}</h2>
<p class="text-base leading-relaxed mt-2">
<h2 class="mt-5">{{ $t('device.title') }}</h2>
<p class="mt-2">
{{ $t('device.instructions') }}
</p>
</div>
<div class="p-4 md:w-1/3 sm:mb-0 mb-6">
<div class="rounded-lg h-80 flex flex-col items-center">
<FolderArrowDownIcon class="h-60 w-60 p-5 mt-10 mb-10 mx-auto text-white" :alt="$t('firmware.title')" />
<div>
<div class="h-80 flex flex-col items-center">
<FolderArrowDownIcon class="w-full p-5 my-10 text-white" :alt="$t('firmware.title')" />
<Firmware />
</div>
<h2 class="text-xl font-medium title-font text-white mt-5">{{ $t('firmware.title') }}</h2>
<p class="text-base leading-relaxed mt-2">
<h2 class="mt-5">{{ $t('firmware.title') }}</h2>
<p class="mt-2">
{{ $t('firmware.instructions') }}
</p>
</div>
<div class="p-4 md:w-1/3 sm:mb-0 mb-6">
<div class="rounded-lg h-80 overflow-hidden flex flex-col items-center">
<BoltIcon class="h-60 w-60 p-5 mt-10 mb-10 mx-auto text-white" />
<div>
<div class="h-80 flex flex-col items-center">
<BoltIcon class="w-full p-5 my-10 text-white" />
<Flash />
</div>
<h2 class="text-xl font-medium title-font text-white mt-5">Flash</h2>
<p class="text-base leading-relaxed mt-2">
<h2 class="mt-5">
Flash
</h2>
<p class="mt-2">
{{ $t('flash.instructions') }}
</p>
</div>
</div>
</div>
</transition>
<div class="flex max-sm:flex-col justify-center gap-1 mt-4">
<div class="flex max-sm:flex-col justify-center gap-3 mt-4">
<button type="button" v-if="!serialMonitorStore.isConnected" @click="monitorSerial" class="bottom-button border-meshtastic text-meshtastic">
{{ $t('buttons.serial_monitor') }} <CommandLineIcon class="h-4 w-4 shrink-0" />
{{ $t('buttons.serial_monitor') }} <CommandLineIcon class="size-4 shrink-0" />
</button>
<a href="https://meshtastic.org/docs" v-if="!serialMonitorStore.isConnected" class="bottom-button border-meshtastic text-meshtastic">
{{ $t('buttons.meshtastic_docs') }} <BookOpenIcon class="h-4 w-4 shrink-0" />
{{ $t('buttons.meshtastic_docs') }} <BookOpenIcon class="size-4 shrink-0" />
</a>
<a href="https://github.com/meshtastic/web-flasher" v-if="!serialMonitorStore.isConnected" class="bottom-button border-meshtastic text-meshtastic">
{{ $t('buttons.contribute') }}
Expand Down Expand Up @@ -218,7 +220,7 @@ onMounted(() => {
}
.bottom-button {
@apply flex items-center justify-center gap-1;
@apply font-medium text-xs text-center rounded-lg px-4 py-2 me-2 mb-2 border;
@apply text-xs rounded-lg px-4 py-2 border;
@apply hover:text-black hover:bg-white hover:border-transparent hover:shadow transition duration-300 ease-in-out;
@apply focus:ring-4 focus:outline-none;
}
Expand Down
111 changes: 65 additions & 46 deletions components/Device.vue
Original file line number Diff line number Diff line change
@@ -1,60 +1,68 @@
<template>
<div>
<button id="selectDeviceButton" data-modal-target="device-modal" data-modal-toggle="device-modal" class="display-inline content-center text-black bg-meshtastic hover:bg-gray-200 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center" type="button">
{{ selectedTarget.replace('_', '-') }}
</button>
<button data-tooltip-target="tooltip-auto" class="mx-2 display-inline content-center px-3 py-2 text-xs font-medium text-center hover:bg-gray-200 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg inline-flex items-center text-white hover:text-black"
type="button"
@click="store.autoSelectHardware">
<RocketLaunchIcon class="h-4 w-4" :class="{'animate-bounce': !store.$state.selectedTarget?.hwModel }" />
</button>
<div id="device-modal" tabindex="-1" aria-hidden="true" class="dark hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
<div class="relative p-4 w-full max-h-full" :class="{ 'max-w-4xl': vendorCobrandingTag.length > 0, 'max-w-8xl': vendorCobrandingTag.length == 0 }">
<div class="relative rounded-lg shadow bg-gray-700">
<div class="flex gap-1">
<button id="selectDeviceButton" data-modal-target="device-modal" data-modal-toggle="device-modal" class="flex justify-center items-center text-black bg-meshtastic hover:bg-gray-200 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg text-sm px-5 py-2.5 duration-150" type="button">
{{ selectedTarget.replace('_', '-') }}
</button>
<button data-tooltip-target="tooltip-auto" class="flex items-center justify-center px-3 text-xs hover:bg-gray-200 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg text-white hover:text-black duration-150"
type="button"
@click="store.autoSelectHardware">
<RocketLaunchIcon class="size-4" :class="{'animate-bounce': !store.selectedTarget?.hwModel }" />
</button>
</div>
<div id="device-modal" tabindex="-1" aria-hidden="true" class="hidden fixed inset-0 z-50 size-full">
<div class="p-4 size-full overflow-y-scroll" :class="[vendorCobrandingEnabled ? 'max-w-4xl' : 'max-w-8xl' ]">
<div class="pb-4 rounded-lg shadow bg-gray-700">
<DeviceHeader />
<div class="flex items-center justify-center py-2 flex-wrap">
<button @click="store.setSelectedTag('all')" type="button" class="text-gray-100 border-gray-900 hover:border-gray-400 bg-green-800 hover:bg-green-700 focus:ring focus:ring-gray-200 rounded-md text-xs px-2 py-2 text-center me-1">{{ $t('device.all_devices') }}</button>
<button v-if="vendorCobrandingTag.length === 0" @click="store.setSelectedTag('RAK')" type="button" class="text-gray-100 border-gray-900 hover:border-gray-400 bg-gray-900 hover:bg-gray-800 focus:ring focus:ring-gray-200 rounded-md text-xs px-2 py-2 text-center me-1">RAK</button>
<button v-if="vendorCobrandingTag.length === 0" @click="store.setSelectedTag('B&Q')" type="button" class="text-gray-100 border-gray-900 hover:border-gray-400 bg-gray-900 hover:bg-gray-800 focus:ring focus:ring-gray-200 rounded-md text-xs px-2 py-2 text-center me-1">B&Q</button>
<button v-if="vendorCobrandingTag.length === 0" @click="store.setSelectedTag('LilyGo')" type="button" class="text-gray-100 border-gray-900 hover:border-gray-400 bg-gray-900 hover:bg-gray-800 focus:ring focus:ring-gray-200 rounded-md text-xs px-2 py-2 text-center me-1">LilyGo</button>
<button v-if="vendorCobrandingTag.length === 0" @click="store.setSelectedTag('Seeed')" type="button" class="text-gray-100 border-gray-900 hover:border-gray-400 bg-gray-900 hover:bg-gray-800 focus:ring focus:ring-gray-200 rounded-md text-xs px-2 py-2 text-center me-1">Seeed</button>
<button v-if="vendorCobrandingTag.length === 0" @click="store.setSelectedTag('Heltec')" type="button" class="text-gray-100 border-gray-900 hover:border-gray-400 bg-gray-900 hover:bg-gray-800 focus:ring focus:ring-gray-200 rounded-md text-xs px-2 py-2 text-center me-1">Heltec</button>
<button v-if="vendorCobrandingTag.length === 0" @click="store.setSelectedTag('Elecrow')" type="button" class="text-gray-100 border-gray-900 hover:border-gray-400 bg-gray-900 hover:bg-gray-800 focus:ring focus:ring-gray-200 rounded-md text-xs px-2 py-2 text-center me-1">Elecrow</button>
<br />
<button @click="store.setSelectedTag(arch)" v-for="arch in store.allArchs" type="button" class=" border-gray-900 focus:ring focus:ring-gray-200 hover:border-gray-700 bg-indigo-500 hover:bg-indigo-400 text-gray-100 rounded-md text-xs px-2 py-2 text-center me-1">{{ arch }}</button>
<div class="mt-2 flex flex-wrap items-center justify-center gap-1">
<button @click="store.setSelectedTag('all')" class="filter-tag bg-green-800 hover:bg-green-700">
{{ $t('device.all_devices') }}
</button>
<button v-if="!vendorCobrandingEnabled" v-for="vendor in supportedVendorDeviceTags" @click="store.setSelectedTag(vendor)" :key="vendor" class="filter-tag bg-gray-900 hover:bg-gray-800">
{{ vendor }}
</button>
</div>
<div class="p-4 mb-1 m-2 text-sm rounded-lg bg-gray-800 text-gray-100" role="alert">
<span class="font-medium">
<InformationCircleIcon class="h-4 w-4 inline" />
{{ $t('device.subheading') }} <button type="button" @click="store.autoSelectHardware" class="bg-meshtastic inline-flex py-2 mx-2 px-3 text-sm font-medium rounded-md hover:bg-white text-black"><RocketLaunchIcon class="h-4 w-4 text-black" /> {{ $t('device.auto_detect') }}</button>
</span>
<div class="mt-2 flex flex-wrap items-center justify-center gap-1">
<button @click="store.setSelectedTag(arch)" v-for="arch in store.allArchs" type="button" class="filter-tag bg-indigo-500 hover:bg-indigo-400">
{{ arch }}
</button>
</div>
<div v-if="vendorCobrandingTag.length === 0" class="p-2 m-2 flex flex-wrap items-center justify-center">
<div class="w-full text-center">
<h2>{{ $t('device.supported_devices') }}</h2>
</div>
<div v-for="device in store.sortedDevices.filter(d => isSupporterDevice(d) && d.supportLevel != 3)" class="max-w-sm border hover:border-gray-300 border-gray-600 rounded-lg m-2 cursor-pointer hover:scale-105 shadow hover:shadow-[0_35px_60px_-15px_rgba(200,200,200,.3)]" @click="setSelectedTarget(device)">
<DeviceDetail :device="device" />
</div>
<hr class="w-full border-gray-400 my-4" />
<div v-if="store.sortedDevices.filter(d => !isSupporterDevice(d) || d.supportLevel == 3).length > 0"class="w-full text-center">
<h2 class="text-yellow-400">{{ $t('device.diy_devices') }}</h2>
</div>
<div v-for="device in store.sortedDevices.filter(d => !isSupporterDevice(d) || d.supportLevel == 3)" class="max-w-sm border hover:border-gray-300 border-gray-600 rounded-lg m-2 cursor-pointer hover:scale-105 shadow hover:shadow-2xl" @click="setSelectedTarget(device)">
<DeviceDetail :device="device" />
</div>
<div class="p-4 mx-2 my-4 text-sm rounded-lg bg-gray-800 text-gray-100" role="alert">
<InformationCircleIcon class="size-4 inline" />
{{ $t('device.subheading') }}
<button type="button" @click="store.autoSelectHardware" class="bg-meshtastic flex items-center gap-2 py-2 px-3 mt-2 rounded-md hover:bg-white text-black duration-150">
<RocketLaunchIcon class="size-4" /> {{ $t('device.auto_detect') }}
</button>
</div>
<div v-else class="p-2 m-2 flex flex-wrap items-center justify-center">
<div v-for="device in store.sortedDevices" class="max-w-sm border hover:border-gray-300 border-gray-600 rounded-lg m-2 cursor-pointer hover:scale-105 hover:shadow-2xl" @click="store.setSelectedTarget(device)">
<DeviceDetail :device="device" />
</div>
<div class="flex flex-wrap items-center justify-center gap-2">
<template v-if="!vendorCobrandingEnabled">
<div class="w-full text-center">
<h2>{{ $t('device.supported_devices') }}</h2>
</div>
<div v-for="device in supportedDevices" class="device-card" @click="setSelectedTarget(device)">
<DeviceDetail :device="device" />
</div>
<hr class="w-full border-gray-400 my-2" />
<div v-if="diyDevices.length > 0" class="w-full text-center">
<h2 class="text-yellow-400">{{ $t('device.diy_devices') }}</h2>
</div>
<div v-for="device in diyDevices" class="device-card" @click="setSelectedTarget(device)">
<DeviceDetail :device="device" />
</div>
</template>
<template v-else>
<div v-for="device in store.sortedDevices" @click="store.setSelectedTarget(device)" class="device-card">
<DeviceDetail :device="device" />
</div>
</template>
</div>
</div>
</div>
</div>
</div>
</template>


<script lang="ts" setup>
import type { DeviceHardware } from '~/types/api';
import {
Expand Down Expand Up @@ -86,7 +94,18 @@ const setSelectedTarget = (device: DeviceHardware) => {
store.setSelectedTarget(device);
firmwareStore.clearState();
}
const selectedTarget = computed(() => store.selectedTarget?.hwModel ? store.selectedTarget?.displayName : t('device.select_device'));

const selectedTarget = computed(() => store.$state.selectedTarget?.hwModel ? store.$state.selectedTarget?.displayName : t('device.select_device'));
const vendorCobrandingEnabled = computed(() => vendorCobrandingTag.length > 0);
const supportedDevices = computed(() => store.sortedDevices.filter(d => isSupporterDevice(d) && d.supportLevel != 3))
const diyDevices = computed(() => store.sortedDevices.filter(d => !isSupporterDevice(d) || d.supportLevel == 3))
</script>

</script>
<style scoped>
.filter-tag {
@apply text-gray-100 border-gray-900 hover:border-gray-400 focus:ring focus:ring-gray-200 rounded-md text-xs p-2;
}
.device-card {
@apply border hover:border-gray-300 border-gray-600 rounded-lg cursor-pointer hover:scale-105 hover:shadow-[0_35px_60px_-15px_rgba(200,200,200,.3)];
}
</style>
14 changes: 7 additions & 7 deletions components/DeviceHeader.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<template>
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t order-gray-600">
<div class="flex items-center justify-between p-4 border-b rounded-t">
<h3 class="text-xl font-semibold text-white">
{{ $t('device.header') }}
</h3>
<button type="button"
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm h-8 w-8 ms-auto inline-flex justify-center items-center hover:bg-gray-600 hover:text-white"
class="text-gray-400 rounded-lg text-sm size-8 p-1 flex justify-center items-center shrink-0 hover:bg-gray-600 hover:text-white"
data-modal-toggle="device-modal">
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
</svg>
<XMarkIcon />
<span class="sr-only">{{ $t('actions.close_dialog') }}</span>
</button>
</div>
</template>

<script setup>
import { XMarkIcon } from '@heroicons/vue/24/solid';
</script>
Loading