[Feature] Native port: RegExr-style regex tool (#tools regex) — client UX; GPL; baseline gskinner/regexr #42

Closed
opened 2026-04-29 16:30:53 +00:00 by spikerj · 1 comment
Owner

Summary

Add a [new feature]: a native SpikerSoft Angular implementation of RegExr-style regex authoring — the interactive editor / cheatsheet / test surface (regexr.com UX: expression bar + flags, cheatsheet sidebar, Text/Tests area with match highlighting, Tools tabs such as Explain / Replace / List / Details). Not an iframe of regexr.com; not the separate “Regexp People–style regex game” — see #41 — regex game (sister ticket, also regex-themed but different product).

Route (target): /tools/(tools:regex) under the tools area — clarify naming/docs so learners don’t confuse it with #41.

Relationship to existing code

We already host a Regular Expression surface in lib-reg-ex (tools library → RegExComponent path). Use it as the starting integration point, but evolve toward a full RegExr-parity tool ported into our ecosystem (standalone components/services, routing, theme). After the port landed, ongoing work tracks SpikerSoft — upstream is only the initial baseline / inspiration, not perpetual sync.

Approach

  • Baseline: Fork mentally from gskinner/regexr [latest default branch] as the port reference snapshot — reimplement client-side behaviors we need inside Angular, SpikerSoft theme, and our component patterns.
  • Scope: Client-side UX + JS regex evaluation aligned with screenshot: cheatsheet/reference panel, highlighted expression editing, corpus editor with highlighted matches & timing/summary, tools panel (Explain, Replace, List, Details, Tests tab as feasible). Omit or defer server regexr pieces (PHP/community save/etc.) unless we add our own backends later.
  • Fork policy: Once ported, development continues on SpikerSoft — we intentionally stop following upstream as source of truth beyond the migration baseline unless we selectively cherry-pick.

License

RegExr is GPL-3.0. Our codebase is public, which aligns with redistribution expectations — still file NOTICE / attribution to Grant Skinner & gskinner, link to upstream, and comply with GPLv3 obligations for this derivative feature (legal review if anything is unclear).

Acceptance (draft)

  • Tools route /tools/(tools:regex) (or documented equivalent) hosts the ported tool.
  • Behavioral parity target: core editor UX from screenshot — expression + flags + text + cheatsheet + primary Tools (Explain/Replace/List/Details) as scoped for v1.
  • Theming integrates with SpikerSoft (glass/light-dark), not pasted regexr.com CSS verbatim long-term.
  • License + credits visible.
  • Cross-linked: this ticket (#42) = RegEx authoring tool; #41 = ManzDev Regexp People game native port.

Links

## Summary Add a **[new feature]**: a **native SpikerSoft Angular** implementation of **[RegExr](https://github.com/gskinner/regexr)**-style regex authoring — the **interactive editor / cheatsheet / test surface** ([regexr.com](https://regexr.com/) UX: expression bar + flags, cheatsheet sidebar, Text/Tests area with match highlighting, Tools tabs such as Explain / Replace / List / Details). **Not** an iframe of regexr.com; **not** the separate “Regexp People–style regex **game**” — see **[#41 — regex game](https://git.spikersoft.com/spikerj/spikersoft-issues/issues/41)** (*sister ticket*, also regex-themed but **different product**). **Route (target):** `/tools/(tools:regex)` under the tools area — clarify naming/docs so learners don’t confuse it with [#41](https://git.spikersoft.com/spikerj/spikersoft-issues/issues/41). ## Relationship to existing code We already host a **Regular Expression** surface in **`lib-reg-ex`** (`tools` library → `RegExComponent` path). Use it as the **starting integration point**, but evolve toward a **full RegExr-parity tool** ported into our ecosystem (standalone components/services, routing, theme). After the port landed, ongoing work **tracks SpikerSoft** — upstream is only the initial baseline / inspiration, not perpetual sync. ## Approach - **Baseline:** Fork mentally from **`gskinner/regexr` [latest default branch]** as **the port reference snapshot** — reimplement client-side behaviors we need inside **Angular**, **SpikerSoft theme**, and our component patterns. - **Scope:** **Client-side UX + JS regex evaluation** aligned with screenshot: cheatsheet/reference panel, highlighted expression editing, corpus editor with highlighted matches & timing/summary, tools panel (**Explain**, **Replace**, **List**, **Details**, Tests tab as feasible). Omit or defer **server** regexr pieces (PHP/community save/etc.) unless we add our own backends later. - **Fork policy:** Once ported, **development continues on SpikerSoft** — we intentionally **stop following upstream** as source of truth beyond the migration baseline unless we selectively cherry-pick. ## License RegExr is **[GPL-3.0](https://github.com/gskinner/regexr/blob/master/LICENSE)**. Our codebase is **public**, which aligns with redistribution expectations — still file **NOTICE** / attribution to Grant Skinner & gskinner, link to upstream, and comply with GPLv3 obligations for this derivative feature (legal review if anything is unclear). ## Acceptance (draft) - [ ] Tools route `/tools/(tools:regex)` (or documented equivalent) hosts the ported tool. - [ ] Behavioral parity target: core editor UX from screenshot — expression + flags + text + cheatsheet + primary Tools (**Explain**/Replace/List/Details) as scoped for v1. - [ ] Theming integrates with SpikerSoft (glass/light-dark), not pasted regexr.com CSS verbatim long-term. - [ ] License + credits visible. - [ ] Cross-linked: **this ticket (#42)** = RegEx **authoring tool**; **[#41](https://git.spikersoft.com/spikerj/spikersoft-issues/issues/41)** = ManzDev **Regexp People game** native port. ## Links - Upstream repo: https://github.com/gskinner/regexr/ - Product reference: https://regexr.com/
Author
Owner

Closing — RegExr-style port shipped, evolved well past v1

The RegEx authoring tool has been live in lib-reg-ex for several sprints and has grown well beyond the original v1 acceptance bar. Closing this ticket as done; new work continues against discrete tickets.

Acceptance checklist

  • Tools route/tools/(tools:regex) hosts the ported tool. Lesson deep links via ?lessonId=NNNNN.
  • Behavioral parity — expression + flags + test text + cheatsheet + Explain / Replace / List / Details all implemented; Details adds capture-group color cycling that the upstream lacks.
  • Theming — full SpikerSoft glassmorphism via @include glass-themed(); honors light/dark theme variables; no RegExr CSS pasted verbatim.
  • License + credits — GPL-3.0 attribution footer linking gskinner/regexr + license, NOTICE-style copy in component header doc.
  • Cross-linked with #41 (Regexp People game — distinct product).

What landed

Core editor (parity items)

  • Tokenizing pattern bar — live syntax highlighting layered behind a transparent <input>; hover-sync with Explain panel via hoveredTokenIndex signal.
  • Flag togglesg / i / m / s / u / y as click-to-toggle buttons (no need to type them in the slashes).
  • Test-text frame — transparent textarea over a highlight layer; six-color match cycle (--regex-group-N palette) so overlapping captures stay readable.
  • Tools tabs — Explain (RegExr-style nested-card layout that visualizes group/set nesting), Replace ($1-style preview), List (custom joiner), Details (range / value / capture-group table).
  • Solver — runs in a Web Worker, races a 250 ms timeout watchdog so a runaway regex never freezes the UI.

Curriculum / lessons (substantial expansion — original ticket scoped only "sandbox" parity)

  • 12 chapters / ~40 lessons in the 40000–49999 lesson-number band (Lesson40100_LookaroundsTutorial etc.).
  • Two lesson kindstutorial (read-and-continue) and challenge (graded against a RegexPlan reference solution).
  • Backend wiringRegexLessonStrategyBase + per-chapter strategy files, RegexLessonRegistry, MediatR GetLessonAttemptQueryHandler / CompleteTutorialCommandHandler. Browser-graded attempts are stashed in a long-lived IOfflineAttemptStore (Mongo) and the short-lived ILessonAttemptCache (Redis) so completion survives a Redis restart.
  • Idempotent tutorial completionLessonsController.CompleteTutorial now returns 200 OK { JustCompleted: false } for stray re-clicks on already-finished tutorials instead of 410 Gone. Resolves the misleading console error reported during dogfooding.
  • Lesson panelib-regex-lesson-pane shows instructions, hints, expected-match preview, real-time grader feedback, gated Continue button (disabled while POST in flight, label flips to Saving…Completed).
  • Catalog navigationLessonCatalogService chapters → in-component Lessons menu (header dropdown) AND Lessons sidebar pane stay in lockstep via the shared activeLessonNumber() signal; selecting in either surface updates both.

UX scaffolding

  • Mode toggle — clear Sandbox ↔ Lesson switch in the menu bar (preserves sandbox state via sandboxStateSnapshot so users can scratch around mid-lesson without losing work).
  • Guided tourRegExTourService implementing IntroTourProvider; data-tour="…" anchors on every notable surface (pattern bar, flag toggles, test text, tools tabs, lessons menu, both side panes, mode toggle, tour button itself). Step list is filtered against live DOM at start time so missing-element steps never explode the tour.
  • Three-column desktop layoutlessons-pane (left, 240px) | main pane (1fr) | cheatsheet-pane (right, 260px) by default. Each pane is independently togglable from the View menu (checkbox-style entries) AND has its own in-pane close button. Header shows a Show Cheatsheet button only when the pane is hidden — no duplicate label noise.
  • Pane swapView → Swap panes flips lessons↔cheatsheet sides for users who prefer the curriculum on the right.
  • Responsive collapse — at ≤1280px the cheatsheet column drops out of the grid (still togglable); at ≤900px the layout collapses to a single column with panes stacking above the main pane.
  • File menuSave to Browser / Download to Device / Load from Browser / Load from Device via shared SaveContentDialogComponent + LoadContentDialogComponent (parameterized to use SpikerSoftRegExStorage / savedRegex IndexedDB store, distinct from the JSON tool's). Persists pattern + flags + test text + replaceWith + listJoiner.

Persistence + migration

  • All editor state + pane preferences saved to localStorage under spikersoft.regex-tool.state.v1.
  • restoreState() reads two generations of legacy keys (sidebarMode string union, very-old cheatsheetOpen boolean) and migrates to the current cheatsheetVisible / lessonsVisible / panesSwapped booleans — covered by unit tests.

Quality bar

  • Frontendnx test tools: 487 passed / 2 skipped (489 total) across 39 spec files. Includes targeted coverage for pane toggles, swap persistence, legacy-state migration (3 separate tests), in-pane close button DOM, header Show Cheatsheet rendering, lesson-mode auto-open w/ manual-hide tracking, idempotent tutorial completion gating, and the regex-curriculum reference-solution validator (verifies every RegexPlan.ExpectedMatches against the lesson's reference solution).
  • BackendLessonsControllerTests: 31/31 passing, including new cases for the offline-store fallback path and the idempotent re-completion no-op. RegexCurriculumTests validates lesson-number ranges and reference-solution↔plan agreement for every lesson.
  • Buildnx build tools clean; no lint errors (eslint / stylelint / angular-eslint template).

Related tickets / follow-ups

  • #19Development > Regular Expressions don't work (referenced; this port is what supersedes it).
  • #41 — Regexp People game native port (sister, distinct product).

Attribution

SpikerSoft port is a clean-room Angular re-implementation inspired by gskinner/regexr (GPL-3.0). Attribution + license link appear in the in-tool footer and in component-level doc comments.

## Closing — RegExr-style port shipped, evolved well past v1 The RegEx authoring tool has been live in `lib-reg-ex` for several sprints and has grown well beyond the original v1 acceptance bar. Closing this ticket as **done**; new work continues against discrete tickets. ### Acceptance checklist - [x] **Tools route** — `/tools/(tools:regex)` hosts the ported tool. Lesson deep links via `?lessonId=NNNNN`. - [x] **Behavioral parity** — expression + flags + test text + cheatsheet + **Explain / Replace / List / Details** all implemented; Details adds capture-group color cycling that the upstream lacks. - [x] **Theming** — full SpikerSoft glassmorphism via `@include glass-themed()`; honors light/dark theme variables; **no** RegExr CSS pasted verbatim. - [x] **License + credits** — GPL-3.0 attribution footer linking gskinner/regexr + license, NOTICE-style copy in component header doc. - [x] **Cross-linked** with #41 (Regexp People game — distinct product). ### What landed #### Core editor (parity items) - **Tokenizing pattern bar** — live syntax highlighting layered behind a transparent `<input>`; hover-sync with Explain panel via `hoveredTokenIndex` signal. - **Flag toggles** — `g / i / m / s / u / y` as click-to-toggle buttons (no need to type them in the slashes). - **Test-text frame** — transparent textarea over a highlight layer; six-color match cycle (`--regex-group-N` palette) so overlapping captures stay readable. - **Tools tabs** — Explain (RegExr-style nested-card layout that visualizes group/set nesting), Replace ($1-style preview), List (custom joiner), Details (range / value / capture-group table). - **Solver** — runs in a Web Worker, races a 250 ms timeout watchdog so a runaway regex never freezes the UI. #### Curriculum / lessons (substantial expansion — original ticket scoped only "sandbox" parity) - **12 chapters / ~40 lessons** in the **`40000–49999`** lesson-number band (`Lesson40100_LookaroundsTutorial` etc.). - **Two lesson kinds** — *tutorial* (read-and-continue) and *challenge* (graded against a `RegexPlan` reference solution). - **Backend wiring** — `RegexLessonStrategyBase` + per-chapter strategy files, `RegexLessonRegistry`, MediatR `GetLessonAttemptQueryHandler` / `CompleteTutorialCommandHandler`. Browser-graded attempts are stashed in a long-lived `IOfflineAttemptStore` (Mongo) **and** the short-lived `ILessonAttemptCache` (Redis) so completion survives a Redis restart. - **Idempotent tutorial completion** — `LessonsController.CompleteTutorial` now returns `200 OK { JustCompleted: false }` for stray re-clicks on already-finished tutorials instead of `410 Gone`. Resolves the misleading console error reported during dogfooding. - **Lesson pane** — `lib-regex-lesson-pane` shows instructions, hints, expected-match preview, real-time grader feedback, gated `Continue` button (disabled while POST in flight, label flips to `Saving…` → `Completed`). - **Catalog navigation** — `LessonCatalogService` chapters → in-component **Lessons menu** (header dropdown) AND **Lessons sidebar pane** stay in lockstep via the shared `activeLessonNumber()` signal; selecting in either surface updates both. #### UX scaffolding - **Mode toggle** — clear Sandbox ↔ Lesson switch in the menu bar (preserves sandbox state via `sandboxStateSnapshot` so users can scratch around mid-lesson without losing work). - **Guided tour** — `RegExTourService` implementing `IntroTourProvider`; `data-tour="…"` anchors on every notable surface (pattern bar, flag toggles, test text, tools tabs, lessons menu, both side panes, mode toggle, tour button itself). Step list is filtered against live DOM at start time so missing-element steps never explode the tour. - **Three-column desktop layout** — **lessons-pane (left, 240px) | main pane (1fr) | cheatsheet-pane (right, 260px)** by default. Each pane is independently togglable from the **View** menu (checkbox-style entries) AND has its own in-pane close button. Header shows a `Show Cheatsheet` button only when the pane is hidden — no duplicate label noise. - **Pane swap** — *View → Swap panes* flips lessons↔cheatsheet sides for users who prefer the curriculum on the right. - **Responsive collapse** — at ≤1280px the cheatsheet column drops out of the grid (still togglable); at ≤900px the layout collapses to a single column with panes stacking above the main pane. - **File menu** — *Save to Browser / Download to Device / Load from Browser / Load from Device* via shared `SaveContentDialogComponent` + `LoadContentDialogComponent` (parameterized to use `SpikerSoftRegExStorage` / `savedRegex` IndexedDB store, distinct from the JSON tool's). Persists pattern + flags + test text + replaceWith + listJoiner. #### Persistence + migration - All editor state + pane preferences saved to `localStorage` under `spikersoft.regex-tool.state.v1`. - `restoreState()` reads two generations of legacy keys (`sidebarMode` string union, very-old `cheatsheetOpen` boolean) and migrates to the current `cheatsheetVisible` / `lessonsVisible` / `panesSwapped` booleans — covered by unit tests. ### Quality bar - **Frontend** — `nx test tools`: **487 passed / 2 skipped (489 total)** across 39 spec files. Includes targeted coverage for pane toggles, swap persistence, legacy-state migration (3 separate tests), in-pane close button DOM, header `Show Cheatsheet` rendering, lesson-mode auto-open w/ manual-hide tracking, idempotent tutorial completion gating, and the regex-curriculum reference-solution validator (verifies every `RegexPlan.ExpectedMatches` against the lesson's reference solution). - **Backend** — `LessonsControllerTests`: **31/31 passing**, including new cases for the offline-store fallback path and the idempotent re-completion no-op. `RegexCurriculumTests` validates lesson-number ranges and reference-solution↔plan agreement for every lesson. - **Build** — `nx build tools` clean; no lint errors (eslint / stylelint / angular-eslint template). ### Related tickets / follow-ups - #19 — *Development > Regular Expressions don't work* (referenced; this port is what supersedes it). - #41 — Regexp People **game** native port (sister, distinct product). ### Attribution SpikerSoft port is a clean-room Angular re-implementation inspired by [gskinner/regexr](https://github.com/gskinner/regexr/) (GPL-3.0). Attribution + license link appear in the in-tool footer and in component-level doc comments.
Sign in to join this conversation.