Skip to content

feat: expand preference editor and add settings reference#362

Draft
meganetaaan wants to merge 1 commit intodev/v1.0from
codex/issue-305-preference-docs-ui
Draft

feat: expand preference editor and add settings reference#362
meganetaaan wants to merge 1 commit intodev/v1.0from
codex/issue-305-preference-docs-ui

Conversation

@meganetaaan
Copy link
Copy Markdown
Collaborator

@meganetaaan meganetaaan commented Feb 28, 2026

Summary by CodeRabbit

  • Documentation

    • Added comprehensive Preference Settings Reference guides detailing preference keys, configuration categories, and usage instructions
    • Updated flashing documentation with preference override information
  • New Features

    • Enhanced Preference Editor with improved form organization, toast notifications for success/error feedback, and dynamic field visibility
  • Bug Fixes

    • Improved servo driver preference compatibility handling

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

📝 Walkthrough

Walkthrough

This PR introduces comprehensive preference documentation (English and Japanese) detailing preference keys and usage patterns. It extends firmware preference keys for driver and TTS domains, adds defensive aliasing in driver preference loading, and significantly refactors the web preference editor UI with field validation, conditional visibility, and toast notifications.

Changes

Cohort / File(s) Summary
Documentation - Preferences Reference
firmware/docs/preferences.md, firmware/docs/preferences_ja.md
New comprehensive preference documentation detailing Wi-Fi, Renderer, Driver, TTS, and AI preference keys with types, descriptions, and example values; includes preference resolution order and web app usage guidance.
Documentation - Existing Files
firmware/docs/flashing-firmware.md, firmware/docs/flashing-firmware_ja.md, firmware/docs/setting-preferences-web.md, firmware/docs/setting-preferences-web_ja.md
Added cross-references to preference documentation and notes clarifying how non-empty form fields override existing values; minor typography fixes in Japanese version.
Firmware - Preference System
firmware/stackchan/utilities/consts.ts, firmware/stackchan/main.ts
Expanded PREF_KEYS with new driver domain keys (panId, tiltId, pwmPan, pwmTilt, baud) and TTS domain keys (voice, speakerId, model, speed, instructions); added defensive aliasing to prefer baud over legacy baudrate during driver preference loading.
Web - Preference Editor UI
web/preference/index.html
Restructured form into logical sections (Wi-Fi, Renderer, Driver, TTS, AI); implemented FIELD_DEFINITIONS for centralized field metadata; added toast notifications for success/error feedback; introduced payload collection with validation for numeric fields; added dynamic field visibility logic driven by TTS and driver type selections; refined form state management tied to BLE connection status.
Web - Preference Editor Styles
web/preference/preference.css
Added styles for form sections, descriptions, conditional field visibility, toast notifications (info/error variants), and responsive layout adjustments for smaller screens.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

  • load preference from mod #328: Touches preference loading in firmware/stackchan by extracting/modifying loadPreferences and its imports; this PR extends PREF_KEYS and adds defensive aliasing in the same area.

Suggested reviewers

  • sskw-ugo

Poem

🐰 Preferences documented clear,
With fields aligned and form sincere,
Toast messages glow like morning dew,
Conditional visibility shines on cue,
Stack-chan's settings now pristine!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: expand preference editor and add settings reference' accurately reflects the main changes: new preference documentation files, web UI enhancements, and settings reference additions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/issue-305-preference-docs-ui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@meganetaaan
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review plz

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

@meganetaaan: I'll review the changes in this PR for you.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
web/preference/index.html (1)

126-130: Handle disconnect failures explicitly.

Line 127 can reject and currently leaves an unhandled async error path.

