mirror of https://github.com/jetkvm/kvm.git
Compare commits
11 Commits
13d348c89e
...
0e1c49c2f6
| Author | SHA1 | Date |
|---|---|---|
|
|
0e1c49c2f6 | |
|
|
c8129405bd | |
|
|
1138ffc210 | |
|
|
0d7cfec3e9 | |
|
|
4b049c4b7c | |
|
|
7955ee9d35 | |
|
|
1ce63664c0 | |
|
|
4b6e796a0e | |
|
|
79098d3546 | |
|
|
50fc88aae1 | |
|
|
204909b49a |
|
|
@ -14,6 +14,7 @@ set -ex
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
sudo apt-get update && \
|
sudo apt-get update && \
|
||||||
sudo apt-get install -y --no-install-recommends \
|
sudo apt-get install -y --no-install-recommends \
|
||||||
|
iputils-ping \
|
||||||
build-essential \
|
build-essential \
|
||||||
device-tree-compiler \
|
device-tree-compiler \
|
||||||
gperf g++-multilib gcc-multilib \
|
gperf g++-multilib gcc-multilib \
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,7 @@ func getDefaultConfig() Config {
|
||||||
return c
|
return c
|
||||||
}(),
|
}(),
|
||||||
DefaultLogLevel: "INFO",
|
DefaultLogLevel: "INFO",
|
||||||
|
VideoQualityFactor: 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
23
display.go
23
display.go
|
|
@ -41,10 +41,16 @@ func switchToMainScreen() {
|
||||||
|
|
||||||
func updateDisplay() {
|
func updateDisplay() {
|
||||||
if networkManager != nil {
|
if networkManager != nil {
|
||||||
nativeInstance.UpdateLabelAndChangeVisibility("home_info_ipv4_addr", networkManager.IPv4String())
|
ipv4 := networkManager.IPv4String()
|
||||||
nativeInstance.UpdateLabelAndChangeVisibility("home_info_ipv6_addr", networkManager.IPv6String())
|
nativeInstance.UISetVar("ip_v4_address", ipv4)
|
||||||
nativeInstance.UpdateLabelIfChanged("home_info_mac_addr", networkManager.MACString())
|
nativeInstance.ChangeVisibility("home_info_ipv4_addr", ipv4 != "")
|
||||||
nativeInstance.UpdateLabelIfChanged("home_info_hostname", networkManager.Hostname())
|
|
||||||
|
ipv6 := networkManager.IPv6String()
|
||||||
|
nativeInstance.UISetVar("ip_v6_address", ipv6)
|
||||||
|
nativeInstance.ChangeVisibility("home_info_ipv6_addr", ipv6 != "")
|
||||||
|
|
||||||
|
nativeInstance.UISetVar("mac_address", networkManager.MACString())
|
||||||
|
nativeInstance.UISetVar("hostname", networkManager.Hostname())
|
||||||
|
|
||||||
// we either show the MAC address (if no IP yet) or the hostname (if either IPv4 or IPv6 are available)
|
// we either show the MAC address (if no IP yet) or the hostname (if either IPv4 or IPv6 are available)
|
||||||
hasIP := networkManager.IPv4Ready() || networkManager.IPv6Ready()
|
hasIP := networkManager.IPv4Ready() || networkManager.IPv6Ready()
|
||||||
|
|
@ -210,7 +216,8 @@ func waitCtrlAndRequestDisplayUpdate(shouldWakeDisplay bool, reason string) {
|
||||||
func updateStaticContents() {
|
func updateStaticContents() {
|
||||||
//contents that never change
|
//contents that never change
|
||||||
if networkManager != nil {
|
if networkManager != nil {
|
||||||
nativeInstance.UpdateLabelIfChanged("home_info_mac_addr", networkManager.MACString())
|
mac := networkManager.MACString()
|
||||||
|
nativeInstance.UISetVar("mac_address", mac)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get cpu info
|
// get cpu info
|
||||||
|
|
@ -236,7 +243,7 @@ func updateStaticContents() {
|
||||||
nativeInstance.UpdateLabelAndChangeVisibility("build_date", version.BuildDate)
|
nativeInstance.UpdateLabelAndChangeVisibility("build_date", version.BuildDate)
|
||||||
nativeInstance.UpdateLabelAndChangeVisibility("golang_version", version.GoVersion)
|
nativeInstance.UpdateLabelAndChangeVisibility("golang_version", version.GoVersion)
|
||||||
|
|
||||||
// nativeInstance.UpdateLabelAndChangeVisibility("boot_screen_device_id", GetDeviceID())
|
nativeInstance.UpdateLabelAndChangeVisibility("device_id", GetDeviceID())
|
||||||
}
|
}
|
||||||
|
|
||||||
// setDisplayBrightness sets /sys/class/backlight/backlight/brightness to alter
|
// setDisplayBrightness sets /sys/class/backlight/backlight/brightness to alter
|
||||||
|
|
@ -312,11 +319,11 @@ func wakeDisplay(force bool, reason string) {
|
||||||
displayLogger.Warn().Err(err).Msg("failed to wake display")
|
displayLogger.Warn().Err(err).Msg("failed to wake display")
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.DisplayDimAfterSec != 0 {
|
if config.DisplayDimAfterSec != 0 && dimTicker != nil {
|
||||||
dimTicker.Reset(time.Duration(config.DisplayDimAfterSec) * time.Second)
|
dimTicker.Reset(time.Duration(config.DisplayDimAfterSec) * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.DisplayOffAfterSec != 0 {
|
if config.DisplayOffAfterSec != 0 && offTicker != nil {
|
||||||
offTicker.Reset(time.Duration(config.DisplayOffAfterSec) * time.Second)
|
offTicker.Reset(time.Duration(config.DisplayOffAfterSec) * time.Second)
|
||||||
}
|
}
|
||||||
backlightState = 0
|
backlightState = 0
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,7 @@ void jetkvm_video_stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int jetkvm_video_set_quality_factor(float quality_factor) {
|
int jetkvm_video_set_quality_factor(float quality_factor) {
|
||||||
if (quality_factor < 0 || quality_factor > 1) {
|
if (quality_factor <= 0 || quality_factor > 1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
video_set_quality_factor(quality_factor);
|
video_set_quality_factor(quality_factor);
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,7 @@ int video_init(float factor)
|
||||||
{
|
{
|
||||||
detect_sleep_mode();
|
detect_sleep_mode();
|
||||||
|
|
||||||
if (factor < 0 || factor > 1) {
|
if (factor <= 0 || factor > 1) {
|
||||||
factor = 1.0f;
|
factor = 1.0f;
|
||||||
}
|
}
|
||||||
quality_factor = factor;
|
quality_factor = factor;
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,10 @@ func uiGetLVGLVersion() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func uiTick() {
|
||||||
|
panicPlatformNotSupported()
|
||||||
|
}
|
||||||
|
|
||||||
func videoGetStreamQualityFactor() (float64, error) {
|
func videoGetStreamQualityFactor() (float64, error) {
|
||||||
panicPlatformNotSupported()
|
panicPlatformNotSupported()
|
||||||
return 0, nil
|
return 0, nil
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ func (n *Native) UpdateLabelIfChanged(objName string, newText string) {
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
l.Msg("label changed")
|
l.Msg("label changed")
|
||||||
|
uiTick()
|
||||||
} else {
|
} else {
|
||||||
l.Msg("label not changed")
|
l.Msg("label not changed")
|
||||||
}
|
}
|
||||||
|
|
@ -130,6 +131,8 @@ func (n *Native) ChangeVisibility(objName string, show bool) {
|
||||||
_, _ = n.UIObjHide(objName)
|
_, _ = n.UIObjHide(objName)
|
||||||
_, _ = n.UIObjHide(containerName)
|
_, _ = n.UIObjHide(containerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uiTick()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SwitchToScreenIf switches to the screen if the screen name is different from the current screen and the screen name is in the shouldSwitch list
|
// SwitchToScreenIf switches to the screen if the screen name is different from the current screen and the screen name is in the shouldSwitch list
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -56,6 +56,10 @@ void action_switch_to_reboot(lv_event_t *e) {
|
||||||
loadScreen(SCREEN_ID_REBOOT_SCREEN);
|
loadScreen(SCREEN_ID_REBOOT_SCREEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void action_switch_to_network(lv_event_t *e) {
|
||||||
|
loadScreen(SCREEN_ID_MENU_NETWORK_SCREEN);
|
||||||
|
}
|
||||||
|
|
||||||
void action_menu_screen_gesture(lv_event_t * e) {
|
void action_menu_screen_gesture(lv_event_t * e) {
|
||||||
handle_gesture_main_screen_switch(e, LV_DIR_RIGHT);
|
handle_gesture_main_screen_switch(e, LV_DIR_RIGHT);
|
||||||
}
|
}
|
||||||
|
|
@ -76,6 +80,11 @@ void action_about_screen_gesture(lv_event_t * e) {
|
||||||
handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN);
|
handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void action_status_screen_gesture(lv_event_t *e) {
|
||||||
|
handle_gesture_screen_switch(e, LV_DIR_RIGHT, SCREEN_ID_MENU_SCREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// user_data doesn't seem to be working, so we use a global variable here
|
// user_data doesn't seem to be working, so we use a global variable here
|
||||||
static uint32_t t_reset_config;
|
static uint32_t t_reset_config;
|
||||||
static uint32_t t_reboot;
|
static uint32_t t_reboot;
|
||||||
|
|
@ -168,9 +177,9 @@ void action_dhcpc(lv_event_t * e) {
|
||||||
.lock = &b_dhcpc_lock,
|
.lock = &b_dhcpc_lock,
|
||||||
.hold_time_seconds = DHCPC_HOLD_TIME,
|
.hold_time_seconds = DHCPC_HOLD_TIME,
|
||||||
.rpc_method = "toggleDHCPClient",
|
.rpc_method = "toggleDHCPClient",
|
||||||
.button_obj = NULL, // No button/spinner for reboot
|
.button_obj = NULL, // No button/spinner for dhcp client change
|
||||||
.spinner_obj = NULL,
|
.spinner_obj = NULL,
|
||||||
.label_obj = objects.dhcpc_label,
|
.label_obj = objects.dhcp_client_label,
|
||||||
.default_text = "Press and hold for\n5 seconds"
|
.default_text = "Press and hold for\n5 seconds"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ extern void action_reboot(lv_event_t * e);
|
||||||
extern void action_switch_to_reboot(lv_event_t * e);
|
extern void action_switch_to_reboot(lv_event_t * e);
|
||||||
extern void action_dhcpc(lv_event_t * e);
|
extern void action_dhcpc(lv_event_t * e);
|
||||||
extern void action_switch_to_dhcpc(lv_event_t * e);
|
extern void action_switch_to_dhcpc(lv_event_t * e);
|
||||||
|
extern void action_status_screen_gesture(lv_event_t * e);
|
||||||
|
extern void action_switch_to_network(lv_event_t * e);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern const lv_font_t ui_font_font_bold30;
|
extern const lv_font_t ui_font_font_bold30;
|
||||||
extern const lv_font_t ui_font_font_bold24;
|
|
||||||
extern const lv_font_t ui_font_font_book16;
|
extern const lv_font_t ui_font_font_book16;
|
||||||
extern const lv_font_t ui_font_font_book18;
|
extern const lv_font_t ui_font_font_book18;
|
||||||
extern const lv_font_t ui_font_font_book20;
|
extern const lv_font_t ui_font_font_book20;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -26,7 +26,7 @@ typedef struct _objects_t {
|
||||||
lv_obj_t *no_network_header_logo;
|
lv_obj_t *no_network_header_logo;
|
||||||
lv_obj_t *no_network_content_container;
|
lv_obj_t *no_network_content_container;
|
||||||
lv_obj_t *no_network_title;
|
lv_obj_t *no_network_title;
|
||||||
lv_obj_t *home_info_ipv6_addr_1;
|
lv_obj_t *no_network_connect_cable;
|
||||||
lv_obj_t *home_header_container;
|
lv_obj_t *home_header_container;
|
||||||
lv_obj_t *home_header_logo;
|
lv_obj_t *home_header_logo;
|
||||||
lv_obj_t *cloud_status_icon;
|
lv_obj_t *cloud_status_icon;
|
||||||
|
|
@ -51,15 +51,15 @@ typedef struct _objects_t {
|
||||||
lv_obj_t *menu_btn_access;
|
lv_obj_t *menu_btn_access;
|
||||||
lv_obj_t *menu_btn_advanced;
|
lv_obj_t *menu_btn_advanced;
|
||||||
lv_obj_t *menu_btn_about;
|
lv_obj_t *menu_btn_about;
|
||||||
lv_obj_t *menu_header_container_1;
|
lv_obj_t *menu_advanced_header_container;
|
||||||
lv_obj_t *menu_items_container_1;
|
lv_obj_t *menu_advanced_items_container;
|
||||||
lv_obj_t *menu_btn_advanced_developer_mode;
|
lv_obj_t *menu_btn_advanced_developer_mode;
|
||||||
lv_obj_t *menu_btn_advanced_usb_emulation;
|
lv_obj_t *menu_btn_advanced_usb_emulation;
|
||||||
lv_obj_t *menu_btn_advanced_reboot;
|
lv_obj_t *menu_btn_advanced_reboot;
|
||||||
lv_obj_t *menu_btn_dhcp_client;
|
lv_obj_t *menu_btn_dhcp_client;
|
||||||
lv_obj_t *menu_btn_advanced_reset_config;
|
lv_obj_t *menu_btn_advanced_reset_config;
|
||||||
lv_obj_t *menu_header_container_2;
|
lv_obj_t *menu_network_header_container;
|
||||||
lv_obj_t *menu_items_container_2;
|
lv_obj_t *menu_network_items_container;
|
||||||
lv_obj_t *menu_btn_network_ipv4;
|
lv_obj_t *menu_btn_network_ipv4;
|
||||||
lv_obj_t *menu_btn_network_ipv6;
|
lv_obj_t *menu_btn_network_ipv6;
|
||||||
lv_obj_t *menu_btn_network_lldp;
|
lv_obj_t *menu_btn_network_lldp;
|
||||||
|
|
@ -85,32 +85,39 @@ typedef struct _objects_t {
|
||||||
lv_obj_t *status_items_container;
|
lv_obj_t *status_items_container;
|
||||||
lv_obj_t *device_id_container;
|
lv_obj_t *device_id_container;
|
||||||
lv_obj_t *device_id;
|
lv_obj_t *device_id;
|
||||||
|
lv_obj_t *device_mac_address_container;
|
||||||
|
lv_obj_t *device_mac_address;
|
||||||
lv_obj_t *cloud_account_id_container;
|
lv_obj_t *cloud_account_id_container;
|
||||||
lv_obj_t *app_version_1;
|
lv_obj_t *cloud_account_id;
|
||||||
lv_obj_t *cloud_domain_container;
|
lv_obj_t *cloud_domain_container;
|
||||||
lv_obj_t *cloud_domain;
|
lv_obj_t *cloud_domain;
|
||||||
lv_obj_t *reset_config_header;
|
lv_obj_t *reset_config_header;
|
||||||
lv_obj_t *reset_config_container;
|
lv_obj_t *reset_config_container;
|
||||||
lv_obj_t *reset_config_label_container;
|
lv_obj_t *reset_config_label_container;
|
||||||
lv_obj_t *reset_config_label;
|
lv_obj_t *reset_config_label;
|
||||||
|
lv_obj_t *reset_config_spinner_container;
|
||||||
lv_obj_t *reset_config_spinner;
|
lv_obj_t *reset_config_spinner;
|
||||||
|
lv_obj_t *reset_config_button_container;
|
||||||
lv_obj_t *reset_config_button;
|
lv_obj_t *reset_config_button;
|
||||||
lv_obj_t *obj0;
|
lv_obj_t *reset_config_button_label;
|
||||||
lv_obj_t *reboot_header;
|
lv_obj_t *reboot_header;
|
||||||
lv_obj_t *reboot_container;
|
lv_obj_t *reboot_container;
|
||||||
lv_obj_t *reboot_label_container;
|
lv_obj_t *reboot_label_container;
|
||||||
lv_obj_t *reboot_label;
|
lv_obj_t *reboot_label;
|
||||||
lv_obj_t *reboot_config_button;
|
lv_obj_t *reboot_device_button_container;
|
||||||
lv_obj_t *obj1;
|
lv_obj_t *reboot_device_button;
|
||||||
|
lv_obj_t *reboot_device_button_label;
|
||||||
|
lv_obj_t *reboot_in_progress_container;
|
||||||
lv_obj_t *reboot_in_progress_logo;
|
lv_obj_t *reboot_in_progress_logo;
|
||||||
lv_obj_t *reboot_in_progress_label;
|
lv_obj_t *reboot_in_progress_label;
|
||||||
lv_obj_t *dhcp_client_header;
|
lv_obj_t *dhcp_client_header;
|
||||||
lv_obj_t *dhcp_client_container;
|
lv_obj_t *dhcp_client_container;
|
||||||
lv_obj_t *dhcp_client_label_container;
|
lv_obj_t *dhcp_client_label_container;
|
||||||
lv_obj_t *dhcpc_label;
|
lv_obj_t *dhcp_client_label;
|
||||||
|
lv_obj_t *dhcp_client_spinner_container;
|
||||||
lv_obj_t *dhcp_client_spinner;
|
lv_obj_t *dhcp_client_spinner;
|
||||||
lv_obj_t *dhcp_client_button;
|
lv_obj_t *dhcp_client_change_button_container;
|
||||||
lv_obj_t *obj2;
|
lv_obj_t *dhcp_client_change_button;
|
||||||
lv_obj_t *dhcp_client_change_label;
|
lv_obj_t *dhcp_client_change_label;
|
||||||
} objects_t;
|
} objects_t;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -170,11 +170,9 @@ void remove_style_flow_row_start_center(lv_obj_t *obj) {
|
||||||
void init_style_flex_column_start_MAIN_DEFAULT(lv_style_t *style) {
|
void init_style_flex_column_start_MAIN_DEFAULT(lv_style_t *style) {
|
||||||
init_style_flow_row_space_between_MAIN_DEFAULT(style);
|
init_style_flow_row_space_between_MAIN_DEFAULT(style);
|
||||||
|
|
||||||
lv_style_set_layout(style, LV_LAYOUT_FLEX);
|
|
||||||
lv_style_set_flex_flow(style, LV_FLEX_FLOW_COLUMN);
|
|
||||||
lv_style_set_flex_track_place(style, LV_FLEX_ALIGN_START);
|
|
||||||
lv_style_set_flex_cross_place(style, LV_FLEX_ALIGN_START);
|
lv_style_set_flex_cross_place(style, LV_FLEX_ALIGN_START);
|
||||||
lv_style_set_flex_main_place(style, LV_FLEX_ALIGN_SPACE_EVENLY);
|
lv_style_set_flex_main_place(style, LV_FLEX_ALIGN_SPACE_EVENLY);
|
||||||
|
lv_style_set_flex_flow(style, LV_FLEX_FLOW_COLUMN);
|
||||||
};
|
};
|
||||||
|
|
||||||
lv_style_t *get_style_flex_column_start_MAIN_DEFAULT() {
|
lv_style_t *get_style_flex_column_start_MAIN_DEFAULT() {
|
||||||
|
|
@ -294,37 +292,6 @@ void remove_style_label_font16(lv_obj_t *obj) {
|
||||||
lv_obj_remove_style(obj, get_style_label_font16_MAIN_DEFAULT(), LV_PART_MAIN | LV_STATE_DEFAULT);
|
lv_obj_remove_style(obj, get_style_label_font16_MAIN_DEFAULT(), LV_PART_MAIN | LV_STATE_DEFAULT);
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
// Style: LabelFontBold24
|
|
||||||
//
|
|
||||||
|
|
||||||
void init_style_label_font_bold24_MAIN_DEFAULT(lv_style_t *style) {
|
|
||||||
init_style_label_font16_MAIN_DEFAULT(style);
|
|
||||||
|
|
||||||
lv_style_set_text_font(style, &ui_font_font_bold24);
|
|
||||||
lv_style_set_length(style, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
lv_style_t *get_style_label_font_bold24_MAIN_DEFAULT() {
|
|
||||||
static lv_style_t *style;
|
|
||||||
if (!style) {
|
|
||||||
style = lv_malloc(sizeof(lv_style_t));
|
|
||||||
lv_style_init(style);
|
|
||||||
init_style_label_font_bold24_MAIN_DEFAULT(style);
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
};
|
|
||||||
|
|
||||||
void add_style_label_font_bold24(lv_obj_t *obj) {
|
|
||||||
(void)obj;
|
|
||||||
lv_obj_add_style(obj, get_style_label_font_bold24_MAIN_DEFAULT(), LV_PART_MAIN | LV_STATE_DEFAULT);
|
|
||||||
};
|
|
||||||
|
|
||||||
void remove_style_label_font_bold24(lv_obj_t *obj) {
|
|
||||||
(void)obj;
|
|
||||||
lv_obj_remove_style(obj, get_style_label_font_bold24_MAIN_DEFAULT(), LV_PART_MAIN | LV_STATE_DEFAULT);
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Style: LabelFontBold30
|
// Style: LabelFontBold30
|
||||||
//
|
//
|
||||||
|
|
@ -558,7 +525,6 @@ void add_style(lv_obj_t *obj, int32_t styleIndex) {
|
||||||
add_style_flex_screen,
|
add_style_flex_screen,
|
||||||
add_style_flex_screen_menu,
|
add_style_flex_screen_menu,
|
||||||
add_style_label_font16,
|
add_style_label_font16,
|
||||||
add_style_label_font_bold24,
|
|
||||||
add_style_label_font_bold30,
|
add_style_label_font_bold30,
|
||||||
add_style_header_link,
|
add_style_header_link,
|
||||||
add_style_menu_button,
|
add_style_menu_button,
|
||||||
|
|
@ -582,7 +548,6 @@ void remove_style(lv_obj_t *obj, int32_t styleIndex) {
|
||||||
remove_style_flex_screen,
|
remove_style_flex_screen,
|
||||||
remove_style_flex_screen_menu,
|
remove_style_flex_screen_menu,
|
||||||
remove_style_label_font16,
|
remove_style_label_font16,
|
||||||
remove_style_label_font_bold24,
|
|
||||||
remove_style_label_font_bold30,
|
remove_style_label_font_bold30,
|
||||||
remove_style_header_link,
|
remove_style_header_link,
|
||||||
remove_style_menu_button,
|
remove_style_menu_button,
|
||||||
|
|
|
||||||
|
|
@ -52,11 +52,6 @@ lv_style_t *get_style_label_font16_MAIN_DEFAULT();
|
||||||
void add_style_label_font16(lv_obj_t *obj);
|
void add_style_label_font16(lv_obj_t *obj);
|
||||||
void remove_style_label_font16(lv_obj_t *obj);
|
void remove_style_label_font16(lv_obj_t *obj);
|
||||||
|
|
||||||
// Style: LabelFontBold24
|
|
||||||
lv_style_t *get_style_label_font_bold24_MAIN_DEFAULT();
|
|
||||||
void add_style_label_font_bold24(lv_obj_t *obj);
|
|
||||||
void remove_style_label_font_bold24(lv_obj_t *obj);
|
|
||||||
|
|
||||||
// Style: LabelFontBold30
|
// Style: LabelFontBold30
|
||||||
lv_style_t *get_style_label_font_bold30_MAIN_DEFAULT();
|
lv_style_t *get_style_label_font_bold30_MAIN_DEFAULT();
|
||||||
void add_style_label_font_bold30(lv_obj_t *obj);
|
void add_style_label_font_bold30(lv_obj_t *obj);
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ void ui_call_rpc_handler(const char *method, const char *params);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(EEZ_FOR_LVGL)
|
#if defined(EEZ_FOR_LVGL)
|
||||||
#include <eez/flow/lvgl_api.h>
|
#include <eez/flow/lvgl_api.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,79 @@ char app_version[100] = { 0 };
|
||||||
char system_version[100] = { 0 };
|
char system_version[100] = { 0 };
|
||||||
char lvgl_version[32] = { 0 };
|
char lvgl_version[32] = { 0 };
|
||||||
char main_screen[32] = "home_screen";
|
char main_screen[32] = "home_screen";
|
||||||
|
char mac_address[18] = { 0 };
|
||||||
|
char ip_v4_address[22] = { 0 };
|
||||||
|
char ip_v6_address[46] = { 0 };
|
||||||
|
char hostname[262] = { 0 };
|
||||||
|
|
||||||
|
const char *get_var_ip_v4_address() {
|
||||||
|
return ip_v4_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_var_ip_v4_address(const char *value) {
|
||||||
|
strncpy(ip_v4_address, value, sizeof(ip_v4_address) / sizeof(char));
|
||||||
|
ip_v4_address[sizeof(ip_v4_address) / sizeof(char) - 1] = 0;
|
||||||
|
|
||||||
|
tick_screen_home_screen();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *get_var_ip_v6_address() {
|
||||||
|
return ip_v6_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_var_ip_v6_address(const char *value) {
|
||||||
|
strncpy(ip_v6_address, value, sizeof(ip_v6_address) / sizeof(char));
|
||||||
|
ip_v6_address[sizeof(ip_v6_address) / sizeof(char) - 1] = 0;
|
||||||
|
|
||||||
|
tick_screen_home_screen();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *get_var_mac_address() {
|
||||||
|
return mac_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_var_mac_address(const char *value) {
|
||||||
|
strncpy(mac_address, value, sizeof(mac_address) / sizeof(char));
|
||||||
|
mac_address[sizeof(mac_address) / sizeof(char) - 1] = 0;
|
||||||
|
|
||||||
|
tick_screen_home_screen();
|
||||||
|
tick_screen_status_screen();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *get_var_hostname() {
|
||||||
|
return hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_var_hostname(const char *value) {
|
||||||
|
strncpy(hostname, value, sizeof(hostname) / sizeof(char));
|
||||||
|
hostname[sizeof(hostname) / sizeof(char) - 1] = 0;
|
||||||
|
|
||||||
|
tick_screen_home_screen();
|
||||||
|
}
|
||||||
|
|
||||||
const char *get_var_app_version() {
|
const char *get_var_app_version() {
|
||||||
return app_version;
|
return app_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_var_app_version(const char *value) {
|
||||||
|
strncpy(app_version, value, sizeof(app_version) / sizeof(char));
|
||||||
|
app_version[sizeof(app_version) / sizeof(char) - 1] = 0;
|
||||||
|
|
||||||
|
tick_screen_boot_screen();
|
||||||
|
tick_screen_about_screen();
|
||||||
|
}
|
||||||
|
|
||||||
const char *get_var_system_version() {
|
const char *get_var_system_version() {
|
||||||
return system_version;
|
return system_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_var_system_version(const char *value) {
|
||||||
|
strncpy(system_version, value, sizeof(system_version) / sizeof(char));
|
||||||
|
system_version[sizeof(system_version) / sizeof(char) - 1] = 0;
|
||||||
|
|
||||||
|
tick_screen_about_screen();
|
||||||
|
}
|
||||||
|
|
||||||
const char *get_var_lvgl_version() {
|
const char *get_var_lvgl_version() {
|
||||||
if (lvgl_version[0] == '\0') {
|
if (lvgl_version[0] == '\0') {
|
||||||
char buf[32];
|
char buf[32];
|
||||||
|
|
@ -28,23 +92,17 @@ const char *get_var_lvgl_version() {
|
||||||
return lvgl_version;
|
return lvgl_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_var_app_version(const char *value) {
|
void set_var_lvgl_version(const char *value) {
|
||||||
strncpy(app_version, value, sizeof(app_version) / sizeof(char));
|
// intentional NOP since this is actually generated
|
||||||
app_version[sizeof(app_version) / sizeof(char) - 1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_var_system_version(const char *value) {
|
tick_screen_about_screen();
|
||||||
strncpy(system_version, value, sizeof(system_version) / sizeof(char));
|
|
||||||
system_version[sizeof(system_version) / sizeof(char) - 1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_var_lvgl_version(const char *value) {}
|
|
||||||
|
|
||||||
void set_var_main_screen(const char *value) {
|
|
||||||
strncpy(main_screen, value, sizeof(main_screen) / sizeof(char));
|
|
||||||
main_screen[sizeof(main_screen) / sizeof(char) - 1] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *get_var_main_screen() {
|
const char *get_var_main_screen() {
|
||||||
return main_screen;
|
return main_screen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_var_main_screen(const char *value) {
|
||||||
|
strncpy(main_screen, value, sizeof(main_screen) / sizeof(char));
|
||||||
|
main_screen[sizeof(main_screen) / sizeof(char) - 1] = 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,11 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
void tick_screen_home_screen();
|
||||||
|
void tick_screen_status_screen();
|
||||||
|
void tick_screen_boot_screen();
|
||||||
|
void tick_screen_about_screen();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -18,7 +23,11 @@ enum FlowGlobalVariables {
|
||||||
FLOW_GLOBAL_VARIABLE_APP_VERSION = 0,
|
FLOW_GLOBAL_VARIABLE_APP_VERSION = 0,
|
||||||
FLOW_GLOBAL_VARIABLE_SYSTEM_VERSION = 1,
|
FLOW_GLOBAL_VARIABLE_SYSTEM_VERSION = 1,
|
||||||
FLOW_GLOBAL_VARIABLE_LVGL_VERSION = 2,
|
FLOW_GLOBAL_VARIABLE_LVGL_VERSION = 2,
|
||||||
FLOW_GLOBAL_VARIABLE_MAIN_SCREEN = 3
|
FLOW_GLOBAL_VARIABLE_MAIN_SCREEN = 3,
|
||||||
|
FLOW_GLOBAL_VARIABLE_MAC_ADDRESS = 4,
|
||||||
|
FLOW_GLOBAL_VARIABLE_IP_V6_ADDRESS = 5,
|
||||||
|
FLOW_GLOBAL_VARIABLE_IP_V4_ADDRESS = 6,
|
||||||
|
FLOW_GLOBAL_VARIABLE_HOSTNAME = 7
|
||||||
};
|
};
|
||||||
|
|
||||||
// Native global variables
|
// Native global variables
|
||||||
|
|
@ -31,6 +40,14 @@ extern const char *get_var_lvgl_version();
|
||||||
extern void set_var_lvgl_version(const char *value);
|
extern void set_var_lvgl_version(const char *value);
|
||||||
extern const char *get_var_main_screen();
|
extern const char *get_var_main_screen();
|
||||||
extern void set_var_main_screen(const char *value);
|
extern void set_var_main_screen(const char *value);
|
||||||
|
extern const char *get_var_mac_address();
|
||||||
|
extern void set_var_mac_address(const char *value);
|
||||||
|
extern const char *get_var_ip_v6_address();
|
||||||
|
extern void set_var_ip_v6_address(const char *value);
|
||||||
|
extern const char *get_var_ip_v4_address();
|
||||||
|
extern void set_var_ip_v4_address(const char *value);
|
||||||
|
extern const char *get_var_hostname();
|
||||||
|
extern void set_var_hostname(const char *value);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ func NewNative(opts NativeOptions) *Native {
|
||||||
sleepModeSupported := isSleepModeSupported()
|
sleepModeSupported := isSleepModeSupported()
|
||||||
|
|
||||||
defaultQualityFactor := opts.DefaultQualityFactor
|
defaultQualityFactor := opts.DefaultQualityFactor
|
||||||
if defaultQualityFactor < 0 || defaultQualityFactor > 1 {
|
if defaultQualityFactor <= 0 || defaultQualityFactor > 1 {
|
||||||
defaultQualityFactor = 1.0
|
defaultQualityFactor = 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -177,10 +177,8 @@ func rpcReboot(force bool) error {
|
||||||
return hwReboot(force, nil, 0)
|
return hwReboot(force, nil, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var streamFactor = 1.0
|
|
||||||
|
|
||||||
func rpcGetStreamQualityFactor() (float64, error) {
|
func rpcGetStreamQualityFactor() (float64, error) {
|
||||||
return streamFactor, nil
|
return config.VideoQualityFactor, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcSetStreamQualityFactor(factor float64) error {
|
func rpcSetStreamQualityFactor(factor float64) error {
|
||||||
|
|
@ -190,7 +188,10 @@ func rpcSetStreamQualityFactor(factor float64) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
streamFactor = factor
|
config.VideoQualityFactor = factor
|
||||||
|
if err := SaveConfig(); err != nil {
|
||||||
|
return fmt.Errorf("failed to save config: %w", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
27
main.go
27
main.go
|
|
@ -14,6 +14,7 @@ import (
|
||||||
var appCtx context.Context
|
var appCtx context.Context
|
||||||
|
|
||||||
func Main() {
|
func Main() {
|
||||||
|
logger.Log().Msg("JetKVM Starting Up")
|
||||||
LoadConfig()
|
LoadConfig()
|
||||||
|
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
|
|
@ -79,16 +80,16 @@ func Main() {
|
||||||
startVideoSleepModeTicker()
|
startVideoSleepModeTicker()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
// wait for 15 minutes before starting auto-update checks
|
||||||
|
// this is to avoid interfering with initial setup processes
|
||||||
|
// and to ensure the system is stable before checking for updates
|
||||||
time.Sleep(15 * time.Minute)
|
time.Sleep(15 * time.Minute)
|
||||||
for {
|
|
||||||
logger.Debug().Bool("auto_update_enabled", config.AutoUpdateEnabled).Msg("UPDATING")
|
|
||||||
if !config.AutoUpdateEnabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isTimeSyncNeeded() || !timeSync.IsSyncSuccess() {
|
for {
|
||||||
logger.Debug().Msg("system time is not synced, will retry in 30 seconds")
|
logger.Info().Bool("auto_update_enabled", config.AutoUpdateEnabled).Msg("auto-update check")
|
||||||
time.Sleep(30 * time.Second)
|
if !config.AutoUpdateEnabled {
|
||||||
|
logger.Debug().Msg("auto-update disabled")
|
||||||
|
time.Sleep(5 * time.Minute) // we'll check if auto-updates are enabled in five minutes
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,6 +99,12 @@ func Main() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isTimeSyncNeeded() || !timeSync.IsSyncSuccess() {
|
||||||
|
logger.Debug().Msg("system time is not synced, will retry in 30 seconds")
|
||||||
|
time.Sleep(30 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
includePreRelease := config.IncludePreRelease
|
includePreRelease := config.IncludePreRelease
|
||||||
err = TryUpdate(context.Background(), GetDeviceID(), includePreRelease)
|
err = TryUpdate(context.Background(), GetDeviceID(), includePreRelease)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -107,6 +114,7 @@ func Main() {
|
||||||
time.Sleep(1 * time.Hour)
|
time.Sleep(1 * time.Hour)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
//go RunFuseServer()
|
//go RunFuseServer()
|
||||||
go RunWebServer()
|
go RunWebServer()
|
||||||
|
|
||||||
|
|
@ -123,7 +131,8 @@ func Main() {
|
||||||
sigs := make(chan os.Signal, 1)
|
sigs := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||||
<-sigs
|
<-sigs
|
||||||
logger.Info().Msg("JetKVM Shutting Down")
|
|
||||||
|
logger.Log().Msg("JetKVM Shutting Down")
|
||||||
//if fuseServer != nil {
|
//if fuseServer != nil {
|
||||||
// err := setMassStorageImage(" ")
|
// err := setMassStorageImage(" ")
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
|
|
|
||||||
40
ota.go
40
ota.go
|
|
@ -176,7 +176,7 @@ func downloadFile(ctx context.Context, path string, url string, downloadProgress
|
||||||
if nr > 0 {
|
if nr > 0 {
|
||||||
nw, ew := file.Write(buf[0:nr])
|
nw, ew := file.Write(buf[0:nr])
|
||||||
if nw < nr {
|
if nw < nr {
|
||||||
return fmt.Errorf("short write: %d < %d", nw, nr)
|
return fmt.Errorf("short file write: %d < %d", nw, nr)
|
||||||
}
|
}
|
||||||
written += int64(nw)
|
written += int64(nw)
|
||||||
if ew != nil {
|
if ew != nil {
|
||||||
|
|
@ -240,7 +240,7 @@ func verifyFile(path string, expectedHash string, verifyProgress *float32, scope
|
||||||
if nr > 0 {
|
if nr > 0 {
|
||||||
nw, ew := hash.Write(buf[0:nr])
|
nw, ew := hash.Write(buf[0:nr])
|
||||||
if nw < nr {
|
if nw < nr {
|
||||||
return fmt.Errorf("short write: %d < %d", nw, nr)
|
return fmt.Errorf("short hash write: %d < %d", nw, nr)
|
||||||
}
|
}
|
||||||
verified += int64(nw)
|
verified += int64(nw)
|
||||||
if ew != nil {
|
if ew != nil {
|
||||||
|
|
@ -260,11 +260,16 @@ func verifyFile(path string, expectedHash string, verifyProgress *float32, scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hashSum := hash.Sum(nil)
|
// close the file so we can rename below
|
||||||
scopedLogger.Info().Str("path", path).Str("hash", hex.EncodeToString(hashSum)).Msg("SHA256 hash of")
|
if err := fileToHash.Close(); err != nil {
|
||||||
|
return fmt.Errorf("error closing file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if hex.EncodeToString(hashSum) != expectedHash {
|
hashSum := hex.EncodeToString(hash.Sum(nil))
|
||||||
return fmt.Errorf("hash mismatch: %x != %s", hashSum, expectedHash)
|
scopedLogger.Info().Str("path", path).Str("hash", hashSum).Msg("SHA256 hash of")
|
||||||
|
|
||||||
|
if hashSum != expectedHash {
|
||||||
|
return fmt.Errorf("hash mismatch: %s != %s", hashSum, expectedHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.Rename(unverifiedPath, path); err != nil {
|
if err := os.Rename(unverifiedPath, path); err != nil {
|
||||||
|
|
@ -313,7 +318,7 @@ func triggerOTAStateUpdate() {
|
||||||
func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) error {
|
func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) error {
|
||||||
scopedLogger := otaLogger.With().
|
scopedLogger := otaLogger.With().
|
||||||
Str("deviceId", deviceId).
|
Str("deviceId", deviceId).
|
||||||
Str("includePreRelease", fmt.Sprintf("%v", includePreRelease)).
|
Bool("includePreRelease", includePreRelease).
|
||||||
Logger()
|
Logger()
|
||||||
|
|
||||||
scopedLogger.Info().Msg("Trying to update...")
|
scopedLogger.Info().Msg("Trying to update...")
|
||||||
|
|
@ -362,8 +367,9 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
|
||||||
otaState.Error = fmt.Sprintf("Error downloading app update: %v", err)
|
otaState.Error = fmt.Sprintf("Error downloading app update: %v", err)
|
||||||
scopedLogger.Error().Err(err).Msg("Error downloading app update")
|
scopedLogger.Error().Err(err).Msg("Error downloading app update")
|
||||||
triggerOTAStateUpdate()
|
triggerOTAStateUpdate()
|
||||||
return err
|
return fmt.Errorf("error downloading app update: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadFinished := time.Now()
|
downloadFinished := time.Now()
|
||||||
otaState.AppDownloadFinishedAt = &downloadFinished
|
otaState.AppDownloadFinishedAt = &downloadFinished
|
||||||
otaState.AppDownloadProgress = 1
|
otaState.AppDownloadProgress = 1
|
||||||
|
|
@ -379,17 +385,21 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
|
||||||
otaState.Error = fmt.Sprintf("Error verifying app update hash: %v", err)
|
otaState.Error = fmt.Sprintf("Error verifying app update hash: %v", err)
|
||||||
scopedLogger.Error().Err(err).Msg("Error verifying app update hash")
|
scopedLogger.Error().Err(err).Msg("Error verifying app update hash")
|
||||||
triggerOTAStateUpdate()
|
triggerOTAStateUpdate()
|
||||||
return err
|
return fmt.Errorf("error verifying app update: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyFinished := time.Now()
|
verifyFinished := time.Now()
|
||||||
otaState.AppVerifiedAt = &verifyFinished
|
otaState.AppVerifiedAt = &verifyFinished
|
||||||
otaState.AppVerificationProgress = 1
|
otaState.AppVerificationProgress = 1
|
||||||
|
triggerOTAStateUpdate()
|
||||||
|
|
||||||
otaState.AppUpdatedAt = &verifyFinished
|
otaState.AppUpdatedAt = &verifyFinished
|
||||||
otaState.AppUpdateProgress = 1
|
otaState.AppUpdateProgress = 1
|
||||||
triggerOTAStateUpdate()
|
triggerOTAStateUpdate()
|
||||||
|
|
||||||
scopedLogger.Info().Msg("App update downloaded")
|
scopedLogger.Info().Msg("App update downloaded")
|
||||||
rebootNeeded = true
|
rebootNeeded = true
|
||||||
|
triggerOTAStateUpdate()
|
||||||
} else {
|
} else {
|
||||||
scopedLogger.Info().Msg("App is up to date")
|
scopedLogger.Info().Msg("App is up to date")
|
||||||
}
|
}
|
||||||
|
|
@ -405,8 +415,9 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
|
||||||
otaState.Error = fmt.Sprintf("Error downloading system update: %v", err)
|
otaState.Error = fmt.Sprintf("Error downloading system update: %v", err)
|
||||||
scopedLogger.Error().Err(err).Msg("Error downloading system update")
|
scopedLogger.Error().Err(err).Msg("Error downloading system update")
|
||||||
triggerOTAStateUpdate()
|
triggerOTAStateUpdate()
|
||||||
return err
|
return fmt.Errorf("error downloading system update: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadFinished := time.Now()
|
downloadFinished := time.Now()
|
||||||
otaState.SystemDownloadFinishedAt = &downloadFinished
|
otaState.SystemDownloadFinishedAt = &downloadFinished
|
||||||
otaState.SystemDownloadProgress = 1
|
otaState.SystemDownloadProgress = 1
|
||||||
|
|
@ -422,8 +433,9 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
|
||||||
otaState.Error = fmt.Sprintf("Error verifying system update hash: %v", err)
|
otaState.Error = fmt.Sprintf("Error verifying system update hash: %v", err)
|
||||||
scopedLogger.Error().Err(err).Msg("Error verifying system update hash")
|
scopedLogger.Error().Err(err).Msg("Error verifying system update hash")
|
||||||
triggerOTAStateUpdate()
|
triggerOTAStateUpdate()
|
||||||
return err
|
return fmt.Errorf("error verifying system update: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
scopedLogger.Info().Msg("System update downloaded")
|
scopedLogger.Info().Msg("System update downloaded")
|
||||||
verifyFinished := time.Now()
|
verifyFinished := time.Now()
|
||||||
otaState.SystemVerifiedAt = &verifyFinished
|
otaState.SystemVerifiedAt = &verifyFinished
|
||||||
|
|
@ -439,8 +451,10 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
otaState.Error = fmt.Sprintf("Error starting rk_ota command: %v", err)
|
otaState.Error = fmt.Sprintf("Error starting rk_ota command: %v", err)
|
||||||
scopedLogger.Error().Err(err).Msg("Error starting rk_ota command")
|
scopedLogger.Error().Err(err).Msg("Error starting rk_ota command")
|
||||||
|
triggerOTAStateUpdate()
|
||||||
return fmt.Errorf("error starting rk_ota command: %w", err)
|
return fmt.Errorf("error starting rk_ota command: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
|
@ -475,13 +489,15 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
|
||||||
Str("output", output).
|
Str("output", output).
|
||||||
Int("exitCode", cmd.ProcessState.ExitCode()).
|
Int("exitCode", cmd.ProcessState.ExitCode()).
|
||||||
Msg("Error executing rk_ota command")
|
Msg("Error executing rk_ota command")
|
||||||
|
triggerOTAStateUpdate()
|
||||||
return fmt.Errorf("error executing rk_ota command: %w\nOutput: %s", err, output)
|
return fmt.Errorf("error executing rk_ota command: %w\nOutput: %s", err, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
scopedLogger.Info().Str("output", output).Msg("rk_ota success")
|
scopedLogger.Info().Str("output", output).Msg("rk_ota success")
|
||||||
otaState.SystemUpdateProgress = 1
|
otaState.SystemUpdateProgress = 1
|
||||||
otaState.SystemUpdatedAt = &verifyFinished
|
otaState.SystemUpdatedAt = &verifyFinished
|
||||||
triggerOTAStateUpdate()
|
|
||||||
rebootNeeded = true
|
rebootNeeded = true
|
||||||
|
triggerOTAStateUpdate()
|
||||||
} else {
|
} else {
|
||||||
scopedLogger.Info().Msg("System is up to date")
|
scopedLogger.Info().Msg("System is up to date")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ type Client struct {
|
||||||
var (
|
var (
|
||||||
defaultTimerDuration = 1 * time.Second
|
defaultTimerDuration = 1 * time.Second
|
||||||
defaultLinkUpTimeout = 30 * time.Second
|
defaultLinkUpTimeout = 30 * time.Second
|
||||||
|
defaultDHCPTimeout = 5 * time.Second // DHCP request timeout (not link up timeout)
|
||||||
maxRenewalAttemptDuration = 2 * time.Hour
|
maxRenewalAttemptDuration = 2 * time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -125,11 +126,11 @@ func NewClient(ctx context.Context, ifaces []string, c *Config, l *zerolog.Logge
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Timeout == 0 {
|
if cfg.Timeout == 0 {
|
||||||
cfg.Timeout = defaultLinkUpTimeout
|
cfg.Timeout = defaultDHCPTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Retries == 0 {
|
if cfg.Retries == 0 {
|
||||||
cfg.Retries = 3
|
cfg.Retries = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
|
|
@ -153,9 +154,15 @@ func NewClient(ctx context.Context, ifaces []string, c *Config, l *zerolog.Logge
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetTimer(t *time.Timer, l *zerolog.Logger) {
|
func resetTimer(t *time.Timer, attempt int, l *zerolog.Logger) {
|
||||||
l.Debug().Dur("delay", defaultTimerDuration).Msg("will retry later")
|
// Exponential backoff: 1s, 2s, 4s, 8s, max 8s
|
||||||
t.Reset(defaultTimerDuration)
|
backoffAttempt := attempt
|
||||||
|
if backoffAttempt > 3 {
|
||||||
|
backoffAttempt = 3
|
||||||
|
}
|
||||||
|
delay := time.Duration(1<<backoffAttempt) * time.Second
|
||||||
|
l.Debug().Dur("delay", delay).Int("attempt", attempt).Msg("will retry later")
|
||||||
|
t.Reset(delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRenewalTime(lease *Lease) time.Duration {
|
func getRenewalTime(lease *Lease) time.Duration {
|
||||||
|
|
@ -168,12 +175,14 @@ func getRenewalTime(lease *Lease) time.Duration {
|
||||||
|
|
||||||
func (c *Client) requestLoop(t *time.Timer, family int, ifname string) {
|
func (c *Client) requestLoop(t *time.Timer, family int, ifname string) {
|
||||||
l := c.l.With().Str("interface", ifname).Int("family", family).Logger()
|
l := c.l.With().Str("interface", ifname).Int("family", family).Logger()
|
||||||
|
attempt := 0
|
||||||
for range t.C {
|
for range t.C {
|
||||||
l.Info().Msg("requesting lease")
|
l.Info().Int("attempt", attempt).Msg("requesting lease")
|
||||||
|
|
||||||
if _, err := c.ensureInterfaceUp(ifname); err != nil {
|
if _, err := c.ensureInterfaceUp(ifname); err != nil {
|
||||||
l.Error().Err(err).Msg("failed to ensure interface up")
|
l.Error().Err(err).Int("attempt", attempt).Msg("failed to ensure interface up")
|
||||||
resetTimer(t, c.l)
|
resetTimer(t, attempt, c.l)
|
||||||
|
attempt++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,11 +197,14 @@ func (c *Client) requestLoop(t *time.Timer, family int, ifname string) {
|
||||||
lease, err = c.requestLease6(ifname)
|
lease, err = c.requestLease6(ifname)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Error().Err(err).Msg("failed to request lease")
|
l.Error().Err(err).Int("attempt", attempt).Msg("failed to request lease")
|
||||||
resetTimer(t, c.l)
|
resetTimer(t, attempt, c.l)
|
||||||
|
attempt++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Successfully obtained lease, reset attempt counter
|
||||||
|
attempt = 0
|
||||||
c.handleLeaseChange(lease)
|
c.handleLeaseChange(lease)
|
||||||
|
|
||||||
nextRenewal := getRenewalTime(lease)
|
nextRenewal := getRenewalTime(lease)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,31 @@ show_help() {
|
||||||
echo " $0 -r 192.168.0.17 -u admin"
|
echo " $0 -r 192.168.0.17 -u admin"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Function to check if device is pingable
|
||||||
|
check_ping() {
|
||||||
|
local host=$1
|
||||||
|
msg_info "▶ Checking if device is reachable at ${host}..."
|
||||||
|
if ! ping -c 3 -W 5 "${host}" > /dev/null 2>&1; then
|
||||||
|
msg_err "Error: Cannot reach device at ${host}"
|
||||||
|
msg_err "Please verify the IP address and network connectivity"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
msg_info "✓ Device is reachable"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if SSH is accessible
|
||||||
|
check_ssh() {
|
||||||
|
local user=$1
|
||||||
|
local host=$2
|
||||||
|
msg_info "▶ Checking SSH connectivity to ${user}@${host}..."
|
||||||
|
if ! ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o ConnectTimeout=10 "${user}@${host}" "echo 'SSH connection successful'" > /dev/null 2>&1; then
|
||||||
|
msg_err "Error: Cannot establish SSH connection to ${user}@${host}"
|
||||||
|
msg_err "Please verify SSH access and credentials"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
msg_info "✓ SSH connection successful"
|
||||||
|
}
|
||||||
|
|
||||||
# Default values
|
# Default values
|
||||||
SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))")
|
SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))")
|
||||||
REMOTE_USER="root"
|
REMOTE_USER="root"
|
||||||
|
|
@ -113,6 +138,10 @@ if [ -z "$REMOTE_HOST" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check device connectivity before proceeding
|
||||||
|
check_ping "${REMOTE_HOST}"
|
||||||
|
check_ssh "${REMOTE_USER}" "${REMOTE_HOST}"
|
||||||
|
|
||||||
# check if the current CPU architecture is x86_64
|
# check if the current CPU architecture is x86_64
|
||||||
if [ "$(uname -m)" != "x86_64" ]; then
|
if [ "$(uname -m)" != "x86_64" ]; then
|
||||||
msg_warn "Warning: This script is only supported on x86_64 architecture"
|
msg_warn "Warning: This script is only supported on x86_64 architecture"
|
||||||
|
|
@ -147,10 +176,10 @@ if [ "$RUN_GO_TESTS" = true ]; then
|
||||||
make build_dev_test
|
make build_dev_test
|
||||||
|
|
||||||
msg_info "▶ Copying device-tests.tar.gz to remote host"
|
msg_info "▶ Copying device-tests.tar.gz to remote host"
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /tmp/device-tests.tar.gz" < device-tests.tar.gz
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > /tmp/device-tests.tar.gz" < device-tests.tar.gz
|
||||||
|
|
||||||
msg_info "▶ Running go tests"
|
msg_info "▶ Running go tests"
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" ash << 'EOF'
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" ash << 'EOF'
|
||||||
set -e
|
set -e
|
||||||
TMP_DIR=$(mktemp -d)
|
TMP_DIR=$(mktemp -d)
|
||||||
cd ${TMP_DIR}
|
cd ${TMP_DIR}
|
||||||
|
|
@ -193,10 +222,10 @@ then
|
||||||
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
|
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
|
||||||
|
|
||||||
# Copy the binary to the remote host as if we were the OTA updater.
|
# Copy the binary to the remote host as if we were the OTA updater.
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app
|
||||||
|
|
||||||
# Reboot the device, the new app will be deployed by the startup process.
|
# Reboot the device, the new app will be deployed by the startup process.
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "reboot"
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "reboot"
|
||||||
else
|
else
|
||||||
msg_info "▶ Building development binary"
|
msg_info "▶ Building development binary"
|
||||||
do_make build_dev \
|
do_make build_dev \
|
||||||
|
|
@ -205,21 +234,21 @@ else
|
||||||
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
|
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
|
||||||
|
|
||||||
# Kill any existing instances of the application
|
# Kill any existing instances of the application
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true"
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true"
|
||||||
|
|
||||||
# Copy the binary to the remote host
|
# Copy the binary to the remote host
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > ${REMOTE_PATH}/jetkvm_app_debug" < bin/jetkvm_app
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > ${REMOTE_PATH}/jetkvm_app_debug" < bin/jetkvm_app
|
||||||
|
|
||||||
if [ "$RESET_USB_HID_DEVICE" = true ]; then
|
if [ "$RESET_USB_HID_DEVICE" = true ]; then
|
||||||
msg_info "▶ Resetting USB HID device"
|
msg_info "▶ Resetting USB HID device"
|
||||||
msg_warn "The option has been deprecated and will be removed in a future version, as JetKVM will now reset USB gadget configuration when needed"
|
msg_warn "The option has been deprecated and will be removed in a future version, as JetKVM will now reset USB gadget configuration when needed"
|
||||||
# Remove the old USB gadget configuration
|
# Remove the old USB gadget configuration
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "rm -rf /sys/kernel/config/usb_gadget/jetkvm/configs/c.1/hid.usb*"
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "rm -rf /sys/kernel/config/usb_gadget/jetkvm/configs/c.1/hid.usb*"
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "ls /sys/class/udc > /sys/kernel/config/usb_gadget/jetkvm/UDC"
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "ls /sys/class/udc > /sys/kernel/config/usb_gadget/jetkvm/UDC"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Deploy and run the application on the remote host
|
# Deploy and run the application on the remote host
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" ash << EOF
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" ash << EOF
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Set the library path to include the directory where librockit.so is located
|
# Set the library path to include the directory where librockit.so is located
|
||||||
|
|
@ -229,6 +258,17 @@ export LD_LIBRARY_PATH=/oem/usr/lib:\$LD_LIBRARY_PATH
|
||||||
killall jetkvm_app || true
|
killall jetkvm_app || true
|
||||||
killall jetkvm_app_debug || true
|
killall jetkvm_app_debug || true
|
||||||
|
|
||||||
|
# Wait until both binaries are killed, max 10 seconds
|
||||||
|
i=1
|
||||||
|
while [ \$i -le 10 ]; do
|
||||||
|
echo "Waiting for jetkvm_app and jetkvm_app_debug to be killed, \$i/10 ..."
|
||||||
|
if ! pgrep -f "jetkvm_app" > /dev/null && ! pgrep -f "jetkvm_app_debug" > /dev/null; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
i=\$((i + 1))
|
||||||
|
done
|
||||||
|
|
||||||
# Navigate to the directory where the binary will be stored
|
# Navigate to the directory where the binary will be stored
|
||||||
cd "${REMOTE_PATH}"
|
cd "${REMOTE_PATH}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "kvm-ui",
|
"name": "kvm-ui",
|
||||||
"version": "2025.10.24.2140",
|
"version": "2025.10.30.0830",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "kvm-ui",
|
"name": "kvm-ui",
|
||||||
"version": "2025.10.24.2140",
|
"version": "2025.10.30.0830",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^2.2.9",
|
"@headlessui/react": "^2.2.9",
|
||||||
"@headlessui/tailwindcss": "^0.2.2",
|
"@headlessui/tailwindcss": "^0.2.2",
|
||||||
|
|
@ -31,18 +31,18 @@
|
||||||
"react-hook-form": "^7.65.0",
|
"react-hook-form": "^7.65.0",
|
||||||
"react-hot-toast": "^2.6.0",
|
"react-hot-toast": "^2.6.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-router": "^7.9.4",
|
"react-router": "^7.9.5",
|
||||||
"react-simple-keyboard": "^3.8.131",
|
"react-simple-keyboard": "^3.8.132",
|
||||||
"react-use-websocket": "^4.13.0",
|
"react-use-websocket": "^4.13.0",
|
||||||
"react-xtermjs": "^1.0.10",
|
"react-xtermjs": "^1.0.10",
|
||||||
"recharts": "^3.3.0",
|
"recharts": "^3.3.0",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"usehooks-ts": "^3.1.1",
|
"usehooks-ts": "^3.1.1",
|
||||||
"validator": "^13.15.15",
|
"validator": "^13.15.20",
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.4.0",
|
"@eslint/compat": "^1.4.1",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "^9.38.0",
|
"@eslint/js": "^9.38.0",
|
||||||
"@inlang/cli": "^3.0.12",
|
"@inlang/cli": "^3.0.12",
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
"@types/react": "^19.2.2",
|
"@types/react": "^19.2.2",
|
||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.2",
|
||||||
"@types/semver": "^7.7.1",
|
"@types/semver": "^7.7.1",
|
||||||
"@types/validator": "^13.15.3",
|
"@types/validator": "^13.15.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
||||||
"@typescript-eslint/parser": "^8.46.2",
|
"@typescript-eslint/parser": "^8.46.2",
|
||||||
"@vitejs/plugin-react-swc": "^4.2.0",
|
"@vitejs/plugin-react-swc": "^4.2.0",
|
||||||
|
|
@ -126,6 +126,7 @@
|
||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
|
|
@ -799,13 +800,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/compat": {
|
"node_modules/@eslint/compat": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.4.1.tgz",
|
||||||
"integrity": "sha512-DEzm5dKeDBPm3r08Ixli/0cmxr8LkRdwxMRUIJBlSCpAwSrvFEJpVBzV+66JhDxiaqKxnRzCXhtiMiczF7Hglg==",
|
"integrity": "sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/core": "^0.16.0"
|
"@eslint/core": "^0.17.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
|
@ -834,21 +835,21 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/config-helpers": {
|
"node_modules/@eslint/config-helpers": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
|
||||||
"integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==",
|
"integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/core": "^0.16.0"
|
"@eslint/core": "^0.17.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/core": {
|
"node_modules/@eslint/core": {
|
||||||
"version": "0.16.0",
|
"version": "0.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
|
||||||
"integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
|
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/json-schema": "^7.0.15"
|
"@types/json-schema": "^7.0.15"
|
||||||
|
|
@ -914,12 +915,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/plugin-kit": {
|
"node_modules/@eslint/plugin-kit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
|
||||||
"integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
|
"integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/core": "^0.16.0",
|
"@eslint/core": "^0.17.0",
|
||||||
"levn": "^0.4.1"
|
"levn": "^0.4.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -1728,9 +1729,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core": {
|
"node_modules/@swc/core": {
|
||||||
"version": "1.13.21",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.21.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.14.0.tgz",
|
||||||
"integrity": "sha512-umBaSb65O1v6Lt8RV3o5srw0nKr25amf/yRIGFPug63sAerL9n2UkmfGywA1l1aN81W7faXIynF0JmlQ2wPSdw==",
|
"integrity": "sha512-oExhY90bes5pDTVrei0xlMVosTxwd/NMafIpqsC4dMbRYZ5KB981l/CX8tMnGsagTplj/RcG9BeRYmV6/J5m3w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
|
@ -1746,16 +1747,16 @@
|
||||||
"url": "https://opencollective.com/swc"
|
"url": "https://opencollective.com/swc"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@swc/core-darwin-arm64": "1.13.21",
|
"@swc/core-darwin-arm64": "1.14.0",
|
||||||
"@swc/core-darwin-x64": "1.13.21",
|
"@swc/core-darwin-x64": "1.14.0",
|
||||||
"@swc/core-linux-arm-gnueabihf": "1.13.21",
|
"@swc/core-linux-arm-gnueabihf": "1.14.0",
|
||||||
"@swc/core-linux-arm64-gnu": "1.13.21",
|
"@swc/core-linux-arm64-gnu": "1.14.0",
|
||||||
"@swc/core-linux-arm64-musl": "1.13.21",
|
"@swc/core-linux-arm64-musl": "1.14.0",
|
||||||
"@swc/core-linux-x64-gnu": "1.13.21",
|
"@swc/core-linux-x64-gnu": "1.14.0",
|
||||||
"@swc/core-linux-x64-musl": "1.13.21",
|
"@swc/core-linux-x64-musl": "1.14.0",
|
||||||
"@swc/core-win32-arm64-msvc": "1.13.21",
|
"@swc/core-win32-arm64-msvc": "1.14.0",
|
||||||
"@swc/core-win32-ia32-msvc": "1.13.21",
|
"@swc/core-win32-ia32-msvc": "1.14.0",
|
||||||
"@swc/core-win32-x64-msvc": "1.13.21"
|
"@swc/core-win32-x64-msvc": "1.14.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@swc/helpers": ">=0.5.17"
|
"@swc/helpers": ">=0.5.17"
|
||||||
|
|
@ -1767,9 +1768,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-darwin-arm64": {
|
"node_modules/@swc/core-darwin-arm64": {
|
||||||
"version": "1.13.21",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.21.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.14.0.tgz",
|
||||||
"integrity": "sha512-0jaz9r7f0PDK8OyyVooadv8dkFlQmVmBK6DtAnWSRjkCbNt4sdqsc9ZkyEDJXaxOVcMQ3pJx/Igniyw5xqACLw==",
|
"integrity": "sha512-uHPC8rlCt04nvYNczWzKVdgnRhxCa3ndKTBBbBpResOZsRmiwRAvByIGh599j+Oo6Z5eyTPrgY+XfJzVmXnN7Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -1784,9 +1785,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-darwin-x64": {
|
"node_modules/@swc/core-darwin-x64": {
|
||||||
"version": "1.13.21",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.21.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.14.0.tgz",
|
||||||
"integrity": "sha512-pLeZn+NTGa7oW/ysD6oM82BjKZl71WNJR9BKXRsOhrNQeUWv55DCoZT2P4DzeU5Xgjmos+iMoDLg/9R6Ngc0PA==",
|
"integrity": "sha512-2SHrlpl68vtePRknv9shvM9YKKg7B9T13tcTg9aFCwR318QTYo+FzsKGmQSv9ox/Ua0Q2/5y2BNjieffJoo4nA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -1801,9 +1802,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||||
"version": "1.13.21",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.21.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.14.0.tgz",
|
||||||
"integrity": "sha512-p9aYzTmP7qVDPkXxnbekOfbT11kxnPiuLrUbgpN/vn6sxXDCObMAiY63WlDR0IauBK571WUdmgb04goe/xTQWw==",
|
"integrity": "sha512-SMH8zn01dxt809svetnxpeg/jWdpi6dqHKO3Eb11u4OzU2PK7I5uKS6gf2hx5LlTbcJMFKULZiVwjlQLe8eqtg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
|
|
@ -1818,9 +1819,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm64-gnu": {
|
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||||
"version": "1.13.21",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.21.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.14.0.tgz",
|
||||||
"integrity": "sha512-yRqFoGlCwEX1nS7OajBE23d0LPeONmFAgoe4rgRYvaUb60qGxIJoMMdvF2g3dum9ZyVDYAb3kP09hbXFbMGr4A==",
|
"integrity": "sha512-q2JRu2D8LVqGeHkmpVCljVNltG0tB4o4eYg+dElFwCS8l2Mnt9qurMCxIeo9mgoqz0ax+k7jWtIRHktnVCbjvQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -1835,9 +1836,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-arm64-musl": {
|
"node_modules/@swc/core-linux-arm64-musl": {
|
||||||
"version": "1.13.21",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.21.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.14.0.tgz",
|
||||||
"integrity": "sha512-wu5EGA86gtdYMW69eU80jROzArzD3/6G6zzK0VVR+OFt/0zqbajiiszIpaniOVACObLfJEcShQ05B3q0+CpUEg==",
|
"integrity": "sha512-uofpVoPCEUjYIv454ZEZ3sLgMD17nIwlz2z7bsn7rl301Kt/01umFA7MscUovFfAK2IRGck6XB+uulMu6aFhKQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -1852,9 +1853,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-x64-gnu": {
|
"node_modules/@swc/core-linux-x64-gnu": {
|
||||||
"version": "1.13.21",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.21.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.14.0.tgz",
|
||||||
"integrity": "sha512-AoGGVPNXH3C4S7WlJOxN1nGW5nj//J9uKysS7CIBotRmHXfHO4wPK3TVFRTA4cuouAWBBn7O8m3A99p/GR+iaw==",
|
"integrity": "sha512-quTTx1Olm05fBfv66DEBuOsOgqdypnZ/1Bh3yGXWY7ANLFeeRpCDZpljD9BSjdsNdPOlwJmEUZXMHtGm3v1TZQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -1869,9 +1870,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-linux-x64-musl": {
|
"node_modules/@swc/core-linux-x64-musl": {
|
||||||
"version": "1.13.21",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.21.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.14.0.tgz",
|
||||||
"integrity": "sha512-cBy2amuDuxMZnEq16MqGu+DUlEFqI+7F/OACNlk7zEJKq48jJKGEMqJz3X2ucJE5jqUIg6Pos6Uo/y+vuWQymQ==",
|
"integrity": "sha512-caaNAu+aIqT8seLtCf08i8C3/UC5ttQujUjejhMcuS1/LoCKtNiUs4VekJd2UGt+pyuuSrQ6dKl8CbCfWvWeXw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -1886,9 +1887,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-arm64-msvc": {
|
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||||
"version": "1.13.21",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.21.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.14.0.tgz",
|
||||||
"integrity": "sha512-2xfR5gnqBGOMOlY3s1QiFTXZaivTILMwX67FD2uzT6OCbT/3lyAM/4+3BptBXD8pUkkOGMFLsdeHw4fbO1GrpQ==",
|
"integrity": "sha512-EeW3jFlT3YNckJ6V/JnTfGcX7UHGyh6/AiCPopZ1HNaGiXVCKHPpVQZicmtyr/UpqxCXLrTgjHOvyMke7YN26A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
|
|
@ -1903,9 +1904,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-ia32-msvc": {
|
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||||
"version": "1.13.21",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.21.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.14.0.tgz",
|
||||||
"integrity": "sha512-0pkpgKlBDwUImWTQxLakKbzZI6TIGVVAxk658oxrY8VK+hxRy2iezFY6m5Urmeds47M/cnW3dO+OY4C2caOF8A==",
|
"integrity": "sha512-dPai3KUIcihV5hfoO4QNQF5HAaw8+2bT7dvi8E5zLtecW2SfL3mUZipzampXq5FHll0RSCLzlrXnSx+dBRZIIQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
|
|
@ -1920,9 +1921,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@swc/core-win32-x64-msvc": {
|
"node_modules/@swc/core-win32-x64-msvc": {
|
||||||
"version": "1.13.21",
|
"version": "1.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.21.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.14.0.tgz",
|
||||||
"integrity": "sha512-DAnIw2J95TOW4Kr7NBx12vlZPW3QndbpFMmuC7x+fPoozoLpEscaDkiYhk7/sTtY9pubPMfHFPBORlbqyQCfOQ==",
|
"integrity": "sha512-nm+JajGrTqUA6sEHdghDlHMNfH1WKSiuvljhdmBACW4ta4LC3gKurX2qZuiBARvPkephW9V/i5S8QPY1PzFEqg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|
@ -2387,6 +2388,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
||||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
|
|
@ -2396,6 +2398,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
|
||||||
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
|
|
@ -2414,9 +2417,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/validator": {
|
"node_modules/@types/validator": {
|
||||||
"version": "13.15.3",
|
"version": "13.15.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.4.tgz",
|
||||||
"integrity": "sha512-7bcUmDyS6PN3EuD9SlGGOxM77F8WLVsrwkxyWxKnxzmXoequ6c7741QBrANq6htVRGOITJ7z72mTP6Z4XyuG+Q==",
|
"integrity": "sha512-LSFfpSnJJY9wbC0LQxgvfb+ynbHftFo0tMsFOl/J4wexLnYMmDSPaj2ZyDv3TkfL1UePxPrxOWJfbiRS8mQv7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
|
@ -2466,6 +2469,7 @@
|
||||||
"integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
|
"integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.46.2",
|
"@typescript-eslint/scope-manager": "8.46.2",
|
||||||
"@typescript-eslint/types": "8.46.2",
|
"@typescript-eslint/types": "8.46.2",
|
||||||
|
|
@ -2772,13 +2776,15 @@
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
||||||
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
|
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.15.0",
|
"version": "8.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
|
|
@ -3062,9 +3068,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/baseline-browser-mapping": {
|
"node_modules/baseline-browser-mapping": {
|
||||||
"version": "2.8.20",
|
"version": "2.8.21",
|
||||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz",
|
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.21.tgz",
|
||||||
"integrity": "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==",
|
"integrity": "sha512-JU0h5APyQNsHOlAM7HnQnPToSDQoEBZqzu/YBlqDnEeymPnZDREeXJA3KBMQee+dKteAxZ2AtvQEvVYdZf241Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
@ -3114,6 +3120,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.19",
|
"baseline-browser-mapping": "^2.8.19",
|
||||||
"caniuse-lite": "^1.0.30001751",
|
"caniuse-lite": "^1.0.30001751",
|
||||||
|
|
@ -3343,7 +3350,8 @@
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/cva": {
|
"node_modules/cva": {
|
||||||
"version": "1.0.0-beta.4",
|
"version": "1.0.0-beta.4",
|
||||||
|
|
@ -3658,9 +3666,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.240",
|
"version": "1.5.243",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.240.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.243.tgz",
|
||||||
"integrity": "sha512-OBwbZjWgrCOH+g6uJsA2/7Twpas2OlepS9uvByJjR2datRDuKGYeD+nP8lBBks2qnB7bGJNHDUx7c/YLaT3QMQ==",
|
"integrity": "sha512-ZCphxFW3Q1TVhcgS9blfut1PX8lusVi2SvXQgmEEnK4TCmE1JhH2JkjJN+DNt0pJJwfBri5AROBnz2b/C+YU9g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
|
@ -3939,6 +3947,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz",
|
||||||
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
|
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
|
|
@ -3999,6 +4008,7 @@
|
||||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"eslint-config-prettier": "bin/cli.js"
|
"eslint-config-prettier": "bin/cli.js"
|
||||||
},
|
},
|
||||||
|
|
@ -4072,6 +4082,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
|
||||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rtsao/scc": "^1.1.0",
|
"@rtsao/scc": "^1.1.0",
|
||||||
"array-includes": "^3.1.9",
|
"array-includes": "^3.1.9",
|
||||||
|
|
@ -4268,6 +4279,18 @@
|
||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint/node_modules/@eslint/core": {
|
||||||
|
"version": "0.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
|
||||||
|
"integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/json-schema": "^7.0.15"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint/node_modules/eslint-visitor-keys": {
|
"node_modules/eslint/node_modules/eslint-visitor-keys": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
||||||
|
|
@ -4510,12 +4533,12 @@
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/focus-trap": {
|
"node_modules/focus-trap": {
|
||||||
"version": "7.6.5",
|
"version": "7.6.6",
|
||||||
"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz",
|
"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.6.tgz",
|
||||||
"integrity": "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==",
|
"integrity": "sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tabbable": "^6.2.0"
|
"tabbable": "^6.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/focus-trap-react": {
|
"node_modules/focus-trap-react": {
|
||||||
|
|
@ -4922,9 +4945,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/immer": {
|
"node_modules/immer": {
|
||||||
"version": "10.1.3",
|
"version": "10.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz",
|
||||||
"integrity": "sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==",
|
"integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
|
@ -5494,6 +5517,7 @@
|
||||||
"integrity": "sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==",
|
"integrity": "sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -5936,9 +5960,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.26",
|
"version": "2.0.27",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
||||||
"integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==",
|
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
|
@ -6216,6 +6240,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
|
|
@ -6261,6 +6286,7 @@
|
||||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
|
|
@ -6417,6 +6443,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
@ -6439,6 +6466,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
||||||
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
|
|
@ -6500,6 +6528,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/use-sync-external-store": "^0.0.6",
|
"@types/use-sync-external-store": "^0.0.6",
|
||||||
"use-sync-external-store": "^1.4.0"
|
"use-sync-external-store": "^1.4.0"
|
||||||
|
|
@ -6519,9 +6548,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "7.9.4",
|
"version": "7.9.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.5.tgz",
|
||||||
"integrity": "sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==",
|
"integrity": "sha512-JmxqrnBZ6E9hWmf02jzNn9Jm3UqyeimyiwzD69NjxGySG6lIz/1LVPsoTCwN7NBX2XjCEa1LIX5EMz1j2b6u6A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cookie": "^1.0.1",
|
"cookie": "^1.0.1",
|
||||||
|
|
@ -6541,9 +6570,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-simple-keyboard": {
|
"node_modules/react-simple-keyboard": {
|
||||||
"version": "3.8.131",
|
"version": "3.8.132",
|
||||||
"resolved": "https://registry.npmjs.org/react-simple-keyboard/-/react-simple-keyboard-3.8.131.tgz",
|
"resolved": "https://registry.npmjs.org/react-simple-keyboard/-/react-simple-keyboard-3.8.132.tgz",
|
||||||
"integrity": "sha512-gICYtaV38AU/E1PTTwzJOF6s5fu6Nu3GZQwnaSNB4VGOO3UwOn8rioDEFBLvjMWpP8kwfWp2of8xywY647rTxA==",
|
"integrity": "sha512-GoXK+6SRu72Jn8qT8fy+PxstIdZEACyIi/7zy0qXcrB6EJaN6zZk0/w3Sv3ALLwXqQd/3t3yUL4DQOwoNO1cbw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
|
@ -6596,7 +6625,8 @@
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/redux-thunk": {
|
"node_modules/redux-thunk": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
|
|
@ -6832,9 +6862,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/set-cookie-parser": {
|
"node_modules/set-cookie-parser": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
|
||||||
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
|
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
|
|
@ -7186,7 +7216,8 @@
|
||||||
"version": "4.1.16",
|
"version": "4.1.16",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz",
|
||||||
"integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==",
|
"integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/tapable": {
|
"node_modules/tapable": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
|
|
@ -7246,6 +7277,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -7422,6 +7454,7 @@
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|
@ -7570,9 +7603,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/validator": {
|
"node_modules/validator": {
|
||||||
"version": "13.15.15",
|
"version": "13.15.20",
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz",
|
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz",
|
||||||
"integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==",
|
"integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
|
|
@ -7605,6 +7638,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz",
|
||||||
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
|
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
|
|
@ -7716,6 +7750,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -7864,6 +7899,7 @@
|
||||||
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
|
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "kvm-ui",
|
"name": "kvm-ui",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2025.10.24.2140",
|
"version": "2025.10.30.0830",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^22.20.0"
|
"node": "^22.20.0"
|
||||||
|
|
@ -50,18 +50,18 @@
|
||||||
"react-hook-form": "^7.65.0",
|
"react-hook-form": "^7.65.0",
|
||||||
"react-hot-toast": "^2.6.0",
|
"react-hot-toast": "^2.6.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-router": "^7.9.4",
|
"react-router": "^7.9.5",
|
||||||
"react-simple-keyboard": "^3.8.131",
|
"react-simple-keyboard": "^3.8.132",
|
||||||
"react-use-websocket": "^4.13.0",
|
"react-use-websocket": "^4.13.0",
|
||||||
"react-xtermjs": "^1.0.10",
|
"react-xtermjs": "^1.0.10",
|
||||||
"recharts": "^3.3.0",
|
"recharts": "^3.3.0",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"usehooks-ts": "^3.1.1",
|
"usehooks-ts": "^3.1.1",
|
||||||
"validator": "^13.15.15",
|
"validator": "^13.15.20",
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.4.0",
|
"@eslint/compat": "^1.4.1",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "^9.38.0",
|
"@eslint/js": "^9.38.0",
|
||||||
"@inlang/cli": "^3.0.12",
|
"@inlang/cli": "^3.0.12",
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
"@types/react": "^19.2.2",
|
"@types/react": "^19.2.2",
|
||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.2",
|
||||||
"@types/semver": "^7.7.1",
|
"@types/semver": "^7.7.1",
|
||||||
"@types/validator": "^13.15.3",
|
"@types/validator": "^13.15.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
||||||
"@typescript-eslint/parser": "^8.46.2",
|
"@typescript-eslint/parser": "^8.46.2",
|
||||||
"@vitejs/plugin-react-swc": "^4.2.0",
|
"@vitejs/plugin-react-swc": "^4.2.0",
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonPropsType>(
|
||||||
Button.displayName = "Button";
|
Button.displayName = "Button";
|
||||||
|
|
||||||
type LinkPropsType = Pick<LinkProps, "to"> &
|
type LinkPropsType = Pick<LinkProps, "to"> &
|
||||||
React.ComponentProps<typeof ButtonContent> & { disabled?: boolean };
|
React.ComponentProps<typeof ButtonContent> & { disabled?: boolean, reloadDocument?: boolean };
|
||||||
export const LinkButton = ({ to, ...props }: LinkPropsType) => {
|
export const LinkButton = ({ to, ...props }: LinkPropsType) => {
|
||||||
const classes = cx(
|
const classes = cx(
|
||||||
"group outline-hidden",
|
"group outline-hidden",
|
||||||
|
|
@ -230,7 +230,7 @@ export const LinkButton = ({ to, ...props }: LinkPropsType) => {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Link to={to} className={classes}>
|
<Link to={to} reloadDocument={props.reloadDocument} className={classes}>
|
||||||
<ButtonContent {...props} />
|
<ButtonContent {...props} />
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import { useRTCStore, PostRebootAction } from "@/hooks/stores";
|
||||||
import LogoBlue from "@/assets/logo-blue.svg";
|
import LogoBlue from "@/assets/logo-blue.svg";
|
||||||
import LogoWhite from "@/assets/logo-white.svg";
|
import LogoWhite from "@/assets/logo-white.svg";
|
||||||
import { isOnDevice } from "@/main";
|
import { isOnDevice } from "@/main";
|
||||||
|
import { sleep } from "@/utils";
|
||||||
|
|
||||||
|
|
||||||
interface OverlayContentProps {
|
interface OverlayContentProps {
|
||||||
|
|
@ -481,8 +482,11 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
||||||
// - Protocol-relative URLs: resolved with current protocol
|
// - Protocol-relative URLs: resolved with current protocol
|
||||||
// - Fully qualified URLs: used as-is
|
// - Fully qualified URLs: used as-is
|
||||||
const targetUrl = new URL(postRebootAction.redirectTo, window.location.origin);
|
const targetUrl = new URL(postRebootAction.redirectTo, window.location.origin);
|
||||||
|
clearInterval(intervalId); // Stop polling before redirect
|
||||||
|
|
||||||
window.location.href = targetUrl.href;
|
window.location.href = targetUrl.href;
|
||||||
|
// Add 1s delay between setting location.href and calling reload() to prevent reload from interrupting the navigation.
|
||||||
|
await sleep(1000);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
||||||
|
|
@ -573,14 +573,18 @@ export interface OtaState {
|
||||||
export interface UpdateState {
|
export interface UpdateState {
|
||||||
isUpdatePending: boolean;
|
isUpdatePending: boolean;
|
||||||
setIsUpdatePending: (isPending: boolean) => void;
|
setIsUpdatePending: (isPending: boolean) => void;
|
||||||
|
|
||||||
updateDialogHasBeenMinimized: boolean;
|
updateDialogHasBeenMinimized: boolean;
|
||||||
|
setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) => void;
|
||||||
|
|
||||||
otaState: OtaState;
|
otaState: OtaState;
|
||||||
setOtaState: (state: OtaState) => void;
|
setOtaState: (state: OtaState) => void;
|
||||||
setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) => void;
|
|
||||||
modalView: UpdateModalViews
|
modalView: UpdateModalViews
|
||||||
setModalView: (view: UpdateModalViews) => void;
|
setModalView: (view: UpdateModalViews) => void;
|
||||||
setUpdateErrorMessage: (errorMessage: string) => void;
|
|
||||||
updateErrorMessage: string | null;
|
updateErrorMessage: string | null;
|
||||||
|
setUpdateErrorMessage: (errorMessage: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUpdateStore = create<UpdateState>(set => ({
|
export const useUpdateStore = create<UpdateState>(set => ({
|
||||||
|
|
@ -611,8 +615,10 @@ export const useUpdateStore = create<UpdateState>(set => ({
|
||||||
updateDialogHasBeenMinimized: false,
|
updateDialogHasBeenMinimized: false,
|
||||||
setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) =>
|
setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) =>
|
||||||
set({ updateDialogHasBeenMinimized: hasBeenMinimized }),
|
set({ updateDialogHasBeenMinimized: hasBeenMinimized }),
|
||||||
|
|
||||||
modalView: "loading",
|
modalView: "loading",
|
||||||
setModalView: (view: UpdateModalViews) => set({ modalView: view }),
|
setModalView: (view: UpdateModalViews) => set({ modalView: view }),
|
||||||
|
|
||||||
updateErrorMessage: null,
|
updateErrorMessage: null,
|
||||||
setUpdateErrorMessage: (errorMessage: string) => set({ updateErrorMessage: errorMessage }),
|
setUpdateErrorMessage: (errorMessage: string) => set({ updateErrorMessage: errorMessage }),
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -73,10 +73,10 @@ export async function checkDeviceAuth() {
|
||||||
.GET(`${DEVICE_API}/device/status`)
|
.GET(`${DEVICE_API}/device/status`)
|
||||||
.then(res => res.json() as Promise<DeviceStatus>);
|
.then(res => res.json() as Promise<DeviceStatus>);
|
||||||
|
|
||||||
if (!res.isSetup) return redirect("/welcome");
|
if (!res.isSetup) throw redirect("/welcome");
|
||||||
|
|
||||||
const deviceRes = await api.GET(`${DEVICE_API}/device`);
|
const deviceRes = await api.GET(`${DEVICE_API}/device`);
|
||||||
if (deviceRes.status === 401) return redirect("/login-local");
|
if (deviceRes.status === 401) throw redirect("/login-local");
|
||||||
if (deviceRes.ok) {
|
if (deviceRes.ok) {
|
||||||
const device = (await deviceRes.json()) as LocalDevice;
|
const device = (await deviceRes.json()) as LocalDevice;
|
||||||
return { authMode: device.authMode };
|
return { authMode: device.authMode };
|
||||||
|
|
@ -86,7 +86,7 @@ export async function checkDeviceAuth() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkAuth() {
|
export async function checkAuth() {
|
||||||
return import.meta.env.MODE === "device" ? checkDeviceAuth() : checkCloudAuth();
|
return isOnDevice ? checkDeviceAuth() : checkCloudAuth();
|
||||||
}
|
}
|
||||||
|
|
||||||
let router;
|
let router;
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ const loader: LoaderFunction = async ({ params }: LoaderFunctionArgs) => {
|
||||||
return { device, user };
|
return { device, user };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
return { devices: [] };
|
return { user };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ const loader: LoaderFunction = async ({ params }: LoaderFunctionArgs) => {
|
||||||
return { device, user };
|
return { device, user };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
return { devices: [] };
|
return { user };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ export default function SettingsAccessIndexRoute() {
|
||||||
}
|
}
|
||||||
|
|
||||||
getCloudState();
|
getCloudState();
|
||||||
// In cloud mode, we need to navigate to the device overview page, as we don't a connection anymore
|
// In cloud mode, we need to navigate to the device overview page, as we don't have a connection anymore
|
||||||
if (!isOnDevice) navigate("/");
|
if (!isOnDevice) navigate("/");
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,17 @@ export default function SettingsGeneralRebootRoute() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { send } = useJsonRpc();
|
const { send } = useJsonRpc();
|
||||||
|
|
||||||
|
const onClose = useCallback(() => {
|
||||||
|
navigate(".."); // back to the devices.$id.settings page
|
||||||
|
window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded
|
||||||
|
}, [navigate]);
|
||||||
|
|
||||||
|
|
||||||
const onConfirmUpdate = useCallback(() => {
|
const onConfirmUpdate = useCallback(() => {
|
||||||
send("reboot", { force: true});
|
send("reboot", { force: true});
|
||||||
}, [send]);
|
}, [send]);
|
||||||
|
|
||||||
return <Dialog onClose={() => navigate("..")} onConfirmUpdate={onConfirmUpdate} />;
|
return <Dialog onClose={onClose} onConfirmUpdate={onConfirmUpdate} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Dialog({
|
export function Dialog({
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,11 @@ export default function SettingsGeneralUpdateRoute() {
|
||||||
const { setModalView, otaState } = useUpdateStore();
|
const { setModalView, otaState } = useUpdateStore();
|
||||||
const { send } = useJsonRpc();
|
const { send } = useJsonRpc();
|
||||||
|
|
||||||
|
const onClose = useCallback(() => {
|
||||||
|
navigate(".."); // back to the devices.$id.settings page
|
||||||
|
window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded
|
||||||
|
}, [navigate]);
|
||||||
|
|
||||||
const onConfirmUpdate = useCallback(() => {
|
const onConfirmUpdate = useCallback(() => {
|
||||||
send("tryUpdate", {});
|
send("tryUpdate", {});
|
||||||
setModalView("updating");
|
setModalView("updating");
|
||||||
|
|
@ -36,9 +41,9 @@ export default function SettingsGeneralUpdateRoute() {
|
||||||
} else {
|
} else {
|
||||||
setModalView("loading");
|
setModalView("loading");
|
||||||
}
|
}
|
||||||
}, [otaState.updating, otaState.error, setModalView, updateSuccess]);
|
}, [otaState.error, otaState.updating, setModalView, updateSuccess]);
|
||||||
|
|
||||||
return <Dialog onClose={() => navigate("..")} onConfirmUpdate={onConfirmUpdate} />;
|
return <Dialog onClose={onClose} onConfirmUpdate={onConfirmUpdate} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Dialog({
|
export function Dialog({
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,10 @@ export default function SettingsHardwareRoute() {
|
||||||
}
|
}
|
||||||
|
|
||||||
setBacklightSettings(settings);
|
setBacklightSettings(settings);
|
||||||
handleBacklightSettingsSave();
|
handleBacklightSettingsSave(settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBacklightSettingsSave = () => {
|
const handleBacklightSettingsSave = (backlightSettings: BacklightSettings) => {
|
||||||
send("setBacklightSettings", { params: backlightSettings }, (resp: JsonRpcResponse) => {
|
send("setBacklightSettings", { params: backlightSettings }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(
|
notifications.error(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Outlet,
|
Outlet,
|
||||||
redirect,
|
|
||||||
useLoaderData,
|
useLoaderData,
|
||||||
useLocation,
|
useLocation,
|
||||||
useNavigate,
|
useNavigate,
|
||||||
|
|
@ -16,7 +15,7 @@ import { motion, AnimatePresence } from "framer-motion";
|
||||||
import useWebSocket from "react-use-websocket";
|
import useWebSocket from "react-use-websocket";
|
||||||
|
|
||||||
import { cx } from "@/cva.config";
|
import { cx } from "@/cva.config";
|
||||||
import { CLOUD_API, DEVICE_API } from "@/ui.config";
|
import { CLOUD_API } from "@/ui.config";
|
||||||
import api from "@/api";
|
import api from "@/api";
|
||||||
import { checkAuth, isInCloud, isOnDevice } from "@/main";
|
import { checkAuth, isInCloud, isOnDevice } from "@/main";
|
||||||
import {
|
import {
|
||||||
|
|
@ -51,9 +50,14 @@ import {
|
||||||
RebootingOverlay,
|
RebootingOverlay,
|
||||||
} from "@components/VideoOverlay";
|
} from "@components/VideoOverlay";
|
||||||
import { FeatureFlagProvider } from "@providers/FeatureFlagProvider";
|
import { FeatureFlagProvider } from "@providers/FeatureFlagProvider";
|
||||||
import { DeviceStatus } from "@routes/welcome-local";
|
|
||||||
import { m } from "@localizations/messages.js";
|
import { m } from "@localizations/messages.js";
|
||||||
|
|
||||||
|
export type AuthMode = "password" | "noPassword" | null;
|
||||||
|
|
||||||
|
interface LocalLoaderResp {
|
||||||
|
authMode: AuthMode;
|
||||||
|
}
|
||||||
|
|
||||||
interface CloudLoaderResp {
|
interface CloudLoaderResp {
|
||||||
deviceName: string;
|
deviceName: string;
|
||||||
user: User | null;
|
user: User | null;
|
||||||
|
|
@ -62,35 +66,20 @@ interface CloudLoaderResp {
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AuthMode = "password" | "noPassword" | null;
|
|
||||||
export interface LocalDevice {
|
export interface LocalDevice {
|
||||||
authMode: AuthMode;
|
authMode: AuthMode;
|
||||||
deviceId: string;
|
deviceId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deviceLoader = async () => {
|
const deviceLoader = async () => {
|
||||||
const res = await api
|
const device = await checkAuth();
|
||||||
.GET(`${DEVICE_API}/device/status`)
|
return { authMode: device.authMode } as LocalLoaderResp;
|
||||||
.then(res => res.json() as Promise<DeviceStatus>);
|
|
||||||
|
|
||||||
if (!res.isSetup) return redirect("/welcome");
|
|
||||||
|
|
||||||
const deviceRes = await api.GET(`${DEVICE_API}/device`);
|
|
||||||
if (deviceRes.status === 401) return redirect("/login-local");
|
|
||||||
if (deviceRes.ok) {
|
|
||||||
const device = (await deviceRes.json()) as LocalDevice;
|
|
||||||
return { authMode: device.authMode };
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("Error fetching device");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const cloudLoader = async (params: Params<string>): Promise<CloudLoaderResp> => {
|
const cloudLoader = async (params: Params<string>): Promise<CloudLoaderResp> => {
|
||||||
const user = await checkAuth();
|
const user = await checkAuth();
|
||||||
|
|
||||||
const iceResp = await api.POST(`${CLOUD_API}/webrtc/ice_config`);
|
const iceResp = await api.POST(`${CLOUD_API}/webrtc/ice_config`);
|
||||||
const iceConfig = await iceResp.json();
|
const iceConfig = await iceResp.json();
|
||||||
|
|
||||||
const deviceResp = await api.GET(`${CLOUD_API}/devices/${params.id}`);
|
const deviceResp = await api.GET(`${CLOUD_API}/devices/${params.id}`);
|
||||||
|
|
||||||
if (!deviceResp.ok) {
|
if (!deviceResp.ok) {
|
||||||
|
|
@ -105,11 +94,11 @@ const cloudLoader = async (params: Params<string>): Promise<CloudLoaderResp> =>
|
||||||
device: { id: string; name: string; user: { googleId: string } };
|
device: { id: string; name: string; user: { googleId: string } };
|
||||||
};
|
};
|
||||||
|
|
||||||
return { user, iceConfig, deviceName: device.name || device.id };
|
return { user, iceConfig, deviceName: device.name || device.id } as CloudLoaderResp;
|
||||||
};
|
};
|
||||||
|
|
||||||
const loader: LoaderFunction = ({ params }: LoaderFunctionArgs) => {
|
const loader: LoaderFunction = ({ params }: LoaderFunctionArgs) => {
|
||||||
return import.meta.env.MODE === "device" ? deviceLoader() : cloudLoader(params);
|
return isOnDevice ? deviceLoader() : cloudLoader(params);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function KvmIdRoute() {
|
export default function KvmIdRoute() {
|
||||||
|
|
@ -185,7 +174,7 @@ export default function KvmIdRoute() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await pc.setRemoteDescription(new RTCSessionDescription(remoteDescription));
|
await pc.setRemoteDescription(new RTCSessionDescription(remoteDescription));
|
||||||
console.log("[setRemoteSessionDescription] Remote description set successfully");
|
console.log("[setRemoteSessionDescription] Remote description set successfully to: " + remoteDescription.sdp);
|
||||||
setLoadingMessage(m.establishing_secure_connection());
|
setLoadingMessage(m.establishing_secure_connection());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
|
|
@ -230,9 +219,14 @@ export default function KvmIdRoute() {
|
||||||
const ignoreOffer = useRef(false);
|
const ignoreOffer = useRef(false);
|
||||||
const isSettingRemoteAnswerPending = useRef(false);
|
const isSettingRemoteAnswerPending = useRef(false);
|
||||||
const makingOffer = useRef(false);
|
const makingOffer = useRef(false);
|
||||||
|
const reconnectAttemptsRef = useRef(2000);
|
||||||
const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||||
|
|
||||||
|
const reconnectInterval = (attempt: number) => {
|
||||||
|
// Exponential backoff with a max of 10 seconds between attempts
|
||||||
|
return Math.min(500 * 2 ** attempt, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
const { sendMessage, getWebSocket } = useWebSocket(
|
const { sendMessage, getWebSocket } = useWebSocket(
|
||||||
isOnDevice
|
isOnDevice
|
||||||
? `${wsProtocol}//${window.location.host}/webrtc/signaling/client`
|
? `${wsProtocol}//${window.location.host}/webrtc/signaling/client`
|
||||||
|
|
@ -240,10 +234,10 @@ export default function KvmIdRoute() {
|
||||||
{
|
{
|
||||||
heartbeat: true,
|
heartbeat: true,
|
||||||
retryOnError: true,
|
retryOnError: true,
|
||||||
reconnectAttempts: 2000,
|
reconnectAttempts: reconnectAttemptsRef.current,
|
||||||
reconnectInterval: 1000,
|
reconnectInterval: reconnectInterval,
|
||||||
onReconnectStop: (numAttempts: number) => {
|
onReconnectStop: (numAttempts: number) => {
|
||||||
console.debug("Reconnect stopped", numAttempts);
|
console.debug("Reconnect stopped after ", numAttempts, "attempts");
|
||||||
cleanupAndStopReconnecting();
|
cleanupAndStopReconnecting();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -261,6 +255,7 @@ export default function KvmIdRoute() {
|
||||||
console.error("[Websocket] onError", event);
|
console.error("[Websocket] onError", event);
|
||||||
// We don't want to close everything down, we wait for the reconnect to stop instead
|
// We don't want to close everything down, we wait for the reconnect to stop instead
|
||||||
},
|
},
|
||||||
|
|
||||||
onOpen() {
|
onOpen() {
|
||||||
console.debug("[Websocket] onOpen");
|
console.debug("[Websocket] onOpen");
|
||||||
// We want to clear the reboot state when the websocket connection is opened
|
// We want to clear the reboot state when the websocket connection is opened
|
||||||
|
|
@ -293,6 +288,7 @@ export default function KvmIdRoute() {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const parsedMessage = JSON.parse(message.data);
|
const parsedMessage = JSON.parse(message.data);
|
||||||
|
|
||||||
if (parsedMessage.type === "device-metadata") {
|
if (parsedMessage.type === "device-metadata") {
|
||||||
const { deviceVersion } = parsedMessage.data;
|
const { deviceVersion } = parsedMessage.data;
|
||||||
console.debug("[Websocket] Received device-metadata message");
|
console.debug("[Websocket] Received device-metadata message");
|
||||||
|
|
@ -309,10 +305,12 @@ export default function KvmIdRoute() {
|
||||||
console.log("[Websocket] Device is using new signaling");
|
console.log("[Websocket] Device is using new signaling");
|
||||||
isLegacySignalingEnabled.current = false;
|
isLegacySignalingEnabled.current = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setupPeerConnection();
|
setupPeerConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!peerConnection) return;
|
if (!peerConnection) return;
|
||||||
|
|
||||||
if (parsedMessage.type === "answer") {
|
if (parsedMessage.type === "answer") {
|
||||||
console.debug("[Websocket] Received answer");
|
console.debug("[Websocket] Received answer");
|
||||||
const readyForOffer =
|
const readyForOffer =
|
||||||
|
|
@ -875,7 +873,7 @@ export default function KvmIdRoute() {
|
||||||
style={{ animationDuration: "500ms" }}
|
style={{ animationDuration: "500ms" }}
|
||||||
className="animate-slideUpFade pointer-events-none absolute inset-0 flex items-center justify-center p-4"
|
className="animate-slideUpFade pointer-events-none absolute inset-0 flex items-center justify-center p-4"
|
||||||
>
|
>
|
||||||
<div className="relative h-full max-h-[720px] w-full max-w-[1280px] rounded-md">
|
<div className="relative h-full max-h-[720px] w-full max-w-7xl rounded-md">
|
||||||
{!!ConnectionStatusElement && ConnectionStatusElement}
|
{!!ConnectionStatusElement && ConnectionStatusElement}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ const loader: LoaderFunction = async ()=> {
|
||||||
return { devices, user };
|
return { devices, user };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
return { devices: [] };
|
return { devices: [], user };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue