Compare commits

...

5 Commits

Author SHA1 Message Date
CaptainFrito
5f1e4b785d
Merge af7c0fef9d into d403044f76 2026-02-03 22:29:51 +07:00
Aaron Cunliffe
d403044f76
fix: Increase network SSID display length (#670)
Some checks are pending
CI / build (push) Waiting to run
## Rationale 

I have 2 wifi access points with almost identical names, just one has
`_EXT` at the end of it. With the current display limit of 13 characters
before adding ellipsis, I can't tell which is which.

Before device screenshot with masked SSIDs:
<img
src="https://github.com/user-attachments/assets/3c5cbbaa-b2f6-412f-b5a8-6278963bd0f2"
width="300">


## Summary

Adjusted displayed length from 13 characters to 30 in the Wifi selection
screen - I've left some space for potential proportional font changes in
the future

After image with masked SSIDs:
<img
src="https://github.com/user-attachments/assets/c5f0712b-bbd3-4eec-9820-4693fae90c9f"
width="300">

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**< NO >**_
2026-02-03 18:24:23 +03:00
CaptainFrito
af7c0fef9d Lyra as default theme 2026-02-03 19:47:16 +07:00
CaptainFrito
e5ac4354a4 Removed useless bools in components 2026-02-03 19:19:39 +07:00
Aaron Cunliffe
f67c544e16
fix: webserver folder creation regex change (#653)
Some checks failed
CI / build (push) Has been cancelled
## Summary

Resolves #562 

Implements regex change to support valid characters discussed by
@daveallie in issue
[here](https://github.com/crosspoint-reader/crosspoint-reader/issues/562#issuecomment-3830809156).

Also rejects `.` and `..` as folder names which are invalid in FAT32 and
exFAT filesystems

## Additional Context
- Unsure on the wording for the alert, it feels overly explicit, but
that might be a good thing. Happy to change.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**< PARTIALLY >**_
2026-02-02 21:27:02 +11:00
13 changed files with 40 additions and 38 deletions

View File

@ -153,7 +153,7 @@ Click **File Manager** to access file management features.
1. Click the **+ Add** button in the top-right corner
2. Select **New Folder** from the dropdown menu
3. Enter a folder name (letters, numbers, underscores, and hyphens only)
3. Enter a folder name (must not contain characters \" * : < > ? / \\ | and must not be . or ..)
4. Click **Create Folder**
This is useful for organizing your ebooks by genre, author, or series.

@ -1 +1 @@
Subproject commit bd4e6707503ab9c97d13ee0d8f8c69e9ff03cd12
Subproject commit c8ce3949b3368329c290ca1858e65dc3416fc591

View File

@ -141,7 +141,7 @@ class CrossPointSettings {
// Long-press chapter skip on side buttons
uint8_t longPressChapterSkip = 1;
// UI Theme
uint8_t uiTheme = CLASSIC;
uint8_t uiTheme = LYRA;
~CrossPointSettings() = default;

View File

@ -277,7 +277,7 @@ void HomeActivity::render() {
pageHeight - (metrics.headerHeight + metrics.homeTopPadding + metrics.verticalSpacing * 2 +
metrics.buttonHintsHeight)},
static_cast<int>(menuItems.size()), selectorIndex - recentBooks.size(),
[&menuItems](int index) { return std::string(menuItems[index]); }, false, nullptr);
[&menuItems](int index) { return std::string(menuItems[index]); }, nullptr);
const auto labels = mappedInput.mapLabels("", "Select", "Up", "Down");
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);

View File

@ -201,7 +201,7 @@ void MyLibraryActivity::render() const {
} else {
GUI.drawList(
renderer, Rect{0, contentTop, pageWidth, contentHeight}, files.size(), selectorIndex,
[this](int index) { return files[index]; }, false, nullptr, false, nullptr, false, nullptr);
[this](int index) { return files[index]; }, nullptr, nullptr, nullptr);
}
// Help text

View File

@ -137,8 +137,8 @@ void RecentBooksActivity::render() const {
} else {
GUI.drawList(
renderer, Rect{0, contentTop, pageWidth, contentHeight}, recentBooks.size(), selectorIndex,
[this](int index) { return recentBooks[index].title; }, true,
[this](int index) { return recentBooks[index].author; }, false, nullptr, false, nullptr);
[this](int index) { return recentBooks[index].title; }, [this](int index) { return recentBooks[index].author; },
nullptr, nullptr);
}
// Help text

View File