💡 Proposed fix
       const handleDisconnectButtonClick = async () => {
-        await client.disconnect()
-        updateView()
-        showToast('Disconnected', 'info')
+        try {
+          await client.disconnect()
+          showToast('Disconnected', 'info')
+        } catch (error) {
+          console.error(error)
+          showToast(`Disconnect failed: ${String(error)}`, 'error')
+        } finally {
+          updateView()
+        }
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/preference/index.html` around lines 126 - 130, The async handler
handleDisconnectButtonClick calls client.disconnect() without catching
rejections; wrap the await in a try/catch so failures are handled: inside
handleDisconnectButtonClick, try awaiting client.disconnect(), then call
updateView() and showToast('Disconnected','info') on success; in the catch block
log the error (e.g., console.error or a logger) and call showToast with an error
message (include error.message) so the UI reflects the failure and no unhandled
rejection occurs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@firmware/docs/setting-preferences-web_ja.md`:
- Around line 39-40: Replace the wording that implies "消去" with phrasing that
clearly indicates "上書き": update the sentence
"既存値を消したい場合は、消去対象を明示的に設定したうえで再起動確認してください。" to explicitly use "上書き" (for example
"既存値を上書きしたい場合は、上書き対象を明示的に設定したうえで再起動を確認してください。") and ensure the preceding line
still states that empty fields are not sent so the intent to overwrite (上書き) is
unambiguous.

In `@web/preference/index.html`:
- Around line 60-63: The UI toggles visibility via setVisible(selector, visible)
but hidden fields remain enabled and are still collected/submitted; update
setVisible to also disable/enable all form controls inside the selector (e.g.,
input, select, textarea, button) when toggling visibility so hidden groups
cannot be submitted, and ensure the form-collection logic skips elements that
are disabled or whose container has the 'hidden' class (i.e., when gathering
values check element.disabled or container.classList.contains('hidden') so
token/driver fields are never sent while hidden).
- Around line 151-153: The finally block currently always sets
submitButton.disabled = false which can re-enable the UI even if Bluetooth
disconnected; change the flow so the button is only re-enabled when the BLE
client is actually connected (e.g., check the connection flag or call
client.connected/isConnected before setting submitButton.disabled = false)
and/or move the re-enable into the success path after client.send; additionally
hook the BLE disconnect event (or client.ondisconnect) to explicitly disable
submitButton when the connection drops so submit-button state stays in sync with
the real connection state (refer to submitButton, client.send, and the finally
block).

---

Nitpick comments:
In `@web/preference/index.html`:
- Around line 126-130: The async handler handleDisconnectButtonClick calls
client.disconnect() without catching rejections; wrap the await in a try/catch
so failures are handled: inside handleDisconnectButtonClick, try awaiting
client.disconnect(), then call updateView() and showToast('Disconnected','info')
on success; in the catch block log the error (e.g., console.error or a logger)
and call showToast with an error message (include error.message) so the UI
reflects the failure and no unhandled rejection occurs.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f61949 and d9ca604.

📒 Files selected for processing (10)
  • firmware/docs/flashing-firmware.md
  • firmware/docs/flashing-firmware_ja.md
  • firmware/docs/preferences.md
  • firmware/docs/preferences_ja.md
  • firmware/docs/setting-preferences-web.md
  • firmware/docs/setting-preferences-web_ja.md
  • firmware/stackchan/main.ts
  • firmware/stackchan/utilities/consts.ts
  • web/preference/index.html
  • web/preference/preference.css

Comment on lines +39 to +40
> Web 設定アプリでは、空欄の項目は送信されません。
> 既存値を消したい場合は、消去対象を明示的に設定したうえで再起動確認してください。
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

「消去」ではなく「上書き」に寄せた表現にしたほうが正確です。

現在の文言だと値の削除が可能に読めるため、上書き意図を明確化するのが安全です。

✏️ Proposed wording fix
-> 既存値を消したい場合は、消去対象を明示的に設定したうえで再起動確認してください。
+> 既存値を上書きしたい場合は、置き換え値を明示的に設定し、再起動後に反映を確認してください。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@firmware/docs/setting-preferences-web_ja.md` around lines 39 - 40, Replace
the wording that implies "消去" with phrasing that clearly indicates "上書き": update
the sentence "既存値を消したい場合は、消去対象を明示的に設定したうえで再起動確認してください。" to explicitly use "上書き"
(for example "既存値を上書きしたい場合は、上書き対象を明示的に設定したうえで再起動を確認してください。") and ensure the
preceding line still states that empty fields are not sent so the intent to
overwrite (上書き) is unambiguous.

Comment thread web/preference/index.html
Comment on lines +60 to +63
const setVisible = (selector, visible) => {
document.querySelectorAll(selector).forEach((element) => {
element.classList.toggle('hidden', !visible)
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hidden conditional fields are still being submitted.

Line 60 only toggles visibility, but Line 93 still collects all non-empty inputs. This can resend stale values from hidden TTS/driver groups (including token fields) unintentionally.

💡 Proposed fix
 const setVisible = (selector, visible) => {
   document.querySelectorAll(selector).forEach((element) => {
     element.classList.toggle('hidden', !visible)
+    element.querySelectorAll('input, select, textarea').forEach((control) => {
+      control.disabled = !visible
+    })
   })
 }

 const collectPayload = () => {
   const payload = {}
   for (const field of FIELD_DEFINITIONS) {
     const element = document.getElementById(field.id)
-    if (element == null) {
+    if (element == null || element.disabled) {
       continue
     }

Also applies to: 91-111

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/preference/index.html` around lines 60 - 63, The UI toggles visibility
via setVisible(selector, visible) but hidden fields remain enabled and are still
collected/submitted; update setVisible to also disable/enable all form controls
inside the selector (e.g., input, select, textarea, button) when toggling
visibility so hidden groups cannot be submitted, and ensure the form-collection
logic skips elements that are disabled or whose container has the 'hidden' class
(i.e., when gathering values check element.disabled or
container.classList.contains('hidden') so token/driver fields are never sent
while hidden).

Comment thread web/preference/index.html
Comment on lines +151 to 153
} finally {
submitButton.disabled = false
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Keep submit-button state synced with BLE connection state.

Line 152 always enables the button. If disconnect happens during client.send, the UI can end up enabled while disconnected.

💡 Proposed fix
         } catch (error) {
           console.error(error)
           showToast(`Submit failed: ${String(error)}`, 'error')
         } finally {
-          submitButton.disabled = false
+          updateView()
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} finally {
submitButton.disabled = false
}
} catch (error) {
console.error(error)
showToast(`Submit failed: ${String(error)}`, 'error')
} finally {
updateView()
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/preference/index.html` around lines 151 - 153, The finally block
currently always sets submitButton.disabled = false which can re-enable the UI
even if Bluetooth disconnected; change the flow so the button is only re-enabled
when the BLE client is actually connected (e.g., check the connection flag or
call client.connected/isConnected before setting submitButton.disabled = false)
and/or move the re-enable into the success path after client.send; additionally
hook the BLE disconnect event (or client.ondisconnect) to explicitly disable
submitButton when the connection drops so submit-button state stays in sync with
the real connection state (refer to submitButton, client.send, and the finally
block).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant