Skip to content

add tests, typescript, and improve pager/next chapter - #65

Merged
nschimme merged 15 commits into
MUME:masterfrom
nschimme:fix-tutorial-pager-scroll-15358760155283308909
Sep 21, 2026
Merged

nschimme merged 15 commits into
MUME:masterfrom
nschimme:fix-tutorial-pager-scroll-15358760155283308909

Conversation

@nschimme

@nschimme nschimme commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary by Sourcery

Improve the interactive tutorial player with typed state-driven progression, more reliable paging and navigation, richer Markdown rendering, and comprehensive automated tests.

New Features:

  • Add explicit tutorial player states for command entry, output paging, narrative playback, and chapter completion.
  • Provide touch- and keyboard-friendly paging and chapter continuation controls.
  • Expand tutorial inline Markdown support with links, emphasis, code, lazy-loaded images, and image lightbox compatibility.

Bug Fixes:

  • Improve tutorial progression through overflowing output, passive story beats, final-step completion, and backward navigation.
  • Normalize tutorial chapter routing consistently across base paths and HTML-style URLs.

Enhancements:

  • Migrate the tutorial player and supporting utilities to TypeScript.
  • Refactor Markdown formatting, pager detection, and chapter lookup into reusable utilities.
  • Improve output scrolling, media-load handling, responsive completion messaging, and modal keyboard dismissal.

Build:

  • Add Vitest, Vue test utilities, jsdom, and the Vite Vue plugin; update the test script to run unit tests alongside link and asset checks.

Tests:

  • Add unit tests for shared Markdown, pager, and tutorial routing utilities.
  • Add component tests covering common UI components and tutorial player progression, navigation, paging, errors, and completion behavior.

- Align scroll view to top of new content blocks/lesson text when content overflows
- Defer chapter completion on final step command output so full responses can be read/paged
- Extract TypeScript utility modules for markdown, routing, and pager calculation
- Add Vitest unit test suite for utilities and Vue components
…croll positioning

- Align scroll view to top of new content blocks/lesson text when content overflows
- Prevent image load DOM jumping using aspect ratios, CSS containment, and load event capturing
- Defer chapter completion on final step command output so full responses can be read/paged
- Extract TypeScript utility modules for markdown, routing, and pager calculation
- Add Vitest unit test suite for utilities and Vue components
…tioning

- Align scroll view to top of new content blocks/lesson text when content overflows
- Prevent image load DOM jumping using aspect ratios, CSS containment, and load event capturing
- Defer chapter completion on final step command output so full responses can be read/paged
- Extract TypeScript utility modules for markdown, routing, and pager calculation
- Add Vitest unit test suite for utilities and Vue components
- Ensure fallback checks on error hint strings for steps without explicit ask targets
…tioning

- Align scroll view to top of new content blocks/lesson text when content overflows
- Prevent image load DOM jumping using aspect ratios, CSS containment, and load event capturing
- Defer chapter completion on final step command output so full responses can be read/paged
- Harden pager scroll calculation against sub-pixel display scale discrepancies
- Extract TypeScript utility modules for markdown, routing, and pager calculation
- Add Vitest unit test suite for utilities and Vue components
…tioning

- Align scroll view to top of new content blocks/lesson text when content overflows
- Prevent image load DOM jumping using aspect ratios, CSS containment, and load event capturing
- Defer chapter completion on final step command output so full responses can be read/paged
- Harden pager scroll calculation against sub-pixel display scale discrepancies
- Extract TypeScript utility modules for markdown, routing, and pager calculation
- Add Vitest unit test suite for utilities and Vue components
- Align scroll position to top of new blocks when content overflows
- Defer chapter completion on final command responses until paged
- Add :readonly and :inputmode="none" on command input during Pager mode
- Refactor markdown, tutorial, and pager logic into TypeScript utilities
- Add Vitest unit test suite covering utilities and Vue components
- Pin scroll to top on step 0 so chapter intro lesson text is read from the beginning
- Activate pager mode when chapter intro text overflows terminal viewport height
- Ensure smooth paging and mobile keyboard prevention across all tutorial chapters
…ove error feedback

