Skip to content

feat(widgets): add LoadingDots widget#674

Open
realtushartyagi wants to merge 1 commit into
Karanjot786:mainfrom
realtushartyagi:feat/loading-dots
Open

feat(widgets): add LoadingDots widget#674
realtushartyagi wants to merge 1 commit into
Karanjot786:mainfrom
realtushartyagi:feat/loading-dots

Conversation

@realtushartyagi
Copy link
Copy Markdown

@realtushartyagi realtushartyagi commented Jun 4, 2026

Description

Adds a LoadingDots widget for inline loading states. The component animates dots through tick(), maintains a fixed width to prevent layout shifts, and supports ASCII fallbacks when Unicode is unavailable.

Related Issue

Closes #479

Which package(s)?

@termuijs/widgets

Type of Change

  • 🐛 Bug fix (type:bug)
  • ✨ Feature (type:feature)
  • 📝 Docs (type:docs)
  • 🧪 Tests (type:testing)
  • ♻️ Refactor (type:refactor)
  • 🎨 Design / UX (type:design)
  • ♿ Accessibility (type:accessibility)
  • ⚡ Performance (type:performance)
  • 🔧 DevOps / CI (type:devops)
  • 🔒 Security (type:security)

Checklist

  • ⭐ You starred the repo. The needs-star check blocks your merge otherwise.
  • Tests pass locally: bun vitest run
  • Build passes: bun run build
  • Typecheck passes: bun run typecheck
  • You read CONTRIBUTING.md.
  • Your PR title follows type: short description.
  • Widget state mutators call markDirty() (if your change affects rendering).
  • No new any types without an inline comment explaining why.
  • No unrelated refactors bundled into this PR.

GSSoC 2026 Participation

  • You are a GSSoC 2026 contributor.
  • Your GSSoC profile: https://gssoc.girlscript.org/profile/f49aa65e-aab2-4e9f-99d6-2174f339ecfe

Screenshots / Recordings (UI changes)

N/A There is no UI changes

Notes for the Reviewer

  • Used vi.spyOn to test ASCII fallback behavior safely.
  • Verified tick() and setLabel() trigger markDirty().
  • Confirmed fixed-width dot padding prevents UI jitter.

Summary by CodeRabbit

  • New Features

    • LoadingDots widget with configurable label, max dot count, and color
    • Renders using Unicode dot when available, falls back to ASCII dot otherwise
  • Tests

    • Added test suite covering initial render, dot animation/cycling, label updates, and Unicode/ASCII fallback

Copilot AI review requested due to automatic review settings June 4, 2026 08:02
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 4, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: df48241b-b07a-4546-b2b1-e17df7ae5e64

📥 Commits

Reviewing files that changed from the base of the PR and between 16fdf8f and 27996ae.

📒 Files selected for processing (3)
  • packages/widgets/src/feedback/LoadingDots.test.ts
  • packages/widgets/src/feedback/LoadingDots.ts
  • packages/widgets/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/widgets/src/index.ts
  • packages/widgets/src/feedback/LoadingDots.test.ts
  • packages/widgets/src/feedback/LoadingDots.ts

📝 Walkthrough

Walkthrough

Adds a new LoadingDots widget and tests: label + fixed-width cycling dots, tick-driven advancement with wrap at maxDots, Unicode/ASCII glyph selection, color support, and package exports.

Changes

LoadingDots Widget Feature

Layer / File(s) Summary
Contract and initialization
packages/widgets/src/feedback/LoadingDots.ts
LoadingDotsOptions defines label, maxDots, color; LoadingDots stores label, maxDots, color, and dot counter; ctor sets height=1 and applies defaults.
State mutation and dirty marking
packages/widgets/src/feedback/LoadingDots.ts
tick() advances/wraps dot counter and calls markDirty(); setLabel() updates label and calls markDirty().
Render implementation
packages/widgets/src/feedback/LoadingDots.ts
_renderSelf() writes label, renders padded dot sequence using · or . based on caps.unicode, applies foreground color, and early-returns on non-positive content rect.
Public API export
packages/widgets/src/index.ts
Exports LoadingDots and LoadingDotsOptions from the feedback module.
Comprehensive test suite
packages/widgets/src/feedback/LoadingDots.test.ts
Vitest cases validate initial label render, tick adds a dot, dot count cycles after maxDots, setLabel() updates output, and ASCII fallback when caps.unicode is false.

Sequence Diagram(s)

sequenceDiagram
  participant Host
  participant LoadingDots
  participant Screen
  Host->>LoadingDots: tick()
  LoadingDots->>LoadingDots: update dot counter and mark dirty
  Host->>LoadingDots: render(screen)
  LoadingDots->>Screen: write label and padded dots
  Screen->>Host: updated back buffer row
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • Karanjot786

Poem

🐰 I twitch my nose and count each dot,

