Skip to content

feat(core,ui): add configurable Bootstrap text and JTabView component - #624

Merged
JasonXuDeveloper merged 7 commits into
masterfrom
feat/configurable-bootstrap-text
Feb 6, 2026
Merged

feat(core,ui): add configurable Bootstrap text and JTabView component#624
JasonXuDeveloper merged 7 commits into
masterfrom
feat/configurable-bootstrap-text

Conversation

@JasonXuDeveloper

@JasonXuDeveloperJasonXuDeveloper commented Feb 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add [Serializable] struct BootstrapText with all 33 user-facing strings as configurable fields (default = current English text)
  • Replace all hardcoded strings in Bootstrap.cs with text.* field references, enabling per-project localization/branding
  • Add JTabView tabbed container component with configurable maxTabsPerRow (rounded button-style tabs, 3x2 grid layout)
  • Refactor Text Settings section in BootstrapEditorUI from flat sub-headers into 6 categorized tabs: Package Init, Scene Load, Inline Status, Dialog Titles, Dialog Buttons, Dialog Content
  • Add 46 new unit tests across 8 editor UI components (JButton, JCard, JObjectField, JTextField, JDropdown, JStack, JTabView, BootstrapEditorUI) using reflection for private event handlers
  • Update GitHub instructions to require 93%+ test coverage for non-core packages, with support for both EditMode and non-interactive PlayMode tests

Closes#622

Test plan

  • Unity compiles without errors
  • New Bootstrap component shows Text Settings with 6 tabbed categories
  • Tab view displays as 3x2 grid with rounded tab buttons
  • Clicking tabs switches content panels correctly
  • Customizing a text field (e.g. initializing to Chinese) displays correctly at runtime
  • Format strings (dialogDownloadPrompt with {0}, {1}) resolve correctly
  • "Reset to Defaults" button restores all fields to English defaults
  • Text Settings visible in both default and enhanced (JEngine.UI) inspectors
  • All unit tests pass in Unity Test Runner

🤖 Generated with Claude Code

Add a serializable BootstrapText struct that externalizes all ~30
user-facing strings from Bootstrap, enabling per-project localization
and branding without modifying framework code.
Closes#622
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net>

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a configurable BootstrapText struct to make all user-facing Bootstrap strings editable per project (localization/branding), and wires those settings into both the default Core inspector and the enhanced JEngine.UI inspector, with basic editor tests.

Changes:

  • Introduces BootstrapText (serializable) with default English strings and format-string tooltips.
  • Updates Bootstrap to use text.* instead of hardcoded UI/status/dialog strings.
  • Adds a “Text Settings” inspector section + “Reset to Defaults” button in both inspectors, plus UI tests for section presence.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 9 comments.

Show a summary per file
FileDescription
UnityProject/Packages/com.jasonxudeveloper.jengine.core/Runtime/BootstrapText.csNew serializable struct holding all user-facing Bootstrap strings + defaults.
UnityProject/Packages/com.jasonxudeveloper.jengine.core/Runtime/Bootstrap.csReplaces hardcoded strings with configurable BootstrapText fields and adds default reset.
UnityProject/Packages/com.jasonxudeveloper.jengine.core/Editor/CustomEditor/BootstrapEditor.csAdds “Text Settings” group and reset-to-defaults logic to the default inspector.
UnityProject/Packages/com.jasonxudeveloper.jengine.ui/Editor/Internal/BootstrapEditorUI.csAdds enhanced “Text Settings” section + reset button to the JEngine.UI inspector.
UnityProject/Packages/com.jasonxudeveloper.jengine.ui/Tests/Editor/Internal/BootstrapEditorUITests.csAdds tests asserting the new Text Settings section/controls exist.
UnityProject/Packages/com.jasonxudeveloper.jengine.core/Runtime/BootstrapText.cs.metaNew Unity meta for the added runtime file.

@github-actions

github-actionsBot commented Feb 6, 2026