@ -547,8 +547,8 @@ void WifiSelectionActivity::renderNetworkList() const {
// Draw network name (truncate if too long)
std::string displayName = network.ssid;
if (displayName.length() > 16) {
displayName.replace(13, displayName.length() - 13, "...");
if (displayName.length() > 33) {
displayName.replace(30, displayName.length() - 30, "...");
}
renderer.drawText(UI_10_FONT_ID, 20, networkY, displayName.c_str());

View File

@ -277,7 +277,7 @@ void SettingsActivity::render() const {
pageHeight - (metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.buttonHintsHeight +
metrics.verticalSpacing * 2)},
settingsCount, selectedSettingIndex - 1, [this](int index) { return std::string(settingsList[index].name); },
false, nullptr, false, nullptr, true,
nullptr, nullptr,
[this](int i) {
const auto& setting = settingsList[i];
std::string valueText = "";

View File

@ -158,11 +158,12 @@ void BaseTheme::drawSideButtonHints(const GfxRenderer& renderer, const char* top
}
void BaseTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex,
const std::function<std::string(int index)>& rowTitle, bool hasSubtitle,
const std::function<std::string(int index)>& rowSubtitle, bool hasIcon,
const std::function<std::string(int index)>& rowIcon, bool hasValue,
const std::function<std::string(int index)>& rowTitle,
const std::function<std::string(int index)>& rowSubtitle,
const std::function<std::string(int index)>& rowIcon,
const std::function<std::string(int index)>& rowValue) const {
int rowHeight = hasSubtitle ? BaseMetrics::values.listWithSubtitleRowHeight : BaseMetrics::values.listRowHeight;
int rowHeight =
(rowSubtitle != nullptr) ? BaseMetrics::values.listWithSubtitleRowHeight : BaseMetrics::values.listRowHeight;
int pageItems = rect.height / rowHeight;
const int totalPages = (itemCount + pageItems - 1) / pageItems;
@ -200,15 +201,15 @@ void BaseTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount,
const auto pageStartIndex = selectedIndex / pageItems * pageItems;
for (int i = pageStartIndex; i < itemCount && i < pageStartIndex + pageItems; i++) {
const int itemY = rect.y + (i % pageItems) * rowHeight;
int textWidth = contentWidth - BaseMetrics::values.contentSidePadding * 2 - (hasValue ? 60 : 0);
int textWidth = contentWidth - BaseMetrics::values.contentSidePadding * 2 - (rowValue != nullptr ? 60 : 0);
// Draw name
auto itemName = rowTitle(i);
auto font = hasSubtitle ? UI_12_FONT_ID : UI_10_FONT_ID;
auto font = (rowSubtitle != nullptr) ? UI_12_FONT_ID : UI_10_FONT_ID;
auto item = renderer.truncatedText(font, itemName.c_str(), textWidth);
renderer.drawText(font, rect.x + BaseMetrics::values.contentSidePadding, itemY, item.c_str(), i != selectedIndex);
if (hasSubtitle) {
if (rowSubtitle != nullptr) {
// Draw subtitle
std::string subtitleText = rowSubtitle(i);
auto subtitle = renderer.truncatedText(UI_10_FONT_ID, subtitleText.c_str(), textWidth);
@ -216,7 +217,7 @@ void BaseTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount,
i != selectedIndex);
}
if (hasValue) {
if (rowValue != nullptr) {
// Draw value
std::string valueText = rowValue(i);
const auto valueTextWidth = renderer.getTextWidth(UI_10_FONT_ID, valueText.c_str());
@ -570,7 +571,7 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
}
void BaseTheme::drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex,
const std::function<std::string(int index)>& buttonLabel, bool hasIcon,
const std::function<std::string(int index)>& buttonLabel,
const std::function<std::string(int index)>& rowIcon) const {
for (int i = 0; i < buttonCount; ++i) {
const int tileY = BaseMetrics::values.verticalSpacing + rect.y +

View File

@ -98,9 +98,9 @@ class BaseTheme {
const char* btn4) const;
virtual void drawSideButtonHints(const GfxRenderer& renderer, const char* topBtn, const char* bottomBtn) const;
virtual void drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex,
const std::function<std::string(int index)>& rowTitle, bool hasSubtitle,
const std::function<std::string(int index)>& rowSubtitle, bool hasIcon,
const std::function<std::string(int index)>& rowIcon, bool hasValue,
const std::function<std::string(int index)>& rowTitle,
const std::function<std::string(int index)>& rowSubtitle,
const std::function<std::string(int index)>& rowIcon,
const std::function<std::string(int index)>& rowValue) const;
virtual void drawHeader(const GfxRenderer& renderer, Rect rect, const char* title) const;
@ -110,7 +110,7 @@ class BaseTheme {
const int selectorIndex, bool& coverRendered, bool& coverBufferStored,
bool& bufferRestored, std::function<bool()> storeCoverBuffer) const;
virtual void drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex,
const std::function<std::string(int index)>& buttonLabel, bool hasIcon,
const std::function<std::string(int index)>& buttonLabel,
const std::function<std::string(int index)>& rowIcon) const;
virtual Rect drawPopup(const GfxRenderer& renderer, const char* message) const;
virtual void fillPopupProgress(const GfxRenderer& renderer, const Rect& layout, const int progress) const;

View File

@ -115,11 +115,12 @@ void LyraTheme::drawTabBar(const GfxRenderer& renderer, Rect rect, const std::ve
}
void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex,
const std::function<std::string(int index)>& rowTitle, bool hasSubtitle,
const std::function<std::string(int index)>& rowSubtitle, bool hasIcon,
const std::function<std::string(int index)>& rowIcon, bool hasValue,
const std::function<std::string(int index)>& rowTitle,
const std::function<std::string(int index)>& rowSubtitle,
const std::function<std::string(int index)>& rowIcon,
const std::function<std::string(int index)>& rowValue) const {
int rowHeight = hasSubtitle ? LyraMetrics::values.listWithSubtitleRowHeight : LyraMetrics::values.listRowHeight;
int rowHeight =
(rowSubtitle != nullptr) ? LyraMetrics::values.listWithSubtitleRowHeight : LyraMetrics::values.listRowHeight;
int pageItems = rect.height / rowHeight;
const int totalPages = (itemCount + pageItems - 1) / pageItems;
@ -153,13 +154,13 @@ void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount,
// Draw name
int textWidth = contentWidth - LyraMetrics::values.contentSidePadding * 2 - hPaddingInSelection * 2 -
(hasValue ? 60 : 0); // TODO truncate according to value width?
(rowValue != nullptr ? 60 : 0); // TODO truncate according to value width?
auto itemName = rowTitle(i);
auto item = renderer.truncatedText(UI_10_FONT_ID, itemName.c_str(), textWidth);
renderer.drawText(UI_10_FONT_ID, rect.x + LyraMetrics::values.contentSidePadding + hPaddingInSelection * 2,
itemY + 6, item.c_str(), true);
if (hasSubtitle) {
if (rowSubtitle != nullptr) {
// Draw subtitle
std::string subtitleText = rowSubtitle(i);
auto subtitle = renderer.truncatedText(SMALL_FONT_ID, subtitleText.c_str(), textWidth);
@ -167,7 +168,7 @@ void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount,
itemY + 30, subtitle.c_str(), true);
}
if (hasValue) {
if (rowValue != nullptr) {
// Draw value
std::string valueText = rowValue(i);
if (!valueText.empty()) {
@ -329,7 +330,7 @@ void LyraTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
}
void LyraTheme::drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex,
const std::function<std::string(int index)>& buttonLabel, bool hasIcon,
const std::function<std::string(int index)>& buttonLabel,
const std::function<std::string(int index)>& rowIcon) const {
for (int i = 0; i < buttonCount; ++i) {
int tileWidth = (rect.width - LyraMetrics::values.contentSidePadding * 2 - LyraMetrics::values.menuSpacing) / 2;

View File

@ -41,15 +41,15 @@ class LyraTheme : public BaseTheme {
void drawTabBar(const GfxRenderer& renderer, Rect rect, const std::vector<TabInfo>& tabs,
bool selected) const override;
void drawList(const GfxRenderer& renderer, Rect rect, int itemCount, int selectedIndex,
const std::function<std::string(int index)>& rowTitle, bool hasSubtitle,
const std::function<std::string(int index)>& rowSubtitle, bool hasIcon,
const std::function<std::string(int index)>& rowIcon, bool hasValue,
const std::function<std::string(int index)>& rowTitle,
const std::function<std::string(int index)>& rowSubtitle,
const std::function<std::string(int index)>& rowIcon,
const std::function<std::string(int index)>& rowValue) const override;
void drawButtonHints(GfxRenderer& renderer, const char* btn1, const char* btn2, const char* btn3,
const char* btn4) const override;
void drawSideButtonHints(const GfxRenderer& renderer, const char* topBtn, const char* bottomBtn) const override;
void drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount, int selectedIndex,
const std::function<std::string(int index)>& buttonLabel, bool hasIcon,
const std::function<std::string(int index)>& buttonLabel,
const std::function<std::string(int index)>& rowIcon) const override;
void drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std::vector<RecentBook>& recentBooks,
const int selectorIndex, bool& coverRendered, bool& coverBufferStored, bool& bufferRestored,

View File

@ -1146,10 +1146,10 @@ function retryAllFailedUploads() {
return;
}
// Validate folder name (no special characters except underscore and hyphen)
const validName = /^[a-zA-Z0-9_\-]+$/.test(folderName);
// Validate folder name
const validName = /^(?!\.{1,2}$)[^"*:<>?\/\\|]+$/.test(folderName);
if (!validName) {
alert('Folder name can only contain letters, numbers, underscores, and hyphens.');
alert('Folder name cannot contain \" * : < > ? / \\ | and must not be . or ..');
return;
}