Skip to content

feat: Add grouped lines animation mode#459

Open
hardtokidnap wants to merge 7 commits into
DenverCoder1:mainfrom
hardtokidnap:main
Open

feat: Add grouped lines animation mode#459
hardtokidnap wants to merge 7 commits into
DenverCoder1:mainfrom
hardtokidnap:main

Conversation

@hardtokidnap

Copy link
Copy Markdown

Summary

Adds a groups parameter that lets users group lines so each group types sequentially, then all lines in the group clear together before the next group starts. For example, groups=2,1 means the first two lines type and clear as a unit, then the third line plays solo.

Why this change?

I noticed when setting this up for myself, i had long lines of "Quotes" that didn't work as well for longer lines. If i wanted to split it across two lines, it would also spawn the third line when multiline was used.

This change allows you to use both.

The change can be tested here:

Typing SVG

readmesvgdemo


  • Backend: new groups param on RendererModel, validated so group sizes must sum to total line count. RendererView and main.php template compute per-group SMIL animation timings (staggered begin, shared clear points).
  • Random + groups: when random=true is used with groups, whole groups are shuffled as units so lines within each group stay together.
  • Demo UI: interactive clickable dividers between lines. Dividers are invisible by default, appear on hover, and turn blue when active. Groups are computed from active divider positions and serialized to the URL for permalink support.
  • Docs: added groups to the README options table.

Note

Some CSS, JS, and PHP files have larger diffs than expected. I ran composer run format as described in CONTRIBUTING.md and Prettier decided to reformat a bunch of existing code (wrapping changes, trailing commas, comment repositioning, etc.). I left the reformatting in since fighting the formatter felt worse than including it. Here's what it looks like when you run it:

src/demo/css/style.css 19ms
src/demo/css/toggle-dark.css 3ms
src/demo/js/script.js 54ms
src/models/RendererModel.php 9ms
src/templates/main.php 7ms
tests/RendererTest.php 10ms

If you'd prefer I separate the formatting into its own commit or revert it entirely, I'm happy to do that. I could not figure out a clean way to get Prettier to only touch the lines I changed. If there's a trick for that, let me know because I genuinely could not find one.

Type of change

  • Bug fix (added a non-breaking change which fixes an issue)
  • New feature (added a non-breaking change which adds functionality)
  • Updated documentation (updated the readme, templates, or other repo files)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

How Has This Been Tested?

  • Ran tests with composer test
  • Added or updated test cases to test new features

Test cases added for grouped lines: testValidGroups, testGroupsNotProvided, testGroupsSumMismatch, testGroupsZeroSize, testGroupedLinesRender, testGroupedLinesPause, testGroupedLinesRepeatFalse, testRandomWithGroups. All pass (47 total, 1 Google Font test failed due to local networking).

@DenverCoder1 DenverCoder1 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Creative idea!

One thought I had --

As this is likely an obscure feature, I think it would be better not to have it cause large gaps between line elements by default, though I do like the clean UI implementation.

image

I noticed that this feature is independent of whether multiline is true or false. Perhaps by default the feature is hidden and the grouping feature could be enabled by selecting a menu item like "Create line groups" from the dropdown?

Image

This would deviate a bit from being a direct adaptation of the query parameters though. Open to suggestions.


Don't need to worry about the formatting check as it should be able to fix itself.

Copilot AI 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 groups parameter so README typing animations can type multiple lines sequentially within a group, clear the group together, and then continue to the next group. The PR also exposes the option in the demo UI and documents/tests the new behavior.

Changes:

  • Adds backend parsing, validation, rendering, and randomization behavior for grouped line animations.
  • Adds demo UI dividers for creating/restoring line groups in permalinks.
  • Adds grouped animation tests and README option documentation.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/models/RendererModel.php Adds groups validation and group-preserving random shuffle.
src/views/RendererView.php Passes groups from the model into the SVG template.
src/templates/main.php Adds grouped SMIL animation timing/rendering branch.
src/demo/js/script.js Adds divider UI, URL serialization/restoration, and reset behavior for groups.
src/demo/css/style.css Styles interactive group dividers.
tests/RendererTest.php Adds grouped rendering and random/groups tests.
tests/OptionsTest.php Adds groups option validation tests.
README.md Documents the new groups parameter.

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

Comment thread src/demo/js/script.js
Comment on lines +155 to +157
divider.addEventListener("click", function () {
this.classList.toggle("active");
});
Comment thread tests/RendererTest.php Outdated
Comment on lines +430 to +432
// Lines within each group share the same dur, so groups stay as units
// Both groups have size 2 with same timing, verify grouped animation is used
$this->assertStringContainsString("Grouped lines", $svg);
Comment thread README.md Outdated
| `duration` | Duration of the printing of a single line in milliseconds (default: `5000`) | integer | Any positive number |
| `pause` | Duration of the pause between lines in milliseconds (default: `0`) | integer | Any non-negative number |
| `repeat` | `true` to loop around to the first line after the last (default: `true`) | boolean | `true` or `false` |
| `groups` | Group lines so each group types sequentially then clears together (default: `none`) | string | `2,1` (first 2 lines as group 1, next 1 as group 2) |
Comment thread src/demo/js/script.js Outdated
Comment on lines +221 to +226
// DOM layout: [Line 1] [divider after=1] [Line 2] [divider after=2] [Line 3]
// Remove the divider before the deleted line (index-1), except line 1
// has no preceding divider, so remove the one after it instead
const dividerToRemove = index > 1 ? index - 1 : 1;
const divider = parent.querySelector(`.group-divider[data-divider-after="${dividerToRemove}"]`);
if (divider) parent.removeChild(divider);
@hardtokidnap

Copy link
Copy Markdown
Author

Creative idea!

One thought I had --

As this is likely an obscure feature, I think it would be better not to have it cause large gaps between line elements by default, though I do like the clean UI implementation.

I noticed that this feature is independent of whether multiline is true or false. Perhaps by default the feature is hidden and the grouping feature could be enabled by selecting a menu item like "Create line groups" from the dropdown?

Thanks, that makes sense and I agree the default spacing shouldn't be there for everyone.

This would deviate a bit from being a direct adaptation of the query parameters though. Open to suggestions.

I'd like to avoid the dropdown mode item if we can, mainly because it'd be the first demo control that doesn't map directly to a query parameter. Everything else in the form is a one-to-one mirror of a param, and a "Create line groups" item would be UI-only state with no URL equivalent.

Instead I'd propose keeping the dividers always present but fully collapsed by default. Zero gap and invisible until a divider is actually activated, at which point the gap appears only around the active boundaries. That removes the large default spacing you saw, keeps the demo a faithful mirror of the params (no mode toggle), and the grouping still serializes straight to groups= only when you actually use it. So for anyone not using the feature, the demo looks and behaves exactly as it does today.

I will do this approach and then represent the results in this PR, to see what you think @DenverCoder1 👍

hardtokidnap and others added 3 commits June 7, 2026 16:12
composer format runs prettier with print width 120 which may account for some differences
display:none until hover/focus-within keeps default line spacing; dividers are <button> with aria-pressed for keyboard use
removeLine dropped the active divider before the deleted line; surviving divider now stays active if either adjacent boundary was
seed RNG and assert intra-group adjacency so an individual-line shuffle regression is caught
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.

3 participants