Copy link
Copy Markdown

Unity Test Results

EditMode: All tests passed
PlayMode: All tests passed

Unity Version: 2022.3.55f1
Project Path: UnityProject

✅ All tests passed! The PR is ready for review.

View workflow run

Click here to view the full workflow run

@codecov

codecovBot commented Feb 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.84543% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.91%. Comparing base (2ea9238) to head (afb0034).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

@@ Coverage Diff @@## master #624 +/- ##
==========================================
+ Coverage 93.08% 94.91% +1.83% 
==========================================
Files 68 70 +2 Lines 9480 10431 +951 ==========================================
+ Hits 8824 9901 +1077 + Misses 656 530 -126 
FlagCoverage Δ
ui95.35% <96.84%> (+2.31%)⬆️
util93.19% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

- Add BootstrapText.SafeFormat helper with try/catch fallback for
malformed user-edited format strings
- Replace all string.Format calls with SafeFormat in Bootstrap.cs
- Use nameof(BootstrapText.*) instead of string literals in editor UI
- Use BindProperty for JTextField binding (proper undo/prefab support)
- Add tests for SafeFormat, Default field validation, exact field count,
sub-headers, and reset logic
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net>
github-actions[bot]
github-actionsBot previously approved these changes Feb 6, 2026

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Auto-approved: Copilot review found no issues and Unity Tests passed (or were skipped for non-code changes).

JasonXuDeveloperand others added 2 commits February 7, 2026 08:59
- Add JTabView tabbed container component with configurable maxTabsPerRow
- Refactor BootstrapEditorUI Text Settings into 6 categorized tabs (3x2 grid)
- Add 46 new tests across 8 components using reflection for private methods
(JButton, JCard, JObjectField, JTextField, JDropdown, JStack, JTabView,
BootstrapEditorUI)
- Update GitHub instructions to require 93%+ coverage for non-core packages
with support for both EditMode and non-interactive PlayMode tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net>
CopilotAI review requested due to automatic review settings February 6, 2026 22:00
@github-actionsgithub-actionsBot added the documentation Improvements or additions to documentation label Feb 6, 2026
@JasonXuDeveloperJasonXuDeveloper changed the title feat(core): add configurable Bootstrap text via BootstrapText structfeat(core,ui): add configurable Bootstrap text and JTabView componentFeb 6, 2026
github-actions[bot]
github-actionsBot previously approved these changes Feb 6, 2026

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Auto-approved: Copilot review found no issues and Unity Tests passed (or were skipped for non-code changes).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net>
github-actions[bot]
github-actionsBot previously approved these changes Feb 6, 2026

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Auto-approved: Copilot review found no issues and Unity Tests passed (or were skipped for non-code changes).

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 27 out of 35 changed files in this pull request and generated 5 comments.

- Add null guard to BootstrapText.SafeFormat for null templates
- Make JTabView._maxTabsPerRow readonly
- Use evt.currentTarget instead of evt.target in JTabView handlers
- Add SafeFormat null template test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net>
github-actions[bot]
github-actionsBot previously approved these changes Feb 6, 2026

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Auto-approved: Copilot review found no issues and Unity Tests passed (or were skipped for non-code changes).

The currentTarget setter is internal in UIElements. Use reflection
with a null check so tests gracefully skip if the API changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: JasonXuDeveloper - 傑 <jason@xgamedev.net>
CopilotAI review requested due to automatic review settings February 6, 2026 22:13

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Auto-approved: Copilot review found no issues and Unity Tests passed (or were skipped for non-code changes).

@JasonXuDeveloper
JasonXuDeveloper merged commit e9c7bf7 into masterFeb 6, 2026
26 checks passed
@JasonXuDeveloper
JasonXuDeveloper deleted the feat/configurable-bootstrap-text branch February 6, 2026 22:20

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 27 out of 35 changed files in this pull request and generated 7 comments.