- Refactor TutorialPlayer.vue to an explicit Player State Machine
- Ensure story beats look ahead to following quest prompts and advance cleanly after overflow
- Improve unrecognized command error text to clarify tutorial step context and offer hints
- Add unit tests covering state machine transitions and error feedback
…nsitions

- Normalize the Chapter Complete prompt bar with gold completion styling and a glowing 'Next Chapter →' action button
- Prevent virtual keyboard popups on mobile touch devices during Chapter Complete state
- Refactor player state machine transitions across multi-step chapters
- Add Vitest unit tests verifying completion bar prompt state
Refactor TutorialPlayer.vue to handle large step outputs and large initial
lesson content without skipping content or auto-advancing premature chapter
completions.

- Implement state machine in TutorialPlayer.vue (AWAITING_COMMAND,
  PAGING_OUTPUT, PLAYING_BEAT, CHAPTER_COMPLETE)
- Add top scroll alignment for long content blocks via scrollLogToLatestBlock()
- Add touch-aware prompt bar copy for paging and chapter completion states
- Extract markdown, tutorial, and pager utility functions into modular TS files
- Add Vitest unit tests for utilities and components
… bar action pill UX

- Add Player State Machine and deferred completion logic in TutorialPlayer.vue
- Align viewport scroll position to the top of newly added content blocks
- Replaced prompt input bar during output overflow with a device-tailored full-width action pill
- Add unit test suite for markdown, tutorial, and pager utility modules
… action pill UX

- Fix terminal output pagination and defer chapter completion state machine transitions
- Align scroll position to top of new content blocks for large lesson text and long MUD command outputs
- Replace prompt bar during output overflow and chapter completion with device-tailored full-width action pills
- Clean up duplicate buttons in log completion cards and add unit tests for utility modules
…ling

- Refactor terminal output paging logic into explicit PlayerState machine
- Align viewport scrolling to top of newly added blocks instead of auto-scrolling to bottom
- Defer chapter completion triggers when output overflows until paged through
- Transform prompt input bar into full-width action pills for Paging and Chapter Complete states
- Modularize markdown formatting, tutorial route normalization, and pager state utilities
- Add comprehensive Vitest unit tests for tutorial player and utility modules
@sourcery-ai

sourcery-ai Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

The PR converts the tutorial player to TypeScript, reorganizes it around an explicit state machine for paging and chapter progression, improves Markdown/media and responsive controls, and adds Vitest-based utility, component, and player tests.

State diagram for the tutorial player progression

stateDiagram-v2
    [*] --> AWAITING_COMMAND
    AWAITING_COMMAND --> PAGING_OUTPUT: isOverflowActive
    PAGING_OUTPUT --> PAGING_OUTPUT: pageForward
    PAGING_OUTPUT --> PLAYING_BEAT: output fully scrolled
    PLAYING_BEAT --> AWAITING_COMMAND: advanceSubStep
    AWAITING_COMMAND --> AWAITING_COMMAND: submit valid command
    AWAITING_COMMAND --> CHAPTER_COMPLETE: final step completed
    PAGING_OUTPUT --> CHAPTER_COMPLETE: final output fully scrolled
    CHAPTER_COMPLETE --> AWAITING_COMMAND: navigateToUrl
Loading

Flow diagram for the Vitest tutorial test setup

flowchart LR
    TestFiles["Utility, component, and player tests"] --> Vitest["Vitest"]
    Vitest --> VuePlugin["@vitejs/plugin-vue"]
    VuePlugin --> JSDOM["jsdom test environment"]
    JSDOM --> TestResults["Test results"]
Loading

File-Level Changes

Change Details Files
Introduces a typed tutorial player state machine and revised paging/advancement flow.
  • Adds explicit awaiting-command, output-paging, story-beat, and chapter-complete states.
  • Defers automatic transitions until overflowing output has been paged.
  • Improves story-beat lookahead, final-step completion, backward navigation, focus handling, and touch/desktop action labels.
  • Extracts route/chapter lookup and overflow detection helpers.
docs/.vitepress/theme/components/TutorialPlayer.vue
docs/.vitepress/theme/utils/tutorial.ts
docs/.vitepress/theme/utils/pager.ts
Adds reusable, escaped inline Markdown rendering with richer content support.
  • Moves formatting out of the component and adds links, emphasis, code, line breaks, and lazy-loaded image embeds.
  • Applies VitePress base paths to local image and link URLs while preserving external URLs.
  • Adds image containers, sizing, lightbox interaction support, and tutorial pager/completion pill styling.
docs/.vitepress/theme/components/TutorialPlayer.vue
docs/.vitepress/theme/utils/markdown.ts
Converts the tutorial player to TypeScript and adds automated Vitest coverage.
  • Adds typed refs, handlers, navigation arguments, chapter data, and player states.
  • Configures Vitest with Vue, jsdom, and Vite plugins and includes it in the package test script.
  • Tests shared utilities, tutorial player command/navigation behavior, and several tutorial UI components.
docs/.vitepress/theme/components/TutorialPlayer.vue
docs/.vitepress/theme/utils/markdown.ts
docs/.vitepress/theme/utils/pager.ts
docs/.vitepress/theme/utils/tutorial.ts
tests/components.test.ts
tests/tutorial_player.test.ts
tests/utils.test.ts
vitest.config.ts
package.json
package-lock.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

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.

Hey - I've found 4 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="docs/.vitepress/theme/utils/markdown.ts" line_range="46-53" />
<code_context>
+  })
+
+  // Links: [text](url)
+  safe = safe.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, linkText, url) => {
+    const cleanText = linkText
+    let cleanUrl = url.trim()
+    if (!/^(?:https?:\/\/|mailto:)/i.test(cleanUrl) && cleanUrl.startsWith('/') && typeof withBaseFn === 'function') {
+      cleanUrl = withBaseFn(cleanUrl)
+    }
+    const isExternal = /^(?:https?:\/\/)/i.test(cleanUrl)
+    return `<a href="${cleanUrl}" ${isExternal ? 'target="_blank" rel="noopener noreferrer"' : ''} class="tut-link">${cleanText}</a>`
+  })
+
</code_context>
<issue_to_address>
**🚨 issue (security):** Markdown links are emitted into `v-html` without validating their scheme or escaping their URL attribute. A link such as `[x](javascript:...)` produces an executable `href`, and a URL containing a quote can break out of the attribute and inject markup.

**Triggers:** When tutorial markdown contains an untrusted or compromised link URL.

**Suggested fix:** Allow only explicitly safe schemes such as `https:`, `mailto:`, and relative URLs, and HTML-escape the final URL before inserting it into the attribute.
</issue_to_address>

### Comment 2
<location path="docs/.vitepress/theme/utils/tutorial.ts" line_range="29-32" />
<code_context>
+ */
+export function normalizeChapterPath(rawPath: string | undefined, basePrefix: string = '/'): string {
+  if (!rawPath) return '/'
+  let currentPath = rawPath.replace(/\.html$/, '').replace(/\/$/, '')
+  const base = basePrefix || '/'
+  if (base !== '/' && currentPath.startsWith(base.replace(/\/$/, ''))) {
+    currentPath = '/' + currentPath.slice(base.replace(/\/$/, '').length).replace(/^\//, '')
+  }
+  return currentPath || '/'
+}
</code_context>
<issue_to_address>
**issue (bug_risk):** The base-prefix check matches any path that merely starts with the base text, not only the base path segment. For example, base `/pr-123/` also matches `/pr-1234/...`, strips the wrong prefix, and causes chapter lookup to select the fallback chapter.

**Triggers:** When a route path shares the base string but not its path-segment boundary.

**Suggested fix:** Require the base prefix to end at a path boundary, or normalize and compare the base with a trailing slash before stripping it.

```suggestion
  const base = basePrefix || '/'
  const basePath = base.replace(/\/$/, '')
  if (base !== '/' && (currentPath === basePath || currentPath.startsWith(basePath + '/'))) {
    currentPath = '/' + currentPath.slice(basePath.length).replace(/^\//, '')
  }
```
</issue_to_address>

### Comment 3
<location path="docs/.vitepress/theme/components/TutorialPlayer.vue" line_range="237-240" />
<code_context>
 }

