esp32tool 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/apple-touch-icon.png +0 -0
- package/css/style.css +388 -2
- package/dist/esp_loader.js +3 -13
- package/dist/web/index.js +1 -1
- package/icons/icon-128.png +0 -0
- package/icons/icon-144.png +0 -0
- package/icons/icon-152.png +0 -0
- package/icons/icon-192.png +0 -0
- package/icons/icon-384.png +0 -0
- package/icons/icon-512.png +0 -0
- package/icons/icon-72.png +0 -0
- package/icons/icon-96.png +0 -0
- package/index.html +7 -4
- package/js/modules/esptool.js +1 -1
- package/js/nvs-editor.js +732 -0
- package/js/script.js +142 -4
- package/package.json +8 -5
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/esp_loader.ts +3 -16
- package/sw.js +2 -1
package/README.md
CHANGED
|
@@ -135,6 +135,8 @@ December 2025 – Now with full LittleFS, SPIFFS, and FATFS support, plus file a
|
|
|
135
135
|
|
|
136
136
|
January 2026 – Added Android mobile devices support, standalone CLI.
|
|
137
137
|
|
|
138
|
+
February 2026 - Added IMPROV support, NEW Features: Flash Hex Editor and NVS Parser / Editor
|
|
139
|
+
|
|
138
140
|
---
|
|
139
141
|
|
|
140
142
|
© Adafruit, Nabu Casa & Johann Obermeier
|
package/apple-touch-icon.png
CHANGED
|
Binary file
|
package/css/style.css
CHANGED
|
@@ -539,12 +539,15 @@ div.clear {
|
|
|
539
539
|
padding-top: 20px;
|
|
540
540
|
border-top: 2px solid #ccc;
|
|
541
541
|
display: flex;
|
|
542
|
-
flex-direction:
|
|
542
|
+
flex-direction: row;
|
|
543
|
+
flex-wrap: wrap;
|
|
544
|
+
justify-content: center;
|
|
543
545
|
align-items: center;
|
|
546
|
+
gap: 10px;
|
|
544
547
|
}
|
|
545
548
|
|
|
546
549
|
#commands .partition-table button {
|
|
547
|
-
margin: 0
|
|
550
|
+
margin: 0;
|
|
548
551
|
border-width: 2px;
|
|
549
552
|
border-style: solid;
|
|
550
553
|
padding: 2px 30px;
|
|
@@ -565,6 +568,12 @@ div.clear {
|
|
|
565
568
|
#commands .partition-table .progress-bar {
|
|
566
569
|
width: 400px;
|
|
567
570
|
margin-bottom: 10px;
|
|
571
|
+
flex-basis: 100%;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
#commands .partition-table #partitionList {
|
|
575
|
+
flex-basis: 100%;
|
|
576
|
+
width: 100%;
|
|
568
577
|
}
|
|
569
578
|
|
|
570
579
|
.partition-table-display {
|
|
@@ -2732,3 +2741,380 @@ body.hexeditor-active .main {
|
|
|
2732
2741
|
width: 70px;
|
|
2733
2742
|
}
|
|
2734
2743
|
}
|
|
2744
|
+
|
|
2745
|
+
/* ========== NVS Editor Container ========== */
|
|
2746
|
+
.nvseditor-container {
|
|
2747
|
+
position: fixed;
|
|
2748
|
+
top: 0;
|
|
2749
|
+
left: 0;
|
|
2750
|
+
right: 0;
|
|
2751
|
+
bottom: 0;
|
|
2752
|
+
z-index: 1001;
|
|
2753
|
+
background-color: #1c1c1c;
|
|
2754
|
+
display: flex;
|
|
2755
|
+
flex-direction: column;
|
|
2756
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
2757
|
+
color: #ddd;
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
.nvseditor-container.hidden {
|
|
2761
|
+
display: none;
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
/* Hide header and commands when NVS editor is active */
|
|
2765
|
+
body.nvseditor-active .header {
|
|
2766
|
+
display: none !important;
|
|
2767
|
+
}
|
|
2768
|
+
body.nvseditor-active #commands {
|
|
2769
|
+
display: none !important;
|
|
2770
|
+
}
|
|
2771
|
+
body.nvseditor-active #notSupported {
|
|
2772
|
+
display: none !important;
|
|
2773
|
+
}
|
|
2774
|
+
body.nvseditor-active .main {
|
|
2775
|
+
padding-top: 0 !important;
|
|
2776
|
+
overflow: hidden !important;
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
/* NVS Editor Toolbar */
|
|
2780
|
+
.nvseditor-toolbar {
|
|
2781
|
+
display: flex;
|
|
2782
|
+
align-items: center;
|
|
2783
|
+
gap: 10px;
|
|
2784
|
+
padding: 8px 12px;
|
|
2785
|
+
background: #252525;
|
|
2786
|
+
border-bottom: 1px solid #444;
|
|
2787
|
+
flex-shrink: 0;
|
|
2788
|
+
flex-wrap: wrap;
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
.nvseditor-toolbar h3 {
|
|
2792
|
+
margin: 0;
|
|
2793
|
+
font-size: 15px;
|
|
2794
|
+
color: #eee;
|
|
2795
|
+
white-space: nowrap;
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
.nvseditor-toolbar .nvs-info {
|
|
2799
|
+
font-size: 11px;
|
|
2800
|
+
color: #888;
|
|
2801
|
+
white-space: nowrap;
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
.nvseditor-toolbar .spacer {
|
|
2805
|
+
flex: 1;
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
.nvseditor-toolbar button {
|
|
2809
|
+
padding: 4px 12px;
|
|
2810
|
+
border: 1px solid #555;
|
|
2811
|
+
border-radius: 3px;
|
|
2812
|
+
background: #333;
|
|
2813
|
+
color: #ddd;
|
|
2814
|
+
cursor: pointer;
|
|
2815
|
+
font-size: 12px;
|
|
2816
|
+
white-space: nowrap;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
.nvseditor-toolbar button:hover {
|
|
2820
|
+
background: #444;
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
.nvseditor-toolbar button.primary {
|
|
2824
|
+
background: #1565c0;
|
|
2825
|
+
border-color: #1976d2;
|
|
2826
|
+
color: #fff;
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2829
|
+
.nvseditor-toolbar button.primary:hover {
|
|
2830
|
+
background: #1976d2;
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2833
|
+
.nvseditor-toolbar button:disabled {
|
|
2834
|
+
opacity: 0.4;
|
|
2835
|
+
cursor: default;
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
.nvseditor-filter input {
|
|
2839
|
+
padding: 3px 8px;
|
|
2840
|
+
border: 1px solid #555;
|
|
2841
|
+
border-radius: 3px;
|
|
2842
|
+
background: #2a2a2a;
|
|
2843
|
+
color: #ddd;
|
|
2844
|
+
font-size: 12px;
|
|
2845
|
+
width: 200px;
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
.nvseditor-filter input:focus {
|
|
2849
|
+
border-color: #1976d2;
|
|
2850
|
+
outline: none;
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
/* NVS Editor Body */
|
|
2854
|
+
.nvseditor-body {
|
|
2855
|
+
flex: 1;
|
|
2856
|
+
overflow-y: auto;
|
|
2857
|
+
padding: 10px;
|
|
2858
|
+
position: relative;
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
/* Progress overlay */
|
|
2862
|
+
.nvseditor-progress-overlay {
|
|
2863
|
+
position: absolute;
|
|
2864
|
+
top: 0;
|
|
2865
|
+
left: 0;
|
|
2866
|
+
right: 0;
|
|
2867
|
+
bottom: 0;
|
|
2868
|
+
display: flex;
|
|
2869
|
+
flex-direction: column;
|
|
2870
|
+
align-items: center;
|
|
2871
|
+
justify-content: center;
|
|
2872
|
+
background: rgba(28, 28, 28, 0.92);
|
|
2873
|
+
z-index: 10;
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
.nvseditor-progress-overlay.hidden {
|
|
2877
|
+
display: none;
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
.nvseditor-progress-overlay .progress-text {
|
|
2881
|
+
color: #ccc;
|
|
2882
|
+
margin-bottom: 12px;
|
|
2883
|
+
font-size: 14px;
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
.nvseditor-progress-overlay .progress-bar-outer {
|
|
2887
|
+
width: 300px;
|
|
2888
|
+
height: 8px;
|
|
2889
|
+
background: #333;
|
|
2890
|
+
border-radius: 4px;
|
|
2891
|
+
overflow: hidden;
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
.nvseditor-progress-overlay .progress-bar-inner {
|
|
2895
|
+
height: 100%;
|
|
2896
|
+
background: #1976d2;
|
|
2897
|
+
transition: width 0.15s;
|
|
2898
|
+
border-radius: 4px;
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
/* Status bar */
|
|
2902
|
+
.nvseditor-statusbar {
|
|
2903
|
+
display: flex;
|
|
2904
|
+
gap: 16px;
|
|
2905
|
+
padding: 4px 12px;
|
|
2906
|
+
background: #252525;
|
|
2907
|
+
border-top: 1px solid #444;
|
|
2908
|
+
font-size: 11px;
|
|
2909
|
+
color: #888;
|
|
2910
|
+
flex-shrink: 0;
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
/* NVS Page */
|
|
2914
|
+
.nvs-page {
|
|
2915
|
+
margin-bottom: 16px;
|
|
2916
|
+
border: 1px solid #3a3a3a;
|
|
2917
|
+
border-radius: 6px;
|
|
2918
|
+
overflow: hidden;
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2921
|
+
.nvs-page-header {
|
|
2922
|
+
display: flex;
|
|
2923
|
+
align-items: center;
|
|
2924
|
+
gap: 16px;
|
|
2925
|
+
padding: 8px 12px;
|
|
2926
|
+
background: #2a2a2a;
|
|
2927
|
+
font-size: 12px;
|
|
2928
|
+
color: #bbb;
|
|
2929
|
+
border-bottom: 1px solid #3a3a3a;
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
.nvs-page-header.state-active { border-left: 4px solid #43a047; }
|
|
2933
|
+
.nvs-page-header.state-full { border-left: 4px solid #ffa726; }
|
|
2934
|
+
.nvs-page-header.state-freeing { border-left: 4px solid #ef5350; }
|
|
2935
|
+
.nvs-page-header.state-other { border-left: 4px solid #666; }
|
|
2936
|
+
|
|
2937
|
+
.nvs-page-state {
|
|
2938
|
+
font-weight: bold;
|
|
2939
|
+
padding: 2px 8px;
|
|
2940
|
+
border-radius: 3px;
|
|
2941
|
+
font-size: 11px;
|
|
2942
|
+
text-transform: uppercase;
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2945
|
+
.state-active .nvs-page-state { background: #1b5e20; color: #a5d6a7; }
|
|
2946
|
+
.state-full .nvs-page-state { background: #e65100; color: #ffcc80; }
|
|
2947
|
+
.state-freeing .nvs-page-state { background: #b71c1c; color: #ef9a9a; }
|
|
2948
|
+
.state-other .nvs-page-state { background: #424242; color: #999; }
|
|
2949
|
+
|
|
2950
|
+
/* NVS Namespace */
|
|
2951
|
+
.nvs-namespace {
|
|
2952
|
+
margin: 0;
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
.nvs-namespace-header {
|
|
2956
|
+
display: flex;
|
|
2957
|
+
align-items: center;
|
|
2958
|
+
gap: 8px;
|
|
2959
|
+
padding: 6px 12px;
|
|
2960
|
+
background: #262626;
|
|
2961
|
+
border-bottom: 1px solid #333;
|
|
2962
|
+
font-size: 13px;
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2965
|
+
.nvs-ns-icon {
|
|
2966
|
+
font-size: 14px;
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
.nvs-ns-name {
|
|
2970
|
+
font-weight: 600;
|
|
2971
|
+
color: #90caf9;
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
.nvs-ns-count {
|
|
2975
|
+
color: #777;
|
|
2976
|
+
font-size: 11px;
|
|
2977
|
+
}
|
|
2978
|
+
|
|
2979
|
+
/* NVS Table */
|
|
2980
|
+
.nvs-table {
|
|
2981
|
+
width: 100%;
|
|
2982
|
+
border-collapse: collapse;
|
|
2983
|
+
font-size: 12px;
|
|
2984
|
+
}
|
|
2985
|
+
|
|
2986
|
+
.nvs-table thead th {
|
|
2987
|
+
background: #222;
|
|
2988
|
+
color: #999;
|
|
2989
|
+
padding: 5px 8px;
|
|
2990
|
+
text-align: left;
|
|
2991
|
+
font-weight: 600;
|
|
2992
|
+
border-bottom: 1px solid #3a3a3a;
|
|
2993
|
+
position: sticky;
|
|
2994
|
+
top: 0;
|
|
2995
|
+
z-index: 1;
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
.nvs-table tbody tr {
|
|
2999
|
+
border-bottom: 1px solid #2a2a2a;
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
.nvs-table tbody tr:hover {
|
|
3003
|
+
background: #2a2a2a;
|
|
3004
|
+
}
|
|
3005
|
+
|
|
3006
|
+
.nvs-table td {
|
|
3007
|
+
padding: 4px 8px;
|
|
3008
|
+
vertical-align: middle;
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
.nvs-key {
|
|
3012
|
+
font-weight: 500;
|
|
3013
|
+
color: #e0e0e0;
|
|
3014
|
+
max-width: 160px;
|
|
3015
|
+
overflow: hidden;
|
|
3016
|
+
text-overflow: ellipsis;
|
|
3017
|
+
white-space: nowrap;
|
|
3018
|
+
}
|
|
3019
|
+
|
|
3020
|
+
.nvs-type {
|
|
3021
|
+
color: #a5d6a7;
|
|
3022
|
+
white-space: nowrap;
|
|
3023
|
+
width: 80px;
|
|
3024
|
+
}
|
|
3025
|
+
|
|
3026
|
+
.nvs-value {
|
|
3027
|
+
color: #fff;
|
|
3028
|
+
max-width: 400px;
|
|
3029
|
+
overflow: hidden;
|
|
3030
|
+
text-overflow: ellipsis;
|
|
3031
|
+
white-space: nowrap;
|
|
3032
|
+
font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
|
|
3033
|
+
font-size: 11px;
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
.nvs-crc {
|
|
3037
|
+
width: 30px;
|
|
3038
|
+
text-align: center;
|
|
3039
|
+
}
|
|
3040
|
+
|
|
3041
|
+
.nvs-crc.crc-ok {
|
|
3042
|
+
color: #66bb6a;
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
.nvs-crc.crc-bad {
|
|
3046
|
+
color: #ef5350;
|
|
3047
|
+
font-weight: bold;
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
.nvs-offset {
|
|
3051
|
+
font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
|
|
3052
|
+
font-size: 11px;
|
|
3053
|
+
color: #888;
|
|
3054
|
+
white-space: nowrap;
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
.nvs-actions {
|
|
3058
|
+
white-space: nowrap;
|
|
3059
|
+
width: 70px;
|
|
3060
|
+
}
|
|
3061
|
+
|
|
3062
|
+
.nvs-actions button {
|
|
3063
|
+
padding: 2px 6px;
|
|
3064
|
+
margin-right: 2px;
|
|
3065
|
+
border: 1px solid #555;
|
|
3066
|
+
border-radius: 3px;
|
|
3067
|
+
background: #333;
|
|
3068
|
+
color: #ddd;
|
|
3069
|
+
cursor: pointer;
|
|
3070
|
+
font-size: 12px;
|
|
3071
|
+
}
|
|
3072
|
+
|
|
3073
|
+
.nvs-actions button:hover {
|
|
3074
|
+
background: #444;
|
|
3075
|
+
}
|
|
3076
|
+
|
|
3077
|
+
.nvs-btn-delete:hover {
|
|
3078
|
+
background: #c62828 !important;
|
|
3079
|
+
border-color: #e53935 !important;
|
|
3080
|
+
color: #fff !important;
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
.nvs-btn-edit:hover {
|
|
3084
|
+
background: #1565c0 !important;
|
|
3085
|
+
border-color: #1976d2 !important;
|
|
3086
|
+
color: #fff !important;
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
/* Empty state */
|
|
3090
|
+
.nvs-empty {
|
|
3091
|
+
text-align: center;
|
|
3092
|
+
padding: 60px 20px;
|
|
3093
|
+
color: #666;
|
|
3094
|
+
font-size: 16px;
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
/* NVS content area */
|
|
3098
|
+
.nvseditor-content {
|
|
3099
|
+
min-height: 100%;
|
|
3100
|
+
}
|
|
3101
|
+
|
|
3102
|
+
/* ─── Responsive NVS Editor ─── */
|
|
3103
|
+
@media (max-width: 700px) {
|
|
3104
|
+
.nvseditor-toolbar {
|
|
3105
|
+
padding: 6px 8px;
|
|
3106
|
+
gap: 6px;
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
.nvseditor-filter input {
|
|
3110
|
+
width: 140px;
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
.nvs-value {
|
|
3114
|
+
max-width: 150px;
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
.nvs-offset {
|
|
3118
|
+
display: none;
|
|
3119
|
+
}
|
|
3120
|
+
}
|
package/dist/esp_loader.js
CHANGED
|
@@ -3595,7 +3595,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3595
3595
|
}
|
|
3596
3596
|
catch (err) {
|
|
3597
3597
|
if (err instanceof SlipReadError) {
|
|
3598
|
-
this.logger.debug(
|
|
3598
|
+
this.logger.debug(`${err.message} at byte 0x${resp.length.toString(16)}`);
|
|
3599
3599
|
// Send empty SLIP frame to abort the stub's read operation
|
|
3600
3600
|
// The stub expects 4 bytes (ACK), if we send less it will break out
|
|
3601
3601
|
try {
|
|
@@ -3705,18 +3705,8 @@ export class ESPLoader extends EventTarget {
|
|
|
3705
3705
|
// Check if it's a timeout error or SLIP error
|
|
3706
3706
|
if (err instanceof SlipReadError) {
|
|
3707
3707
|
if (retryCount <= MAX_RETRIES) {
|
|
3708
|
-
this.logger.debug(
|
|
3709
|
-
|
|
3710
|
-
await this.drainInputBuffer(200);
|
|
3711
|
-
// Clear application buffer
|
|
3712
|
-
await this.flushSerialBuffers();
|
|
3713
|
-
// Wait before retry to let hardware settle
|
|
3714
|
-
await sleep(SYNC_TIMEOUT);
|
|
3715
|
-
// Continue to retry the same chunk (will send NEW read command)
|
|
3716
|
-
}
|
|
3717
|
-
catch (drainErr) {
|
|
3718
|
-
this.logger.debug(`Buffer drain error: ${drainErr}`);
|
|
3719
|
-
}
|
|
3708
|
+
this.logger.debug(`Cleared buffer and retrying (attempt ${retryCount}/${MAX_RETRIES})...`);
|
|
3709
|
+
// Continue to retry the same chunk (will send NEW read command)
|
|
3720
3710
|
}
|
|
3721
3711
|
else {
|
|
3722
3712
|
// All retries exhausted - attempt recovery by reloading stub
|