Cyan specks dancing, never caught,
Tick by tick they grow and play,
Then vanish clean to start the sway,
A dotty hop in ASCII or dot.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The code changes implement the LoadingDots widget with the required API (constructor with options, tick() and setLabel() methods), proper exports, comprehensive tests covering label rendering, dot cycling, and ASCII fallback, and mark dirty calls as specified in issue #479. Add height guard (height <= 0) in _renderSelf and use stringWidth from @termuijs/core to advance cursor by label width, as requested by reviewer Karanjot786, to fully meet issue #479 requirements.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat(widgets): add LoadingDots widget' directly summarizes the main change—implementing a new LoadingDots widget in the widgets package.
Description check ✅ Passed The PR description follows the template structure with all required sections: Description, Related Issue (Closes #479), package selection (@termuijs/widgets), Type of Change (Feature selected), complete Checklist, GSSoC 2026 participation, and Notes for Reviewer.
Out of Scope Changes check ✅ Passed All changes are scoped to packages/widgets: new LoadingDots.ts and LoadingDots.test.ts files plus an index.ts export update. No changes to other packages, dependencies, or unrelated code.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@github-actions github-actions Bot added type:feature +10 pts. New feature. area:widgets @termuijs/widgets type:testing +10 pts. Tests. labels Jun 4, 2026
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

🎉 Thanks for your first PR to TermUI, @realtushartyagi.

Before your PR merges:

  1. Star the repo. Required. The star-check job blocks your merge otherwise.
  2. ✅ All checks green: build, test, typecheck.
  3. 🏷 PR title follows type: short description. Example: fix: handle empty list.
  4. 🔗 Link your closing issue in the description.

GSSoC 2026 points come from labels after merge:

  • gssoc:approved. +50 base points.
  • level:beginner / intermediate / advanced / critical. +20 / +35 / +55 / +80.
  • quality:clean / exceptional. x 1.2 / x 1.5.
  • type:*. Stackable bonus.

Your reviewer responds within 48 hours. Ping @Karanjot786 on Discord for urgent help.

🚀 Welcome to the cohort.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a new LoadingDots feedback widget to @termuijs/widgets and exposes it from the package entrypoint, along with a Vitest test suite.

Changes:

  • Export LoadingDots and LoadingDotsOptions from the widgets package index.
  • Implement LoadingDots widget with label + animated dot cycle and unicode/ASCII dot selection.
  • Add unit tests covering rendering, ticking behavior, label updates, and ASCII fallback.

Reviewed changes

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

File Description
packages/widgets/src/index.ts Exposes LoadingDots widget and its options via public package exports.
packages/widgets/src/feedback/LoadingDots.ts Implements the LoadingDots widget rendering and tick/label update behavior.
packages/widgets/src/feedback/LoadingDots.test.ts Adds Vitest coverage for rendering and unicode/ASCII behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +23 to +31
it('tick adds a dot', () => {
const screen = new Screen(20, 1);
const ld = new LoadingDots({}, { label: 'Thinking', maxDots: 3 });
ld.updateRect({ x: 0, y: 0, width: 20, height: 1 });
ld.tick();
ld.render(screen);
const row = screen.back[0].map(c => c.char).join('');
expect(row).toContain('Thinking· ');
});
constructor(style: Partial<Style> = {}, opts: LoadingDotsOptions = {}) {
super({ height: 1, ...style });
this._label = opts.label ?? '';
this._maxDots = opts.maxDots ?? 3;
Comment on lines +30 to +33
tick(): void {
this._dotCount = (this._dotCount + 1) % (this._maxDots + 1);
this.markDirty();
}
Comment on lines +55 to +56
const dots = dotChar.repeat(this._dotCount);
const padding = ' '.repeat(this._maxDots - this._dotCount);
Comment on lines +49 to +52
if (this._label) {
screen.writeString(currentX, y, this._label, attrs);
currentX += this._label.length;
}
Copy link
Copy Markdown

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/widgets/src/feedback/LoadingDots.ts`:
- Line 5: The _renderSelf method must early-return when height <= 0 and use
display-width-safe cursor advancement for the label: add a guard at the start of
_renderSelf to return when this.height <= 0, and replace any label cursor
increments that use label.length with a call to the core stringWidth utility
(import it from '`@termuijs/core`' alongside
Style/Color/Screen/caps/styleToCellAttrs) so the dot region stays aligned for
wide Unicode characters; update all occurrences (around the label advance logic
and the blocks noted) to use stringWidth(label) instead of .length.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cabb8077-9d90-493d-a5fd-6ee584c49bc6

📥 Commits

Reviewing files that changed from the base of the PR and between c36cc8c and 16fdf8f.

📒 Files selected for processing (3)
  • packages/widgets/src/feedback/LoadingDots.test.ts
  • packages/widgets/src/feedback/LoadingDots.ts
  • packages/widgets/src/index.ts

Comment thread packages/widgets/src/feedback/LoadingDots.ts Outdated
@Karanjot786 Karanjot786 added gssoc:approved Approved PR. Earns +50 base points. quality:clean x 1.2 multiplier. Clean implementation. level:intermediate +35 pts. Moderate task. labels Jun 4, 2026
@Karanjot786
Copy link
Copy Markdown
Owner

Review: Two fixes needed in LoadingDots

1. Missing height guard (line 5 of _renderSelf):

protected _renderSelf(screen: Screen): void {
    const { x, y, width, height } = this._getContentRect();
    if (width <= 0 || height <= 0) return;

2. Use stringWidth for label cursor advancement:

import { stringWidth } from '@termuijs/core';

// replace label.length with:
cursor += stringWidth(label);

This prevents layout corruption when the label contains wide Unicode characters.

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

Labels

area:widgets @termuijs/widgets gssoc:approved Approved PR. Earns +50 base points. level:intermediate +35 pts. Moderate task. quality:clean x 1.2 multiplier. Clean implementation. type:feature +10 pts. New feature. type:testing +10 pts. Tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(widgets): add LoadingDots widget

3 participants