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 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
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: column;
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 0 10px 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
+ }
@@ -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(`SLIP read error at ${resp.length} bytes: ${err.message}`);
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(`${err.message} at 0x${currentAddr.toString(16)}. Draining buffer and retrying (attempt ${retryCount}/${MAX_RETRIES})...`);
3709
- try {
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