Skip to content

feat: rebuild the guided-tour engine for data-driven, admin-configurable tours (0.6.0)#180

Open
aaronstevenwhite wants to merge 13 commits into
mainfrom
refactor/tour-engine-0.6.0
Open

feat: rebuild the guided-tour engine for data-driven, admin-configurable tours (0.6.0)#180
aaronstevenwhite wants to merge 13 commits into
mainfrom
refactor/tour-engine-0.6.0

Conversation

@aaronstevenwhite

Copy link
Copy Markdown
Collaborator

Description

Rebuilds the guided-tour engine so a deployment can author and tailor tours from data rather than code. A tour is now a validated data document an administrator ships under public/tours/, every UI surface a tour points at is a named entry in a typed anchor catalog the build checks, and the runner drives each step's prerequisites declaratively. This is the planned 0.6.0 minor release; the built-in tours and the in-app tour menu are unchanged for end users.

The aim throughout was configurability for an admin running a local build who wants to tailor tours to their user base: the anchor catalog is a typed contract, the tour schema validates authored documents loudly, and public/tours/ overrides merge over the built-ins without touching application code.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test improvements
  • Build/infrastructure changes

Related Issues

Fixes #
Relates to #

Changes Made

  • Typed anchor catalog (annotation-tool/src/tours/engine/anchorCatalog.ts): every tour-addressable surface declared once; AnchorId = keyof typeof anchorCatalog, so naming an undefined anchor fails to compile. Components publish elements with useTourAnchor(id) (anchorRegistry.tsx).
  • Validated tour schema + file-based authoring (tourSchema.ts, content/tourLoader.ts): parseTour validates and narrows a document; admin tours from public/tours/ (manifest /tours/index.json) merge over the built-ins (override-by-id, append, enabled: false drops).
  • Hexagonal runner + declarative drivers (TourRunner.tsx, capabilities.ts, seeders.ts): each step navigates to its route, runs its driver capability, resolves the anchor via the catalog reachedBy chain, and simulates its action. Added the previously-missing capabilities (world entity/location/event/time editors, entity/time collection builders, import dialog, project video-assignment, transcription) and corrected the project-video-assignment and group-management-page catalog surfaces.
  • Tour analytics, resume cursor, pause control (menu/tourTelemetry.ts, TourProvider.tsx, StepCard.tsx): the provider folds the runner's events into started/step_viewed(dwell)/completed(totalMs)/abandoned(reason+lastStepIndex); a fovea.tour.cursor resumes a reload in place; the step card carries a Pause control.
  • Dialogs yield to a running tour (components/ui/dialog.tsx): a dialog goes non-modal and ignores outside-press/focus-out close while a tour runs, so the step card stays reachable; outside a tour, behavior is unchanged (replaces a deployment-mode check).
  • Catalogue load tolerates a missing manifest (content/tourLoader.ts): an HTML SPA-fallback response for /tours/index.json is read as "no manifest" instead of throwing.
  • Every built-in tour has an engine-walked regression spec (test/e2e/regression/tours/) plus a comprehensive engine smoke suite; the regression helper asserts completion from the analytics telemetry.
  • Version bumped to 0.6.0 across all manifests; CHANGELOG.md and docs/docs/project/changelog.md updated.

Testing

Test Environment

  • OS: macOS (darwin 24.6.0)
  • Browser (if applicable): Chromium / Google Chrome (H.264 for the MP4 corpus)
  • Node version: 22
  • Python version (if applicable): n/a

Test Cases

  • Unit tests added/updated
  • E2E tests added/updated (if applicable)
  • Manual testing completed
  • All existing tests pass

Local results (all green):

  • 12/12 regression tour walks (test/e2e/regression/tours/) against the real backend + the MP4 video corpus — every built-in tour launches through the engine and every step's anchor resolves to completion.
  • 65/65 engine smoke tests (test/e2e/smoke/tour-engine.spec.ts, tour-runner.spec.ts) — runner, spotlight, step card, telemetry, the resume cursor, pause/resume, focus, keyboard.
  • 15/15 unit tests (tourTelemetry.test.ts 7, tourLoader.test.ts 8).
  • 11/11 tour-anchors + critical-path smoke (confirms the dialog change leaves normal dialog behavior intact).
  • Frontend tsc --noEmit, eslint, and vitest (tours + ui) all clean.

How to Test

  1. docker compose -f docker-compose.e2e.yml -f docker-compose.e2e.mp4.yml up -d --build
  2. cd annotation-tool && E2E_BASE_URL=http://localhost:3000 npx playwright test --config=playwright.tours.config.ts --project=regression tours/
  3. E2E_BASE_URL=http://localhost:3000 npx playwright test --config=playwright.config.ts --project=smoke test/e2e/smoke/tour-engine.spec.ts test/e2e/smoke/tour-runner.spec.ts

Screenshots/Videos

Documentation

  • Code comments updated
  • API documentation updated
  • User guide updated
  • README updated
  • CHANGELOG updated (for user-visible changes)
  • No documentation needed

Performance Impact

  • No significant performance impact
  • Performance improved (describe below)
  • Performance may be affected (describe below and justify)

Details: The runner resolves anchors by waking on the registry's change notification rather than polling the DOM, and the spotlight tracks via a single requestAnimationFrame loop while mounted; both are bounded to the active tour's lifetime.

Breaking Changes

Breaking changes:

  • The previous tour engine's internal modules and the data-tour-id attribute convention are removed. Tours and anchors are expressed through the catalog, schema, registry, and capabilities.

Migration guide:

  • Re-author any custom tours as validated tour documents (the Tour schema) and place them under public/tours/ with a /tours/index.json manifest; reference anchors by their AnchorId from the catalog. Components that exposed a surface to a tour use useTourAnchor(id) instead of a data-tour-id attribute.

Checklist

  • My code follows the project's coding standards
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings or errors
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Additional Notes

The demo-deployment specs under test/e2e/smoke/tour-demo-*.spec.ts / tour-rigorous-*.spec.ts / tour-live-every-feature.spec.ts target the demo.fovea.video deployment configuration (a VITE_DEMO_PUBLIC frontend with a FOVEA_DEMO_MODE backend that issues anonymous sessions and a seeded clip corpus). They auto-skip outside that environment. Run locally against the tour-demo stack, the engine itself works in the demo (MSW) context, and the failures are entirely deployment-configuration layers (VITE_DEMO_PUBLIC disables MSW in favor of a real demo-mode backend, which then needs anonymous-auth and a seeded corpus) — none of which this PR changes. These are best validated against the live demo deployment via LIVE_DEMO_URL at deploy time; the tour engine itself is fully covered by the regression + smoke suites above.

Reviewer Guidance

Worth a close look: the analytics-telemetry mapping in menu/tourTelemetry.ts and its wiring in TourProvider.tsx (the step_viewed dwell and single-close semantics), and the dialog modal gating in components/ui/dialog.tsx (it now reads tour-active state app-wide).

…e surface and a zod Tour schema that types first-party tours and validates admin-authored tour JSON at load with field-anchored errors.
…tour anchor id and the engine reads the live element for a step through a synchronous subscription.
…t resolves anchors by subscription, declarative state-driver capabilities, a config loader that validates admin tour files, an author-mode anchor inspector, and every component publishing its element through useTourAnchor.
…s data-tour-anchor, seed the catalogue with the first-party tours synchronously so a launch resolves immediately, and point the tour specs at the anchor attribute.
… Alert wrappers so a tour anchor ref attaches to their underlying DOM node.
…t from the card, asserts each step anchor resolves, and drive the ontology tour through it.
…a running tour, implementing the missing world, import, project-assignment, and transcription driver capabilities with their anchor catalog corrections, adding the analytics telemetry, the resume cursor, and the step-card pause control, tolerating a missing tour manifest in the catalogue loader, rewriting every built-in tour onto the engine-walked regression helper, and bumping to 0.6.0 with changelogs.
… so unit tests that mount them in isolation no longer crash, with useTourAnchor and useAnchorElement degrading to a no-op when no registry is present.
…0.6.0

# Conflicts:
#	CHANGELOG.md
#	annotation-tool/package.json
#	docs/docs/project/changelog.md
#	docs/package.json
#	model-service/package.json
#	model-service/pyproject.toml
#	package.json
#	server/package.json
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