Comment on lines +124 to +132
// Constrain tabs per row via percentage width
if (_maxTabsPerRow > 0)
{
// Reduce basis to account for per-tab margins; flexGrow fills remaining space
var percent = (100f - (_maxTabsPerRow * 2f)) / _maxTabsPerRow;
tabButton.style.flexBasis = new StyleLength(new Length(percent, LengthUnit.Percent));
tabButton.style.flexGrow = 1;
tabButton.style.flexShrink = 1;
}

CopilotAIFeb 6, 2026

Copy link

Choose a reason for hiding this comment

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

The flexBasis calculation for _maxTabsPerRow subtracts (_maxTabsPerRow * 2f) from 100f (percent units) to “account for margins”, but the margins are set in pixels (Tokens.Spacing.Xs). This mixes units and will behave inconsistently across inspector widths. Consider using a pure percentage basis (100f / _maxTabsPerRow) and let pixel margins naturally reduce available space (or compute basis without assuming percent-based margins).

Copilot uses AI. Check for mistakes.
Comment on lines +494 to +501
var dialogContentTab = new VisualElement();
AddTextField(dialogContentTab, textProperty, nameof(BootstrapText.dialogInitFailed), "Init Failed");
AddTextField(dialogContentTab, textProperty, nameof(BootstrapText.dialogDownloadPrompt), "Download Prompt");
AddTextField(dialogContentTab, textProperty, nameof(BootstrapText.dialogDownloadProgress), "Download Progress");
AddTextField(dialogContentTab, textProperty, nameof(BootstrapText.dialogSceneLoadFailed), "Scene Failed");
AddTextField(dialogContentTab, textProperty, nameof(BootstrapText.dialogInitException), "Init Exception");
AddTextField(dialogContentTab, textProperty, nameof(BootstrapText.dialogCodeException), "Code Exception");
AddTextField(dialogContentTab, textProperty, nameof(BootstrapText.dialogFunctionCallFailed), "Call Failed");

CopilotAIFeb 6, 2026

Copy link

Choose a reason for hiding this comment

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

The enhanced inspector’s Dialog Content fields don’t surface the placeholder guidance from BootstrapText (e.g., {0}, {1} tooltips on the format-string fields). Without this, it’s easy to enter a localized string with the wrong placeholders. Consider adding tooltips/help text for the format-string fields (at least dialogInitFailed, dialogDownloadPrompt, dialogDownloadProgress, dialogSceneLoadFailed, dialogInitException, dialogFunctionCallFailed).

Copilot uses AI. Check for mistakes.
Comment on lines 306 to +312
<field signature="int $Obfuz$RVA_Value18" newName="$lC" />
<field signature="int $Obfuz$RVA_Value19" newName="$LC" />
<field signature="int $Obfuz$RVA_Value20" newName="$fD" />
<field signature="string $Obfuz$RVA_Value21" newName="$FD" />
<field signature="int $Obfuz$RVA_Value21" newName="$Kc" />
<field signature="int $Obfuz$RVA_Value22" newName="$Qd" />
<field signature="int $Obfuz$RVA_Value23" newName="$gD" />
<field signature="float $Obfuz$RVA_Value24" newName="$GD" />
<field signature="float $Obfuz$RVA_Value23" newName="$DC" />
<field signature="string $Obfuz$RVA_Value24" newName="$HC" />

CopilotAIFeb 6, 2026

Copy link

Choose a reason for hiding this comment

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

Assets/Obfuz/SymbolObfus/symbol-mapping.xml changes appear to be generated obfuscation mapping updates (including new BootstrapText-related signatures). Since this file can affect runtime symbol resolution/debugging, please confirm it’s intended to be updated as part of this PR; otherwise it may be safer to keep generated mapping changes out of the feature change.