+function handleMediaLoad() {
+  updatePagerState()
+}
+
 function handleScroll() {
</code_context>
<issue_to_address>
**issue (bug_risk):** Pager state is sampled once 300 ms after `scrollBy`, but `handleScroll` does not recompute it when the smooth scroll actually finishes. If the smooth animation lasts longer than the fixed timeout, the container remains marked as overflowing at the bottom and a pending chapter/beat transition is not executed until the user performs another paging action.

**Triggers:** When smooth scrolling takes longer than 300 ms, such as on a busy device or with a long scroll distance.

**Suggested fix:** Recompute pager state from the scroll-end/debounced scroll handler, or poll until scrolling has settled before executing the pending transition.
</issue_to_address>

### Comment 4
<location path="tests/components.test.ts" line_range="92-95" />
<code_context>
+  it('filters commands by category tab', async () => {
+    const wrapper = mount(MumeCommandGuide)
+    expect(wrapper.find('.command-table').exists()).toBe(true)
+    const filterTabs = wrapper.findAll('.filter-tab')
+    const gearTab = filterTabs.find(b => b.text().includes('Gear & Inventory'))
+    if (gearTab) {
+      await gearTab.trigger('click')
+      expect(wrapper.text()).toContain('equipment')
+    }
+  })
+})
</code_context>
<issue_to_address>
**issue (testing):** The test only performs its assertion inside `if (gearTab)`, so it passes without checking anything when the expected filter tab is absent. The test therefore cannot detect a regression that removes or renames the Gear & Inventory tab.

**Triggers:** When the component no longer renders the expected filter tab.

**Suggested fix:** Assert that `gearTab` exists before triggering it, then assert the filtered content.

```suggestion
    expect(gearTab).toBeDefined()
    await gearTab!.trigger('click')
    expect(wrapper.text()).toContain('equipment')
```
</issue_to_address>

Sourcery assessment

Approval pending. 4 findings to address first.

Blocking findings: docs/.vitepress/theme/utils/markdown.ts:53, docs/.vitepress/theme/utils/tutorial.ts:32, docs/.vitepress/theme/components/TutorialPlayer.vue:240, tests/components.test.ts:95


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread docs/.vitepress/theme/utils/markdown.ts Outdated
Comment thread docs/.vitepress/theme/utils/tutorial.ts
Comment thread docs/.vitepress/theme/components/TutorialPlayer.vue Outdated
Comment thread tests/components.test.ts Outdated
…tection

- Validate link schemes (http, https, mailto, relative, anchors) and escape URL attributes in markdown utility
- Enforce strict path-segment boundary checking in normalizeChapterPath
- Add scrollend listener and debounced scroll settling for pager state updates
- Strengthen unit test assertions in components test suite

@sourcery-ai sourcery-ai Bot left a comment

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.

Sourcery assessment

Approved.

…ete states

- Add Enter keydown handling during CHAPTER_COMPLETE state to advance chapters or open end-of-tutorial modal on Chapter 15
- Add Enter keydown listener to pagination state to scroll terminal output
- Update unit tests to verify keydown handling for chapter complete state
@nschimme
nschimme merged commit 26920fa into MUME:master Sep 21, 2026
2 checks passed

This branch was successfully deployed

1 active deployment
pr-65 — 69767bd0 Deployed Sep 21, 2026 by github-actions[bot]
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