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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions src/ui/editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class Editor {
bool ime_font_acquired;
uint32_t ime_cursor_anchor_pos;
bool ime_cursor_anchor_valid;
bool scroll_to_start_pending;
bool scroll_to_end_pending;
bool programmatic_scroll_in_progress;
Comment on lines 56 to +61

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title/description says the scope is limited to webflash/index.html, but this change set also modifies src/ui/editor.h (adds new scroll state flags and event handling). This looks unrelated to the web flasher progress/size fix and makes the PR harder to review; please either remove these changes from this PR or update the PR description/title and rationale accordingly (ideally split into a separate PR).

Copilot uses AI. Check for mistakes.
String current_file;
std::function<void()> on_exit_cb;
std::function<void()> on_save_cb;
Expand All @@ -68,6 +71,7 @@ class Editor {
save_popup(nullptr), save_popup_timer(nullptr),
ime_cand_syncing(false), ime_is_k9_mode(false), ime_cand_count(0), ime_cand_page(0),
ime_visible(false), large_doc_mode(false), read_chunk_mode(false), read_chunk_has_prev(false), read_chunk_has_next(false), ime_font_acquired(false), ime_cursor_anchor_pos(0), ime_cursor_anchor_valid(false),
scroll_to_start_pending(false), scroll_to_end_pending(false), programmatic_scroll_in_progress(false),
current_file(""), on_exit_cb(nullptr), on_save_cb(nullptr), on_read_prev_chunk_cb(nullptr), on_read_next_chunk_cb(nullptr) {
memset(ime_compose, 0, sizeof(ime_compose));
memset(ime_cands, 0, sizeof(ime_cands));
Expand Down Expand Up @@ -206,6 +210,7 @@ class Editor {
lv_obj_set_style_height(textarea, editor_cursor_h, LV_PART_CURSOR);
lv_obj_add_event_cb(textarea, textarea_cursor_anchor_event_cb, LV_EVENT_CLICKED, this);
lv_obj_add_event_cb(textarea, textarea_cursor_anchor_event_cb, LV_EVENT_VALUE_CHANGED, this);
lv_obj_add_event_cb(textarea, textarea_scroll_event_cb, LV_EVENT_SCROLL_BEGIN, this);
lv_obj_add_event_cb(textarea, textarea_scroll_event_cb, LV_EVENT_SCROLL, this);
lv_obj_add_event_cb(textarea, textarea_scroll_event_cb, LV_EVENT_SCROLL_END, this);
ime_cursor_anchor_pos = lv_textarea_get_cursor_pos(textarea);
Expand Down Expand Up @@ -611,6 +616,10 @@ class Editor {
static void textarea_scroll_event_cb(lv_event_t* e) {
Editor* ed = (Editor*)lv_event_get_user_data(e);
if (!ed) return;
if (lv_event_get_code(e) == LV_EVENT_SCROLL_BEGIN && !ed->programmatic_scroll_in_progress) {
ed->scroll_to_start_pending = false;
ed->scroll_to_end_pending = false;
}
ed->refreshReadChunkButtons();
}

Expand Down Expand Up @@ -650,24 +659,34 @@ class Editor {

static void async_scroll_top_cb(void* user_data) {
Editor* ed = (Editor*)user_data;
if (!ed || !ed->textarea) return;
if (!ed || !ed->textarea || !ed->scroll_to_start_pending) return;
ed->scroll_to_start_pending = false;
ed->programmatic_scroll_in_progress = true;
lv_obj_scroll_to_y(ed->textarea, 0, LV_ANIM_OFF);
ed->programmatic_scroll_in_progress = false;
ed->refreshReadChunkButtons();
}

static void async_scroll_bottom_cb(void* user_data) {
Editor* ed = (Editor*)user_data;
if (!ed || !ed->textarea) return;
if (!ed || !ed->textarea || !ed->scroll_to_end_pending) return;
ed->scroll_to_end_pending = false;
ed->programmatic_scroll_in_progress = true;
lv_obj_update_layout(ed->textarea);
int32_t bottom_offset = lv_obj_get_scroll_bottom(ed->textarea);
lv_obj_scroll_by(ed->textarea, 0, bottom_offset, LV_ANIM_OFF);
ed->programmatic_scroll_in_progress = false;
ed->refreshReadChunkButtons();
}

void moveCursorAndViewToStart() {
if (!textarea) return;
lv_textarea_set_cursor_pos(textarea, 0);
scroll_to_start_pending = true;
scroll_to_end_pending = false;
programmatic_scroll_in_progress = true;
lv_obj_scroll_to_y(textarea, 0, LV_ANIM_OFF);
programmatic_scroll_in_progress = false;
ime_cursor_anchor_pos = 0;
ime_cursor_anchor_valid = true;
lv_async_call(async_scroll_top_cb, this);
Expand All @@ -678,6 +697,8 @@ class Editor {
const char* text = lv_textarea_get_text(textarea);
uint32_t len = text ? (uint32_t)strlen(text) : 0;
lv_textarea_set_cursor_pos(textarea, (int32_t)len);
scroll_to_end_pending = true;
scroll_to_start_pending = false;
ime_cursor_anchor_pos = len;
ime_cursor_anchor_valid = true;
lv_async_call(async_scroll_bottom_cb, this);
Expand Down
33 changes: 29 additions & 4 deletions webflash/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,16 @@ <h2>Log</h2>
return `${Math.round(size / 1024)} KB`;
}

function getFlashFileSize(file) {
const sizeBytes = file?.sizeBytes ?? file?.data?.length;
const size = Number(sizeBytes || 0);
return Number.isFinite(size) && size > 0 ? size : 0;
}

function getTotalFlashSize(files) {
return files.reduce((total, file) => total + getFlashFileSize(file), 0);
}

function formatTimestamp(value) {
const date = value ? new Date(value) : null;
if (!date || Number.isNaN(date.getTime())) {
Expand Down Expand Up @@ -982,12 +992,27 @@ <h2>Log</h2>
}

function updateFlashProgress(files, fileIndex, writtenBytes, totalBytes) {
const percentPerFile = 100 / files.length;
const percent = Math.min(100, Math.round((fileIndex * percentPerFile) + ((writtenBytes / totalBytes) * percentPerFile)));
const fileName = files[fileIndex]?.name || `part ${fileIndex + 1}`;
const totalFlashBytes = getTotalFlashSize(files);
const completedFlashBytes = files.slice(0, fileIndex).reduce((total, file) => total + getFlashFileSize(file), 0);
const currentFileSize = getFlashFileSize(files[fileIndex]);
let currentWrittenBytes = 0;
if (currentFileSize > 0 && totalBytes > 0) {
const transferProgressRatio = writtenBytes / totalBytes;
const currentRatio = Math.min(1, Math.max(0, transferProgressRatio));
currentWrittenBytes = Math.round(currentFileSize * currentRatio);
} else if (currentFileSize > 0) {
currentWrittenBytes = Math.min(currentFileSize, Math.max(0, writtenBytes));
}
const overallWrittenBytes = completedFlashBytes + currentWrittenBytes;
// Prefer the known total firmware bytes, then the current file size, then the loader-reported total.
const displayTotalBytes = totalFlashBytes || currentFileSize || Math.max(0, totalBytes);
const percent = totalFlashBytes > 0
? Math.min(100, Math.round((overallWrittenBytes / totalFlashBytes) * 100))
: 0;
flashProgress.hidden = false;
flashProgress.value = percent;
flashStatus.textContent = `Writing ${fileName}... ${percent}% (${Math.round(writtenBytes / 1024)} KB / ${Math.round(totalBytes / 1024)} KB transferred)`;
flashStatus.textContent = `Writing ${fileName}... ${percent}% (${formatSize(overallWrittenBytes)} / ${formatSize(displayTotalBytes)})`;
}

function updateRestoreProgress(target, writtenBytes, totalBytes) {
Expand Down Expand Up @@ -1136,7 +1161,7 @@ <h2>Log</h2>
setFlashStatus(`Downloading ${partFileName} (${index + 1}/${parts.length})...`, false, "wait");
data = await downloadPartData(part.path, partFileName);
}
files.push({ name: partFileName, data, address: part.offset });
files.push({ name: partFileName, data, address: part.offset, sizeBytes: data.length });
if (!localFile) {
logLine(`Downloaded ${partFileName} (${formatSize(data.length)}).`, "info", "flash");
}
Expand Down
Loading