Copilot uses AI. Check for mistakes.
Comment on lines +156 to +166
public static string SafeFormat(string template, params object[] args)
{
if (string.IsNullOrEmpty(template))
{
return string.Empty;
}

try
{
return string.Format(template, args);
}

CopilotAIFeb 6, 2026

Copy link

Choose a reason for hiding this comment

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

SafeFormat null-guards template, but still passes args directly to string.Format. If a caller passes null explicitly for args (e.g., SafeFormat(template, null)), string.Format will throw ArgumentNullException and can break bootstrap error handling. Consider normalizing args to an empty array (or catching ArgumentNullException) so SafeFormat is safe for all inputs.

Copilot uses AI. Check for mistakes.
Comment on lines +692 to +714
private static string GetStatusText(PackageInitializationStatus status, BootstrapText text)
{
return status switch
{
PackageInitializationStatus.InitializingPackage => "Initializing resource package...",
PackageInitializationStatus.GettingVersion => "Getting resource package version...",
PackageInitializationStatus.UpdatingManifest => "Updating resource manifest...",
PackageInitializationStatus.CheckingUpdate => "Checking resources to download...",
PackageInitializationStatus.DownloadingResources => "Downloading resources...",
PackageInitializationStatus.Completed => "Resource package initialization completed",
PackageInitializationStatus.Failed => "Initialization failed",
_ => "Unknown status"
PackageInitializationStatus.InitializingPackage => text.initializingPackage,
PackageInitializationStatus.GettingVersion => text.gettingVersion,
PackageInitializationStatus.UpdatingManifest => text.updatingManifest,
PackageInitializationStatus.CheckingUpdate => text.checkingUpdate,
PackageInitializationStatus.DownloadingResources => text.downloadingResources,
PackageInitializationStatus.Completed => text.packageCompleted,
PackageInitializationStatus.Failed => text.initializationFailed,
_ => text.unknownPackageStatus
};
}

private static string GetSceneLoadStatusText(SceneLoadStatus status)
private static string GetSceneLoadStatusText(SceneLoadStatus status, BootstrapText text)
{
return status switch
{
SceneLoadStatus.Loading => "Loading scene...",
SceneLoadStatus.Completed => "Scene loading completed",
SceneLoadStatus.Failed => "Scene loading failed",
_ => "Unknown status"
SceneLoadStatus.Loading => text.sceneLoading,
SceneLoadStatus.Completed => text.sceneCompleted,
SceneLoadStatus.Failed => text.sceneFailed,
_ => text.unknownSceneStatus

CopilotAIFeb 6, 2026

Copy link

Choose a reason for hiding this comment

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

GetStatusText/GetSceneLoadStatusText now take BootstrapText by value. Since BootstrapText is a 33-field struct, this copies many references on each call (status updates can be frequent). Consider passing it by in (or changing BootstrapText to an immutable class) to avoid repeated struct copying in hot paths.

Copilot uses AI. Check for mistakes.
@@ -1 +1 @@
260202209 No newline at end of file
260208645 No newline at end of file

CopilotAIFeb 6, 2026

Copy link

Choose a reason for hiding this comment

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

This PR includes updates to YooAsset StreamingAssets package versions/catalog/bundles. These look like generated build artifacts and aren’t mentioned in the PR summary. If they aren’t required for the BootstrapText/JTabView feature, consider removing them from this PR to keep the change focused and avoid accidentally shipping a new built-in catalog/version.

Copilot uses AI. Check for mistakes.
Comment on lines +137 to +140
// Click handler using closure-free pattern via userData
tabButton.userData = index;
tabButton.RegisterCallback<MouseDownEvent>(OnTabClicked);

CopilotAIFeb 6, 2026

Copy link

Choose a reason for hiding this comment

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

AddTab stores the tab index in Label.userData. Since userData is public and commonly used by consumers, this can conflict if callers also use userData for their own metadata. Consider keeping the index in your own internal mapping (e.g., parallel list lookup by label) or using a small private component/class to avoid consuming userData.

Copilot uses AI. Check for mistakes.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

coredocumentationImprovements or additions to documentationeditortests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Bootstrap需要移动到包体外

2 participants

@JasonXuDeveloper