ultimate-jekyll-manager 1.9.4 → 1.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +6 -0
- package/dist/assets/css/core/_utilities.scss +5 -2
- package/dist/assets/js/libs/form-manager.js +7 -8
- package/dist/commands/install.js +2 -2
- package/dist/commands/setup.js +2 -1
- package/dist/defaults/.github/workflows/build.yml +3 -1
- package/dist/defaults/dist/_includes/core/body.html +1 -7
- package/dist/defaults/dist/_includes/themes/classy/frontend/sections/footer.html +9 -1
- package/dist/defaults/dist/_layouts/blueprint/admin/calendar/index.html +1 -1
- package/dist/defaults/dist/_layouts/blueprint/admin/firebase/index.html +2 -2
- package/dist/defaults/dist/_layouts/blueprint/admin/studio/index.html +1 -1
- package/dist/defaults/dist/_layouts/blueprint/admin/users/index.html +2 -2
- package/dist/defaults/dist/_layouts/blueprint/admin/users/new.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html +13 -13
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/reset.html +2 -2
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/signin.html +3 -3
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/signup.html +3 -3
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/index.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/contact.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/download.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/feedback.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/index.html +2 -6
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/payment/checkout.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/portal/email-preferences.html +2 -3
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/status.html +1 -1
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/index.html +1 -1
- package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/contact.html +1 -1
- package/dist/defaults/dist/pages/test/libraries/form-manager.html +31 -31
- package/dist/lib/safe-install.js +13 -0
- package/package.json +8 -1
- package/.nvmrc +0 -1
- package/CHANGELOG.md +0 -792
- package/CLAUDE-ATTRIBUTION.md +0 -215
- package/PROGRESS.md +0 -27
- package/TODO-ATTRIBUTION2.md +0 -753
- package/TODO-AUTH-TESTING.md +0 -165
- package/TODO-BACKEND.md +0 -13
- package/TODO-CHAT1.md +0 -41
- package/TODO-CONSENT-LIVETEST.md +0 -201
- package/TODO-NEW.md +0 -21
- package/TODO-themes.md +0 -34
- package/TODO-tracking.md +0 -69
- package/TODO-tracking2.md +0 -103
- package/TODO.md +0 -415
- package/_notes/CLAUDE-pages.md +0 -19
- package/_notes/CLAUDE-pages2.md +0 -18
- package/_notes/LOGOS.md +0 -1
- package/_notes/NOTES-app.md +0 -92
- package/_notes/NOTES-sub-2.md +0 -42
- package/_notes/NOTES-sub-ai.md +0 -165
- package/_notes/NOTES-sub-endpoints.md +0 -44
- package/_notes/NOTES-sub-example.md +0 -92
- package/_notes/NOTES-sub-plan.md +0 -36
- package/_notes/NOTES-sub-status.md +0 -123
- package/_notes/TODO-Claude.rtf +0 -105
- package/_notes/TODO-FileStructure.md +0 -249
- package/_notes/TODO-checkout.md +0 -33
- package/_notes/TODO-frontend.md +0 -61
- package/_notes/TODO-legacy.md +0 -18
- package/_notes/TODO-template-system.md +0 -73
- package/_notes/TODO-theme.md +0 -17
- package/logs/test.log +0 -144
package/CHANGELOG.md
DELETED
|
@@ -1,792 +0,0 @@
|
|
|
1
|
-
# CHANGELOG
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
|
-
|
|
7
|
-
## Changelog Categories
|
|
8
|
-
|
|
9
|
-
- `BREAKING` for breaking changes.
|
|
10
|
-
- `Added` for new features.
|
|
11
|
-
- `Changed` for changes in existing functionality.
|
|
12
|
-
- `Deprecated` for soon-to-be removed features.
|
|
13
|
-
- `Removed` for now removed features.
|
|
14
|
-
- `Fixed` for any bug fixes.
|
|
15
|
-
- `Security` in case of vulnerabilities.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
## [1.9.4] - 2026-06-20
|
|
19
|
-
|
|
20
|
-
### Fixed
|
|
21
|
-
- **Blog post ads no longer render inside blockquotes.** Ad insertion now filters out `<p>` elements inside `<blockquote>`, `<details>`, and `<figure>` so ads only appear between top-level content blocks.
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
## [1.9.3] - 2026-06-18
|
|
25
|
-
|
|
26
|
-
### Changed
|
|
27
|
-
- **Notifly build-error notifications use absolute path.** Switched from bare `notifly` keyword to full application path (`/Applications/Notifly.app/Contents/MacOS/Notifly`) so the notification binary resolves reliably regardless of PATH.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
## [1.9.2] - 2026-06-17
|
|
31
|
-
|
|
32
|
-
### Added
|
|
33
|
-
- **Account page: API & MCP section.** Renamed "API keys" to "API & MCP". New MCP integration card with server URL (copy-to-clipboard) and tabbed setup instructions for 9 providers: Claude, Cursor, VS Code, Codex, Gemini CLI, OpenCode, Windsurf, Zed, and Other. MCP URL built dynamically from `webManager.getApiUrl()`.
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
## [1.9.1] - 2026-06-17
|
|
37
|
-
|
|
38
|
-
### Changed
|
|
39
|
-
|
|
40
|
-
- **FormManager: snapshot-and-restore disabled-state model (replaces `data-fm-keep-disabled`).** `_init()` now snapshots every element that has `disabled` in HTML markup (excluding submit buttons — those are loading guards FM takes over). Snapshotted elements stay disabled through every state transition automatically — no data attributes needed. Recommended form pattern: `<form data-form-state="initializing" onsubmit="return false">` with CSS `form[data-form-state]:not([data-form-state="ready"]) { pointer-events: none; }`. Documented in `docs/javascript-libraries.md`.
|
|
41
|
-
|
|
42
|
-
### Added
|
|
43
|
-
|
|
44
|
-
- **Comprehensive FormManager test suite (30 page-layer tests).** Disabled-state snapshot (5 tests), getData/setData/input groups (12 tests), validation/honeypot/file-accept (13 tests). Previously FormManager had zero automated tests.
|
|
45
|
-
- **Visual Test 7 on the FM test page** (`/test/libraries/form-manager`) — permanently disabled elements, submit cycle demo, rapid-cycle button for manual verification.
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
## [1.9.0] - 2026-06-17
|
|
49
|
-
|
|
50
|
-
### Added
|
|
51
|
-
|
|
52
|
-
- **Token page: MCP OAuth flow.** When the `/token` page receives `?mcp=true&redirect_uri=...&state=...`, it completes an OAuth-style handshake — the user signs in via Firebase Auth, the page obtains a fresh ID token, and redirects back to the `redirect_uri` with `code=<idToken>&state=<state>`. This lets Claude (or any MCP client) authenticate users through the existing sign-in UI without a custom OAuth server.
|
|
53
|
-
|
|
54
|
-
### Changed
|
|
55
|
-
|
|
56
|
-
- **Dev-URL guidance updated** — docs (local-development.md, themes.md, cdp-debugging.md, CLAUDE.md) now say "prefer `https://localhost:4000`; fall back to the network IP if localhost doesn't connect" instead of hardcoding a specific IP.
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
## [1.8.2] - 2026-06-14
|
|
60
|
-
|
|
61
|
-
### Added
|
|
62
|
-
|
|
63
|
-
- **FormManager: `data-fm-keep-disabled` opt-out for permanently-disabled fields.** `_setDisabled` blanket-toggles `disabled` on every control in the form (loading/submitting ↔ ready), which silently re-enabled fields meant to STAY disabled — e.g. "coming soon" radio options rendered inside a managed form. Controls carrying `data-fm-keep-disabled` are now always forced to `disabled = true` and never re-enabled. Documented in `docs/javascript-libraries.md` (FormManager section).
|
|
64
|
-
- **`docs/cdp-debugging.md` — launching a controllable browser (mirrored across UJM/BEM/BXM/EM).** The canonical Chrome launch for agents and humans: CDP port + REQUIRED dedicated `--user-data-dir` (Chrome 136+ silently ignores the debug port on the default profile — verified on 149), the persistent agent profile (`~/Library/Application Support/chrome-profiles/agent` — log in once, state survives relaunches, verified), the shared-instance model (CDP is multi-client — agents share the one logged-in Chrome on one port, one tab each; a second profile/port only for a second identity), safe quit by profile match, and driving via the `chrome-devtools` MCP (`CHROME_CDP_PORT` set before the session) or any CDP client. UJM flavor: point it at the dev server for the edit → live-reload → screenshot/console/network loop. Indexed in CLAUDE.md.
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
## [1.8.1] - 2026-06-11
|
|
68
|
-
|
|
69
|
-
### Changed
|
|
70
|
-
|
|
71
|
-
- **`docs/audit.md` rewritten as the full-audit check catalog (`/omega:ujm audit`).** The two-stage site audit became an ID'd catalog: mirrored universal checks (U-01..U-14 — tests at every layer, XSS, secrets, config canon, doc parity, dead code, dep health, …), UJM-specific checks (UJM-01..UJM-10 — inline-script ban, theme-prefixed classes, content writing rules, SEO meta, purge safelist, reads-vs-writes, page-module pattern, images, a11y), and framework-repo checks (F-01..F-04 — sister parity, defaults sync, docs completeness, green framework suite), with scope auto-detect (consumer vs framework via package.json), a persisted findings report (`.temp/audit/claude-audit.md`), a severity-ordered TodoWrite fix loop, and the `npx mgr audit` automated stage absorbed from the old doc. Wired to the `omega:ujm` router's Audit process; `docs/audit.md` is mirrored across BEM/BXM/EM. CLAUDE.md's docs index updated to match.
|
|
72
|
-
- **package.json `keywords` corrected** — replaced the stale template list (`Autoprefixer`, `imagemin` — not used; `Browsersync` — true but noise) with accurate, discovery-oriented ones (`jekyll`, `static-site`, `static-site-generator`, `website`, `seo`, `gulp`, `sass`, `webpack`, `postcss`). npm-listing metadata only; no behavior change. Mirrored across BEM/BXM/EM.
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
## [1.8.0] - 2026-06-11
|
|
76
|
-
|
|
77
|
-
### Added
|
|
78
|
-
|
|
79
|
-
- **`docs/migration.md` + expanded `docs/audit.md` + dashboard-pages pattern (action-skill consolidation).** The standalone `UJM:migrate`/`UJM:audit`/`UJM:new-page`/`UJM:new-site` skills were deleted and folded into `omega:ujm` as process checklists; their framework facts landed in the repo: new `docs/migration.md` (full old-UJ→UJM migration incl. `_legacy/` flow + config re-mapping, `_config.yml` quick-fix key schema, revert-posts procedure), `docs/audit.md` expanded to the full two-stage workflow (AI content pass → `npx mgr audit` fix loop), and `docs/layouts-and-pages.md` gained the dashboard list/detail/edit page pattern (separate pages, `?id=` redirects, breadcrumbs).
|
|
80
|
-
- **Dev-process guidance relaxed: only `npm start` is off-limits.** The "NEVER run" rule in CLAUDE.md + `src/defaults/CLAUDE.md` now prohibits only the long-running dev server (instruct the user to start it if it isn't running; read `logs/*.log`, never tail the process) — `npx mgr test` and `npm run build` are fine to run.
|
|
81
|
-
- **Skills-as-routers migration — `docs/no-inline-scripts.md` + `docs/purgecss.md` (new), plus `seo.md` / `layouts-and-pages.md` / `javascript-libraries.md` / `assets.md` extensions.** Framework facts migrated from the `omega:ujm` skill into the repo so they version-match the installed package: `no-inline-scripts.md` carries the full hard-rule playbook (where scripts move, Liquid `data-*`/`<template>`/conditional bridges, window-global callbacks, step-by-step migration, verification grep); `purgecss.md` carries the safelist playbook (two locations, RegExp anchoring, `variables: false` gotcha, per-file processing, current UJM safelist, category guide); `seo.md` retitled "SEO & Content" and gains the content writing rules (action-verb H1s, sentence case, headline/accent structure, frontmatter SEO, superheadline rules) + Services and Solutions page strategies + alternatives content guidelines; `layouts-and-pages.md` gains default-page customization rules (.md vs .html, page exclusions), the per-page customization-levels table (homepage/pricing/about/contact/download incl. hero display modes, pricing feature comments, testimonial/FAQ guidelines), and the custom-page creation checklist; `javascript-libraries.md` gains the reads-vs-writes rule (Firestore SDK for dashboard reads, Cloud Functions for writes) + Firestore read examples; `assets.md` gains the account-dropdown item field reference. The `data-wm-bind` deep reference moved to `web-manager/docs/bindings.md` (the module that owns the feature). All indexed in CLAUDE.md; the skill is now a thin router (pointers + hard rules + process checklists).
|
|
82
|
-
|
|
83
|
-
### Changed
|
|
84
|
-
|
|
85
|
-
- **Router skill renamed `UJM:patterns` → `omega:ujm`** — all framework skills now live under the `omega:` namespace (`omega:em`/`omega:bxm`/`omega:ujm`/`omega:bem` + the `omega:main` hub). CLAUDE.md's Recommended skills section updated.
|
|
86
|
-
- **newsflash homepage post slots are strictly deduplicated.** Each section consumes its own slice of `site.posts` so no story repeats down the page: hero = post 1, top stories = 2–4, "the latest" feed = 5–9, most-read rail = 10–14, "more to chew on" = 15–17 (only the ticker repeats the latest — it's chrome, not content). Sections disappear gracefully when the site doesn't have enough posts to fill their slice.
|
|
87
|
-
- **Dev builds raise `--limit_posts` from 15 to 18** so a fully-populated newsflash front page (17 distinct posts) is visible during development (`--all-posts` still disables the cap entirely).
|
|
88
|
-
- **Footer appearance picker is now icon-only.** The toggle button in classy's and newsflash's footers shows just the mode icon (`data-appearance-icon` spans) — the `data-appearance-current` text label was removed from the button (the mode words remain in the dropdown items). Pure HTML change; the framework `data-appearance-*` logic is untouched. Docs updated in [docs/themes.md](docs/themes.md#the-appearance-picker-is-required-in-every-footer) + [docs/appearance.md](docs/appearance.md).
|
|
89
|
-
- **newsflash homepage desk cards link to their category pages.** Each desk card is now a story-card link (`href` per item, falling back to the slugified title → `/blog/categories/<slug>`) with a "Read the desk" affordance.
|
|
90
|
-
- **newsflash testimonials moved from the homepage to the about page.** The "From our readers" quote-card section (universal `testimonials` key) now lives on about between the principles and the team CTA; the `.quote-card` styles moved from the homepage page CSS to the about page CSS.
|
|
91
|
-
- **newsflash nav + footer include forks deleted — chrome now inherits from classy.** The nav fork was a verbatim copy of classy's (the fallback regenerates an identical file); the footer's editorial look (oversized serif wordmark, hidden avatar, 24em description measure, panel-color repaints of the `.link-muted`/`.text-body` utilities, weight-800 volt column heads) was ported into the theme's `_general.scss` footer rules, so classy's inherited markup renders the same ink-slab design in both modes.
|
|
92
|
-
- **Classy footer language icon is now theme-adaptive.** The language dropdown button's hardcoded multicolor inline SVG (fixed fills that ignored `currentColor` and clashed on dark surfaces like newsflash's ink-slab footer) is replaced with `{% uj_icon "language" %}`, which inherits the button's color in every theme and mode.
|
|
93
|
-
- **`docs/project-structure.md` renamed `docs/directory-structure.md`** (H1 `# Project Structure` → `# Directory Structure`) for cross-framework doc-file parity — BEM names the same concept `docs/directory-structure.md`, and mirrored docs must match down to the file name. All references updated (`CLAUDE.md`, historical CHANGELOG links). Also normalized `docs/test-boot-layer.md`'s H1 to `# Test Framework — Boot Layer` (matches BXM; EM normalized likewise).
|
|
94
|
-
|
|
95
|
-
### Added
|
|
96
|
-
|
|
97
|
-
- **Docs parity — new `docs/build-system.md`, `docs/templating.md`, `docs/logging.md`, `docs/common-mistakes.md`.** `build-system.md` makes CLAUDE.md's existing pipeline-reference link real (was a dead link) — gulp task list, config flow, build modes, pure helpers; `templating.md` graduates from "(planned)" — node-powertools bracket conventions + Liquid coexistence; `logging.md` is now the SSOT for the log-file tee (extracted from local-development.md, which keeps a pointer — mirrors EM/BXM); `common-mistakes.md` extracts the canonical anti-pattern list into the repo (BEM already had one). All indexed in CLAUDE.md → Documentation.
|
|
98
|
-
- **Test coverage convention (docs).** New mirrored "Test coverage" sections in `CLAUDE.md`, `docs/test-framework.md`, `src/defaults/CLAUDE.md`, and `src/defaults/test/README.md` — every feature ships with tests at every layer it has a surface in (logic `build`/`page`, UI `page`, end-to-end `boot`); a layer is skipped only when the feature genuinely has no surface there. Mirrored across EM/BXM/BEM.
|
|
99
|
-
- **New `newsflash` theme — editorial news site.** Paper + ink + vermilion design language with volt highlights: Fraunces serif headlines over Schibsted Grotesk, a live news-ticker marquee above the sticky blurred masthead, framed editorial images, hard-offset "lift" pill buttons, film-grain overlay, and first-class dark mode (deep warm near-black paper, cream ink). Ships custom layouts for base (fonts + ticker), homepage (cover-story hero + top-story tiles + "the latest" editorial post feed with sticky most-read/newsletter rails + numbered rundown playbook + "more to chew on" story tiles + dark big-read CTA band), blog index (lead-story splash + editorial tiles), blog post (reading-progress bar, drop cap, serif crossheads, pullquotes, author card), blog category archives (desk directory with stroked story-count numerals + per-desk fronts), blog tag archives (topics wall + per-topic fronts), pricing (membership tiers with "Editor's pick" + flash-sale promo banner), about (newsroom timeline), contact ("contact the desk" + tips line), team ("the masthead" byline cards + ink-slab charter + newsroom principles), team member (reporter profile with beat facts strip), and a 404 "correction notice". Functional pages (download, feedback, updates, auth, account) intentionally inherit classy markup restyled. Select with `theme.id: "newsflash"`. See [docs/themes.md](docs/themes.md).
|
|
100
|
-
- **Theme-authoring convention: genre-native frontmatter defaults.** A theme's layout frontmatter defaults are part of its identity and must be written for the theme's genre (newsflash ships news copy + news-purposed `rundown`/`desks` homepage sections), never copied from classy's SaaS demo data. Universal section keys (`hero`, `cta`, `stats`, `faqs`, `pricing.plans`, …) stay shared so consumer overrides survive theme swaps. Documented in [docs/themes.md](docs/themes.md#frontmatter-defaults-are-part-of-the-themes-identity) + the Path A authoring checklist.
|
|
101
|
-
- **Appearance picker in every footer.** Classy's footer now ships the appearance (light/dark/system) dropdown next to the language dropdown — a pure drop-in block driven by the framework's `data-appearance-*` attributes. Every theme inherits it via the footer fallback; themes with custom footers (newsflash) include it themselves. Documented as required footer furniture in [docs/themes.md](docs/themes.md) + [docs/appearance.md](docs/appearance.md).
|
|
102
|
-
- **Theme-authoring convention: inherit classy's nav + footer chrome, restyle via CSS.** Themes do NOT fork the chrome includes — `copyFallbackThemeFiles()` supplies classy's nav/footer (with paths rewritten into the theme's namespace) and the chrome's identity comes from theme CSS (newsflash's serif masthead + editorial ink-slab footer are pure `css/layout/` restyles of classy's markup). Fork an include only when the structure genuinely diverges — a re-skin fork is a copy that silently drifts (newsflash's nav fork had diverged from classy by nothing but a comment, and the icon-only picker fix had to be applied twice). Documented in [docs/themes.md](docs/themes.md) + the Path A authoring checklist.
|
|
103
|
-
- **Theme-contract build test.** New `mgr:build/theme-contract` suite turns the docs/themes.md conventions into executable assertions, globbing every shipped theme (`_template` included) so a new theme is covered the moment it lands: entry files + `$avatar-sizes` + shared bootstrap-overrides import, `[ site.theme.id ]` bracket parents (swappability), no theme-prefixed classes in markup, no inline `<script>` bodies, the pricing JS-contract markers, `blog-post-content` in post layouts, and page-asset files matching a layout-declared `asset_path` shape (wrong shape = silent bundle skip). It caught neobrutalism's missing promo banner + `.card-title` on its first run.
|
|
104
|
-
- **Shared plan-pricing include — `core/pricing/resolve-plan.html`.** The plan price-resolution Liquid (product lookup from `site.web_manager.payment.products`, frontmatter-over-config monthly/annual precedence, per-unit math) was copy-pasted across all three themes' pricing layouts; it's now a single shared "logic include" (Jekyll includes share caller scope) that every theme calls per plan and renders the assigned variables from. Also guards the per-unit `divided_by` against nil/zero unit values, which previously crashed the build when the per-unit feature value couldn't resolve.
|
|
105
|
-
- **Theme-authoring convention: develop in ONE appearance mode, ship with BOTH.** Build against a primary mode (the consumer's `theme.appearance` default), then validate light AND dark before declaring the theme done — including a live click-through of the footer appearance picker. Documented in [docs/themes.md](docs/themes.md) authoring conventions + validation checklist.
|
|
106
|
-
- **Theme-authoring gotchas documented:** theme page bundles re-emit Bootstrap after the main bundle (use doubled selectors — `:root:root`, `[data-bs-theme="dark"][data-bs-theme="dark"]`, `.btn.btn`), and `.bg-body-*`/`.text-body-*` utilities paint from the `--bs-*-rgb` companion variables, which must be remapped alongside the hex vars. See [docs/themes.md](docs/themes.md).
|
|
107
|
-
- **`npx mgr blogify --count=<n>`.** The blogify command now accepts a post count (default 12, matching the old hardcoded behavior) — e.g. `--count=18` fills every deduplicated post slot on the newsflash homepage. Documented in README + the CLAUDE.md CLI table.
|
|
108
|
-
- **`npx mgr test` tees output to `logs/test.log`.** All test-runner output is now duplicated (ANSI-stripped) to `<projectRoot>/logs/test.log`, truncated fresh on each run, via the existing `attach-log-file` util (skipped on CI through `isServer()`) — mirrors EM's and BEM's `test.log` pattern. Grep the file after a run instead of scrolling terminal scrollback.
|
|
109
|
-
- **`TEST_EXTENDED_MODE` — extended test mode (`--extended`).** `npx mgr test --extended` (or `TEST_EXTENDED_MODE=true npx mgr test`) opts in tests that hit REAL external services (network fetches, Firebase via web-manager, live APIs); off by default so `npx mgr test` stays fast and offline-safe. The signal is the unprefixed `TEST_EXTENDED_MODE` env var — the SAME name across BEM/BXM/UJM/EM (cross-framework parity) — and once set it propagates to every spawned child (the Jekyll build, the boot HTTP server / Puppeteer browsers) via inherited `process.env`. A warning prints (and is teed to `logs/test.log`) when on. Gate live tests on `process.env.TEST_EXTENDED_MODE`. New `src/test/utils/extended-mode-warning.js` is the SSOT for the warning copy. Documented in [docs/test-framework.md](docs/test-framework.md#extended-mode-test_extended_mode) + the env-vars table.
|
|
110
|
-
|
|
111
|
-
### Changed
|
|
112
|
-
|
|
113
|
-
- **Test command standardizes on `TEST_EXTENDED_MODE`, replacing `--integration` / `UJ_TEST_INTEGRATION`** (no backwards compat — the old flag/env are gone). Mirrors the canonical unprefixed `TEST_EXTENDED_MODE` shared across BEM/BXM/EM.
|
|
114
|
-
|
|
115
|
-
### Fixed
|
|
116
|
-
|
|
117
|
-
- **`attach-log-file` no longer truncates `logs/test.log` mid-run.** The tee state is now wrapped in a `createTee()` factory (independent, stackable instances) with the process-wide singleton built on top. A later `attach()` captures the CURRENT `process.stdout.write` (which may already be an outer tee) and restores that exact reference on `detach()`, so stacked tees fan out and unwind in LIFO order. The new build-layer `attach-log-file` test uses its OWN `createTee()` instance, so exercising attach/detach no longer detaches the live singleton tee that's capturing the actual run (which previously cut `logs/test.log` to ~9 lines). UJM's name-based signature (`attachLogFile('test')`) and synchronous `detach()` are preserved. Mirrors EM's `createTee()` refactor.
|
|
118
|
-
|
|
119
|
-
- **neobrutalism pricing page now satisfies the full framework JS contract.** Same two gaps newsflash had: the flash-sale promo banner block (`#pricing-promo-banner` + badge/text/countdown/code ids, shipped `hidden`) was missing entirely, and plan names lacked the `.card-title` class the pricing JS reads for analytics. Both caught by the new theme-contract test on its first run.
|
|
120
|
-
- **Classy admin minimal layouts use bracket parents.** `admin/core/minimal.html` + `minimal-viewport-locked.html` hardcoded `layout: themes/classy/backend/...` instead of the `themes/[ site.theme.id ]/...` convention (the fallback's path rewrite masked it for non-classy themes). Normalized to brackets; now enforced by the theme-contract test.
|
|
121
|
-
- **newsflash nav + dropdown hover text is readable again.** The hover fill (ink pill) left the label ink-on-ink: the shared nav/account includes put Bootstrap's `.text-body` utility (`!important`) on every link and dropdown item, which beat the theme's hover `color`. The hover/active colors now carry `!important`, and dropdown-item child spans with text utilities flip to `inherit` on hover (fixes the blacked-out account-dropdown rows too).
|
|
122
|
-
- **newsflash dropdown item pills no longer press edge-to-edge against the panel.** Bootstrap's re-emitted `.dropdown-menu` rule (`--bs-dropdown-padding-x: 0`) was stripping the theme's panel inset on pages with page CSS — the highlighted pill touched the panel edges. The theme's dropdown rule now uses the doubled selector (`.dropdown-menu.dropdown-menu`), the established page-bundle-gotcha pattern.
|
|
123
|
-
- **newsflash headings no longer go thin on pages with page CSS.** Bootstrap's re-emitted `.display-*` / `.lead` rules (stock weights 300) were clobbering the theme's main-bundle typography on every page that ships a page bundle (post + about read spindly; contact — no page bundle — looked right). Type metrics now ship as Bootstrap variables in the config `@forward with (...)` block (`$display-font-weight: 550`, `$display-line-height: 1.04`, `$lead-font-weight: 400`, `$headings-line-height: 1.1`, `$line-height-base: 1.55`) so every Bootstrap copy compiles them natively. Documented as an extension of the page-bundle gotcha in [docs/themes.md](docs/themes.md).
|
|
124
|
-
- **newsflash post hero image fills its frame.** Two stacked causes: `uj_post` image-tags render as `<picture><img>`, so the img's percentage height resolved against the auto-height picture wrapper (the base `.art-frame` now makes `picture` fill the frame), and the framework's generic `.blog-post-image { max-height: 480px }` capped the image inside the 21/10 frame (the article-hero rule now lifts it with `max-height: none`).
|
|
125
|
-
- **Footer brand description no longer disappears when its data value liquifies to empty.** Both classy's and newsflash's footers resolve the brand blurb via `data.logo.description | default: site.brand.description`, but `default:` only sees the RAW string — a footer.json value like `'{{ site.meta.description }}'` with no `meta.description` set passed the default check and then liquified to nothing, hiding the description even when `brand.description` was set. The footers now re-check after liquification and fall back to `site.brand.description`.
|
|
126
|
-
- **newsflash dark mode no longer stomps consumer brand colors.** The dark-mode `--bs-primary`/`--bs-link-color` remaps were hardcoded to the brightened vermilion, so a consumer's `$primary` override (via `main.scss` `with (...)`) held in light mode but snapped back to vermilion in dark. The remap now derives from the compile-time `$primary` (stock vermilion keeps its hand-tuned brightening; other brand colors get a generic white-mix lift). Documented as a theme-authoring gotcha in [docs/themes.md](docs/themes.md#derive-dark-mode-brand-remaps-from-primary-gotcha).
|
|
127
|
-
- **newsflash pricing page now satisfies the full framework JS contract.** Added the flash-sale promo banner (`#pricing-promo-banner` + badge/text/countdown/code ids, shipped `hidden` — the framework pricing JS reveals it, fills the rotating sale name + countdown, and offsets the masthead) and the `.card-title` class on plan names (read for add-to-cart analytics). The pricing JS contract — billing radios, `.amount`/`.billing-info`/`.price-per-unit` data attributes, `button[data-plan-id]`, plan-name selector, promo banner ids — is now documented as a required cross-theme contract in [docs/themes.md](docs/themes.md#the-pricing-page-has-a-js-contract-too).
|
|
128
|
-
- **`npx mgr test <target>` now correctly scopes by source.** The positional test target was previously ignored — every run executed all suites regardless of the argument. It now selects which test FILES run: `project:` runs project tests only, `mgr:` / `ujm:` / `framework:` run framework tests only (`mgr:` is the universal cross-framework alias, equivalent to the UJM-specific `ujm:` / `framework:`), and a bare path (no prefix) matches both sources. The `--filter=<substring>` flag stays orthogonal — it matches test NAMES within the already-selected files and composes with the target. See [docs/test-framework.md](docs/test-framework.md#filtering-tests).
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
## [1.7.2] - 2026-06-09
|
|
132
|
-
|
|
133
|
-
### Added
|
|
134
|
-
|
|
135
|
-
- **Push notification subscribe on payment confirmation CTA click** — hooks `subscribe()` onto CTA button clicks on the payment confirmation page. Requires a user gesture for the browser permission prompt, so uses `{ once: true }` click listeners.
|
|
136
|
-
|
|
137
|
-
### Fixed
|
|
138
|
-
|
|
139
|
-
- **FormManager spinner layout in icon-only buttons** — `_showSpinner()` no longer adds `me-2` margin when `submittingText` is empty. Previously, the unnecessary right margin squished the spinner off-center in small round buttons (e.g. chat send).
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
## [1.7.1] - 2026-06-09
|
|
143
|
-
|
|
144
|
-
### Added
|
|
145
|
-
|
|
146
|
-
- **Auto-subscribe push notifications on authenticated page load** — calls `webManager.notifications().subscribe()` in the core auth listener after the consent guard passes. Fires for both fresh signups and returning sign-ins. Failure logs a warning and never blocks navigation.
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
## [1.7.0] - 2026-06-09
|
|
150
|
-
|
|
151
|
-
### Added
|
|
152
|
-
|
|
153
|
-
- **Animation Studio admin page** (`/admin/studio`). New default admin page for creating screen-recording-ready product demo animation clips. Framework provides all boilerplate: sidebar, FormManager controls, resolution picker (540p–4K), aspect ratio toggle (16:9/9:16), playback loop with speed/pause controls, and 60fps tab recording via `getDisplayMedia` + `CropTarget`.
|
|
154
|
-
- **Clip builder helpers** — `flowClip`, `cardClip`, `chatClip` passed alongside `animate`/`el` to clip `build()` functions. Consumers declare data, not structure.
|
|
155
|
-
- **Studio CSS partial** (`_studio.scss`) — importable via `@use 'studio'` in consumer page CSS. Includes boilerplate layout + generic animation primitives (`.s-hidden`, `.s-step-bg`, `.s-bar-fill`).
|
|
156
|
-
- **`docs/animation-studio.md`** — full reference for the clip contract, helpers, recording, resolution, aspect ratios.
|
|
157
|
-
- **Admin sidebar entry** for Animation Studio under new "Content" section.
|
|
158
|
-
|
|
159
|
-
---
|
|
160
|
-
## [1.6.9] - 2026-06-08
|
|
161
|
-
|
|
162
|
-
### Added
|
|
163
|
-
|
|
164
|
-
- **Bootstrap-first theming convention.** Added comprehensive guidance to `docs/themes.md` and the default consumer `CLAUDE.md` — themes must restyle Bootstrap classes, not create parallel design systems.
|
|
165
|
-
- **Dev workflow documentation.** Added explicit instructions to `CLAUDE.md` and consumer defaults to check `logs/dev.log` instead of running `npm start`/`npm test` in consumer projects.
|
|
166
|
-
|
|
167
|
-
### Fixed
|
|
168
|
-
|
|
169
|
-
- **Signup metadata failure notification timeout.** Changed dev-only notification from permanent (`timeout: 0`) to 1 second (`timeout: 1000`) so it doesn't block the screen during development.
|
|
170
|
-
|
|
171
|
-
---
|
|
172
|
-
## [1.6.8] - 2026-06-07
|
|
173
|
-
|
|
174
|
-
### Fixed
|
|
175
|
-
|
|
176
|
-
- **Lazy loading animation flash.** Elements with `data-lazy="@class animation-*"` were briefly visible before the IntersectionObserver fired, causing a jarring flash (visible → disappear → fade-in). Added CSS rule to hide these elements from initial paint so they smoothly animate in.
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
## [1.6.5] - 2026-06-04
|
|
180
|
-
|
|
181
|
-
### Added
|
|
182
|
-
|
|
183
|
-
- **Dynamic event fitting in month view.** Calendar cells now show as many events as physically fit instead of a hardcoded max of 3; "+N more" only appears when events genuinely overflow.
|
|
184
|
-
- **Local time display on calendar events.** Event pills show local time (data layer remains UTC). Editor modal shows a local date+time badge below the UTC inputs.
|
|
185
|
-
- **Hover states on event pills.** Brightness + box-shadow effect on hover for month, week, and day views.
|
|
186
|
-
- **Now line on month view.** Red progress line shows current time of day in today's cell, with dot rendered above cell borders.
|
|
187
|
-
- **Left tab accent on all event pills.** Consistent dark left border matches week/day view style.
|
|
188
|
-
- **Monthly-weekday (Nth weekday) recurrence pattern.** Calendar supports "2nd Wednesday of every month" style recurring campaigns with calendar-relative date stepping.
|
|
189
|
-
|
|
190
|
-
### Changed
|
|
191
|
-
|
|
192
|
-
- **Per-string translation caching replaces all-or-nothing page caching.** Each page's cache is now a `hash→translation` map (`es/pages/index.html.json`). Only strings whose content hash changed are sent to the API — unchanged strings are served from cache. Dramatically reduces API calls and cost on incremental builds.
|
|
193
|
-
- **Prompt hash mismatch now wipes page cache files** (not just meta entries) for a clean slate.
|
|
194
|
-
- **Translation stats now track cached vs new strings** instead of whole-page hit/miss.
|
|
195
|
-
- **Outside-month calendar cells** now fade only content (date number + events), keeping borders at full opacity for consistent grid lines.
|
|
196
|
-
- **Imagemin constants renamed** (`MAX_SOURCE_DIMENSION` → `IMAGE_MAX_DIMENSION`) and default max dimension lowered from 4096 to 2048.
|
|
197
|
-
|
|
198
|
-
### Removed
|
|
199
|
-
|
|
200
|
-
- **`RECHECK_DAYS`** — per-string content hashes make age-based invalidation unnecessary.
|
|
201
|
-
- **All-day row** removed from week view (no all-day event concept in marketing calendar).
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
## [1.6.4] - 2026-06-03
|
|
205
|
-
|
|
206
|
-
### Fixed
|
|
207
|
-
|
|
208
|
-
- **Workflow template `{ github.secrets }` clobbered `{ github.workflows.build.schedule }`.** Renamed to flat `{ githubSecrets }` key to avoid namespace collision in the template spread.
|
|
209
|
-
|
|
210
|
-
---
|
|
211
|
-
## [1.6.3] - 2026-06-03
|
|
212
|
-
|
|
213
|
-
### Changed
|
|
214
|
-
|
|
215
|
-
- **Workflow template dynamically generates secrets from `.env`.** `defaults.js` reads the default `_.env`, extracts all key names, and produces a `{ github.secrets }` template variable — no more hardcoding individual secrets in `build.yml`.
|
|
216
|
-
- **`publishSecrets()` replaces `publishGitHubToken()` in setup.** Now reads the consumer's `.env` and publishes ALL non-empty keys as GitHub Actions repo secrets (not just `GH_TOKEN`).
|
|
217
|
-
|
|
218
|
-
### Added
|
|
219
|
-
|
|
220
|
-
- **Country flag SVGs:** id, in, ph, pk, ru, vn (modern-square style).
|
|
221
|
-
- **Auto-create `pages/` dir for custom themes** in webpack.js — prevents `Module not found: __theme__/pages` error when a consumer theme lacks a `pages/` directory.
|
|
222
|
-
|
|
223
|
-
### Removed
|
|
224
|
-
|
|
225
|
-
- **`BACKEND_MANAGER_KEY`** removed from workflow template (replaced by dynamic `.env` secrets).
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
## [1.6.2] - 2026-06-02
|
|
229
|
-
|
|
230
|
-
### Fixed
|
|
231
|
-
|
|
232
|
-
- **`npx mgr setup` now merges new `.env` keys into existing consumer projects.** Previously, `ensureCoreFiles()` returned early when `src/_config.yml` existed, skipping the `gulp defaults` task that handles `.env` merging. New framework keys (like `BACKEND_MANAGER_OPENAI_API_KEY`) were never added to consumers that had already run setup once.
|
|
233
|
-
|
|
234
|
-
---
|
|
235
|
-
## [1.6.1] - 2026-06-02
|
|
236
|
-
|
|
237
|
-
### Changed
|
|
238
|
-
|
|
239
|
-
- **Translation system overhauled: JSON array format, gpt-5.4-nano model, .env API key.** Replaced tagged text `[0]...[/0]` format with JSON arrays (eliminates tag corruption). Upgraded from `gpt-4.1-mini` to `gpt-5.4-nano` (3.5x cheaper). API key now read from `BACKEND_MANAGER_OPENAI_API_KEY` in `.env` instead of remote `fetchOpenAIKey()`. Uses GPT-5+ Responses API (`developer` role, `reasoning: { effort: 'low' }`).
|
|
240
|
-
- **Translation concurrency: node-powertools `queue()` replaces batched delays.** Removed 25-page batches with 10s sleeps; now uses `queue({ concurrency: 5 })` for steady throughput.
|
|
241
|
-
- **Translation retries simplified.** Removed recursive subdivision system. On JSON array length mismatch, retries up to `MAX_RETRIES` (2) with file/language context in logs.
|
|
242
|
-
|
|
243
|
-
### Added
|
|
244
|
-
|
|
245
|
-
- **`data-uj-no-translate` HTML attribute** — marks DOM elements (and children) to skip during translation. Applied to country and phone code `<select>` dropdowns on the account page, reducing account.html from ~1241 to ~454 translatable strings.
|
|
246
|
-
- **`BACKEND_MANAGER_OPENAI_API_KEY`** added to default `.env` template.
|
|
247
|
-
- **Prompt cache HIT/MISS logging** with entry counts per language.
|
|
248
|
-
- **`IGNORE_EXCEPTIONS`** — allows specific pages through folder-level exclusions (e.g. `test/translation.html` survives the `test/` folder exclusion).
|
|
249
|
-
|
|
250
|
-
### Removed
|
|
251
|
-
|
|
252
|
-
- **`fetchOpenAIKey()`** — remote API key fetch from `api.itwcreativeworks.com` removed.
|
|
253
|
-
- **Subdivision retry system** (`subdivideAndTranslate`) — replaced by simple length-mismatch retry.
|
|
254
|
-
- **`test/`, `team/`, `updates/` folders** excluded from translation by default.
|
|
255
|
-
|
|
256
|
-
---
|
|
257
|
-
## [1.6.0] - 2026-06-02
|
|
258
|
-
|
|
259
|
-
### Changed
|
|
260
|
-
|
|
261
|
-
- **`getEnvironment()` is now the single source of truth for environment detection** (and `testing` is a first-class environment). [src/utils/mode-helpers.js](src/utils/mode-helpers.js) — `getEnvironment()` is the ONLY function that reads the raw signals (`UJ_TEST_MODE` / `window.Configuration.uj.environment` / `UJ_BUILD_MODE` / `UJ_IS_SERVER` / `NODE_ENV`) and resolves them to exactly one of `'development' | 'testing' | 'production'` (mutually exclusive; **testing wins**). `isDevelopment()` / `isProduction()` / `isTesting()` now **derive** from it, so they can never disagree and exactly one is always true. `isProduction()` is a real positive check, not `!isDevelopment()`. [src/build.js](src/build.js) drops its own `getEnvironment()` (now mixed in via `attachTo`). Doc renamed `cross-context-helpers.md` → [docs/environment-detection.md](docs/environment-detection.md).
|
|
262
|
-
- **Redirect delay now keys off non-production (dev OR testing), not dev-only.** [src/assets/js/modules/redirect.js](src/assets/js/modules/redirect.js) delays the redirect whenever `environment !== 'production'` so it's observable in tests too.
|
|
263
|
-
- **Footer language dropdown gates the extra-languages list on `translation.enabled`** (+ vertical-alignment fix). [footer.html](src/defaults/dist/_includes/themes/classy/frontend/sections/footer.html)
|
|
264
|
-
|
|
265
|
-
### Added
|
|
266
|
-
|
|
267
|
-
- **`test/_init.js` pre-test lifecycle hook (setup-only).** [src/test/runner.js](src/test/runner.js) loads an optional `test/_init.js` from BOTH the framework and consumer test roots and runs its `setup()` once before any suite (the `_`-prefix keeps it out of discovery). Mirrors BEM/EM/BXM. Default scaffold at [src/defaults/test/_init.js](src/defaults/test/_init.js). [docs/test-framework.md](docs/test-framework.md) adds a "NEVER mock — test against the real harness" section + `_init.js` reference, and tests now assert the env invariant across every signal scenario.
|
|
268
|
-
- **`translation.exclude` config** — folder or page paths excluded from AI translation (added to both the files and folders match sets). Default [_config.yml](src/defaults/src/_config.yml) excludes `blog`. [src/gulp/tasks/translation.js](src/gulp/tasks/translation.js)
|
|
269
|
-
- **`setup` prunes legacy first-name-only default team members** (e.g. `team/alex`) and their image dirs via `removeLegacyTeamMembers()`. [src/commands/setup.js](src/commands/setup.js)
|
|
270
|
-
- **`npx mgr install live`** accepted as an alias for `prod`/`production`. [src/commands/install.js](src/commands/install.js); docs use `install dev` / `install live` consistently.
|
|
271
|
-
|
|
272
|
-
### Fixed
|
|
273
|
-
|
|
274
|
-
- **Download buttons use a `[data-download]` hook** instead of the brittle `.btn-primary:not([type="submit"])` selector. [download.html](src/defaults/dist/_layouts/themes/classy/frontend/pages/download.html) + [download/index.js](src/assets/js/pages/download/index.js)
|
|
275
|
-
- **Feedback review modal:** $100 gift-card incentive + an explicit "you must actually post your review" warning, and copy normalized "Write a review" → "Post your review". [feedback.html](src/defaults/dist/_layouts/themes/classy/frontend/pages/feedback.html) + [feedback/index.js](src/assets/js/pages/feedback/index.js)
|
|
276
|
-
- Removed a leftover `package copy.json` from the repo root.
|
|
277
|
-
|
|
278
|
-
---
|
|
279
|
-
## [1.5.0] - 2026-06-02
|
|
280
|
-
|
|
281
|
-
### Added
|
|
282
|
-
|
|
283
|
-
- **Neobrutalism theme** — a complete second shipped theme (alongside `classy`), not a recolor: hard ink borders, offset "press" shadows, chunky display type, flat color-blocks, square controls. Custom homepage + pricing layouts. It restyles **standard Bootstrap classes** (`.btn`, `.card`, `.text-bg-*`) and a **universal semantic layout vocabulary** (`.section-hero`, `.showcase-row`, `.stat-block`, `.pricing-plan`, `.cta-panel`, …) — **no theme-prefixed classes** — so the same markup is swappable across themes (change `theme.id`, done). [src/assets/themes/neobrutalism/](src/assets/themes/neobrutalism/)
|
|
284
|
-
- **Theme system: three-layer page-asset cascade (Global → Theme → Consumer) for BOTH CSS and JS.**
|
|
285
|
-
- Theme page CSS: `themes/<id>/pages/<path>/index.scss` compiles to a theme-suffixed bundle (`index.<themeId>.bundle.css`) via [src/gulp/tasks/sass.js](src/gulp/tasks/sass.js), linked by [head.html](src/defaults/dist/_includes/core/head.html) with `{% iffile %}`. Missing = nothing loads (component styles handle it), no fallback needed.
|
|
286
|
-
- Theme page JS: `__theme__/pages/<path>/index.js` loaded as the `#theme` layer in [src/index.js](src/index.js), executed `main → theme → project` (a `webpackInclude: /\.js$/` guard stops `.scss` from being pulled into the JS import context).
|
|
287
|
-
- Per-page HTML layout overrides under `_layouts/themes/<id>/` reuse classy's `page.resolved.*` frontmatter data contract and fall back to classy when a layout is absent.
|
|
288
|
-
- **Theme-authoring template** at [src/assets/themes/_template/](src/assets/themes/_template/) + a full **[docs/themes.md](docs/themes.md)** reference for building a theme inside UJM or in a consumer project (selection/resolution, classy fallback, page CSS/JS layers, the no-prefix vocabulary, adaptive-button + interactive-accent gotchas).
|
|
289
|
-
- **Single interactive-accent token** (`--nb-accent-interactive` = `var(--bs-primary)`) drives all hover/active states (blue), distinct from the yellow signature accent reserved for static blocks. Adaptive buttons (`btn-adaptive`/`-inverse`) get explicit theme overrides so they render solid and press like every other button.
|
|
290
|
-
- **Asset-layer test harness** — `UJ_TEST_LAYERS` flag + [manage-test-layers.js](src/gulp/tasks/utils/manage-test-layers.js) generate real consumer-side test files; the `/test/libraries/layers` page renders the Global/Theme/Consumer cascade as red/green dots to prove all three layers load in order. `npm run start:test-layers` enables it.
|
|
291
|
-
|
|
292
|
-
---
|
|
293
|
-
## [1.4.3] - 2026-05-28
|
|
294
|
-
|
|
295
|
-
### Fixed
|
|
296
|
-
|
|
297
|
-
- **Imagemin: uppercase-extension images (e.g. `IMG_3119.JPG`) now build end to end.** v1.4.2 made the glob case-insensitive so the file was discovered, but `gulp-responsive-modern`'s `lib/format.js` does a case-sensitive `switch` on `path.extname()` and returns the string `'unsupported'` for `.JPG`, which then crashes `sharp.toFormat()`. [src/gulp/tasks/imagemin.js](src/gulp/tasks/imagemin.js) now pipes each file through an in-stream `Transform` that lowercases the extension on the Vinyl path before the responsive plugin sees it (the on-disk source is left untouched).
|
|
298
|
-
- **Log files no longer truncate before the crash that caused them.** [src/utils/attach-log-file.js](src/utils/attach-log-file.js) switched from `fs.createWriteStream` (async-buffered) to synchronous `fs.writeSync` against an open fd. The buffered stream dropped its tail when a gulp task threw and the process exited — so the lines describing the failure never reached `logs/build.log`. Synchronous writes guarantee the full error + stack survive an immediate exit.
|
|
299
|
-
|
|
300
|
-
### Changed
|
|
301
|
-
|
|
302
|
-
- **Auth: signup-consent gating now keys off the user doc's `flags.signupProcessed` instead of a time window.** [src/assets/js/core/auth.js](src/assets/js/core/auth.js) drops the `SIGNUP_MAX_AGE` (5-minute) heuristic and the client-only `localStorage` flag. `sendUserSignupMetadata` fires whenever the doc shows signup unprocessed (the server is idempotent), and the consent guard only signs a user out once signup has actually been processed — removing the risk of locking users out on a transient metadata-send failure.
|
|
303
|
-
- **Footer language dropdown always renders.** No longer gated on `site.translation.enabled`; falls back to `site.translation.default` (or `"en"`) when no extra languages are configured. [src/defaults/dist/_includes/themes/classy/frontend/sections/footer.html](src/defaults/dist/_includes/themes/classy/frontend/sections/footer.html)
|
|
304
|
-
- **Sentence-case copy normalization** across default pages (pricing, alternatives, admin/test pages, sitemap section labels): "API access", "Flash sale", "Root pages", "…and more:" etc.
|
|
305
|
-
- **Updates feed:** the `v0.0.1` sample entry is marked `draft: true` so it's hidden from the listing and sitemap (dev-only).
|
|
306
|
-
|
|
307
|
-
---
|
|
308
|
-
## [1.4.2] - 2026-05-27
|
|
309
|
-
|
|
310
|
-
### Fixed
|
|
311
|
-
|
|
312
|
-
- **Imagemin: uppercase image extensions (e.g. `IMG_3119.JPG`) no longer break the responsive build.** `gulp-responsive-modern` uses micromatch internally, which is strictly case-sensitive regardless of filesystem. On macOS APFS, gulp's `src()` would discover the file and count it toward expected outputs, but the lowercase-only `**/*.{jpg,jpeg,png}` pattern wouldn't match — producing zero outputs and erroring with "Available images do not match the following config". [src/gulp/tasks/imagemin.js](src/gulp/tasks/imagemin.js) now expands `ALL_IMAGE_GLOB` and `RESPONSIVE_GLOB` to include uppercase variants so consumers don't need to rename camera/phone files.
|
|
313
|
-
|
|
314
|
-
---
|
|
315
|
-
## [1.4.1] - 2026-05-27
|
|
316
|
-
|
|
317
|
-
### Changed
|
|
318
|
-
|
|
319
|
-
- **Bumped `puppeteer` `^24.43.1` → `^25.1.0`.** Puppeteer 25 is ESM-only and requires Node 22+; UJM's existing `require('puppeteer')` calls in [src/test/runners/boot.js](src/test/runners/boot.js) and [src/test/runners/chromium.js](src/test/runners/chromium.js) keep working via Node 22's native ESM-require interop.
|
|
320
|
-
- **Bumped `html-validate` `^10.17.0` → `^11.4.0`.** Used by [src/gulp/tasks/audit.js](src/gulp/tasks/audit.js); audit suite still passes.
|
|
321
|
-
|
|
322
|
-
---
|
|
323
|
-
## [1.4.0] - 2026-05-27
|
|
324
|
-
|
|
325
|
-
### Added
|
|
326
|
-
|
|
327
|
-
- **`UJ_IMAGEMIN_REWRITE_SOURCES=true` flag** (opt-in, off by default) — when set, the `imagemin` gulp task scans every image scheduled for processing and rewrites in place any whose longest dimension exceeds 4096px. Uses `sharp` with `fit: 'inside'` (aspect-preserving), JPEG quality 80 / mozjpeg / progressive, PNG quality 80. Cache hashes for affected files are updated so the new content becomes the new cache key. Intended as a one-off cleanup for repos with pre-existing oversized source images that silently stall `gulp-responsive-modern`/`sharp`. See [docs/images.md](docs/images.md#cleanup-for-existing-oversized-sources-uj_imagemin_rewrite_sources).
|
|
328
|
-
- **Log-file tee** (`src/utils/attach-log-file.js`): every line of stdout/stderr produced by the gulp pipeline is duplicated to `logs/dev.log` (during `npm start`) or `logs/build.log` (during `npm run build`) in the consumer project root. ANSI color codes are stripped from the file output; terminal output is unchanged. Files truncate fresh on each run. Skipped when `UJ_IS_SERVER=true` (CI/cloud) — no `logs/` directory is created in workspace contexts. Attached at the top of `src/gulp/main.js`. See [docs/local-development.md](docs/local-development.md#log-files).
|
|
329
|
-
- **Pricing page: 7-day money-back guarantee badge** under the billing toggle and per-card on each paid plan; free plan shows "Upgrade any time" with a rocket icon.
|
|
330
|
-
- **Pricing page: trial-aware CTA copy** — buttons now read "Get free trial" only when the plan's `trial.days > 0`, otherwise "Get started". Free plan stays "Get started". Avoids misleading users into thinking they need a trial for a free plan or that every paid plan offers one.
|
|
331
|
-
- **Auth: field-level error rendering.** New `isPasswordError()` and `passwordErrorMessage()` helpers in `src/assets/js/libs/auth.js` route Firebase password errors (`auth/weak-password`, `auth/missing-password`, `auth/wrong-password`, `auth/password-does-not-meet-requirements`) onto the password input via `FormManager.throwFieldErrors()` instead of the form-level banner. Signin: `auth/invalid-credential` | `auth/wrong-password` | `auth/user-not-found` highlight both email + password with "Incorrect email or password" (Firebase intentionally collapses these to prevent email enumeration). `auth/invalid-email` highlights the email field. Signup: when the email already exists and auto-signin fails, the message lands inline on the email field rather than throwing a generic banner error.
|
|
332
|
-
- **Dev-only consent-guard warning** in `src/assets/js/core/auth.js` `sendUserSignupMetadata` catch block: shows a `webManager.utilities().showNotification()` with the exact wall-clock time the consent guard will sign the user out (and remaining mm:ss) when the metadata POST fails. Wrapped in `/* @dev-only:start */` blocks so production builds strip it.
|
|
333
|
-
|
|
334
|
-
### Changed
|
|
335
|
-
|
|
336
|
-
- **Sentence-case copy normalization** across ~60 layouts, pages, and section configs. Examples: "Sign Out" → "Sign out", "API Keys" → "API keys", "Save Changes" → "Save changes", "Contact Us" → "Contact us", "Main Menu" → "Main menu", "All Items" → "All items", "Admin Panel" → "Admin panel", "Sign Up" → "Sign up". Touches frontend pages (account, auth, contact, download, extension, index, payment, pricing, team, etc.), blueprint admin/legal/portal pages, test pages, and nav/footer/sidebar/account JSON section configs.
|
|
337
|
-
- **FormManager: `.has-validation` on input-group when a field is marked invalid.** Bootstrap requires this class on the wrapping `.input-group` so the trailing element (e.g. a password-visibility toggle) keeps its border-radius once a sibling `.invalid-feedback` is appended.
|
|
338
|
-
- **Classy theme forms SCSS:** restored rounded right corners on `.input-group.has-validation > *:nth-last-child(2)` so the visually-last interactive element keeps its `$classy-radius-lg`. Also added `.form-control.is-invalid:focus` override to keep the danger (red) focus ring instead of the brand-blue ring the classy theme was applying.
|
|
339
|
-
|
|
340
|
-
### Fixed
|
|
341
|
-
|
|
342
|
-
- **`imagemin` gulp task race condition (build mode):** the task was declared `async function imagemin(complete)` and returned a stream directly. Async functions wrap their return value in a Promise, so gulp resolved the task on the (already-resolved) Promise instead of waiting for the stream's `'finish'` event. Downstream tasks (jekyll, audit, minifyHtml) then started while imagemin was still writing to `dist/`, and jekyll would snapshot `_site/` before late images landed. Builds reported success while silently shipping a partial site. Fixed by explicitly `await`ing stream completion via `new Promise((resolve, reject) => { ... .on('finish', resolve).on('error', reject) })` before moving to the cache-push step, then `return complete()`. The wait only ever runs in build mode (dev mode short-circuits earlier in the task), so `npm start` startup is unaffected.
|
|
343
|
-
- **Oversized source images silently failing to land in `_site/`.** Source images with very large dimensions (10000px+ longest side) decode into hundreds of MB per worker in `sharp`, which can stall the `gulp-responsive-modern` stream so quietly that gulp reports the task complete. The build appears successful but some images never reach `_site/`. Documented the constraint and the new `UJ_IMAGEMIN_REWRITE_SOURCES` cleanup flag in [docs/images.md](docs/images.md). Recommended fix is to cap images at the upload step; the rewrite flag is a fallback for cleaning up existing repos.
|
|
344
|
-
|
|
345
|
-
---
|
|
346
|
-
## [1.3.12] - 2026-05-25
|
|
347
|
-
|
|
348
|
-
### Fixed
|
|
349
|
-
|
|
350
|
-
- **Fresh-consumer `npx mgr setup` now works on the first run.** Two ordering bugs prevented setup from succeeding on a brand-new consumer project: (1) `ensureBundle()` ran `bundle install` before `ensureCoreFiles()` scaffolded the `Gemfile`, failing with "Could not locate Gemfile" — reordered so core files are scaffolded first. (2) The gulpfile eagerly `require()`s every task module at load time, and `sass.js`/`distribute.js`/`imagemin.js` all call `Manager.getUJMConfig()` at module top-level, so a fresh consumer (with no `config/ultimate-jekyll-manager.json`) couldn't even invoke `gulp defaults` — extended `ensureCoreFiles()` to seed both `src/_config.yml` and `config/ultimate-jekyll-manager.json` from `dist/defaults/` before invoking gulp. Both steps remain idempotent (existing-file guard + `{ overwrite: false }` copy).
|
|
351
|
-
|
|
352
|
-
---
|
|
353
|
-
## [1.3.11] - 2026-05-24
|
|
354
|
-
|
|
355
|
-
### Changed
|
|
356
|
-
|
|
357
|
-
- **Account page UI polish.** Three small consistency fixes on `/account`: (1) Notifications "Save preferences" button now has the `floppy-disk` icon to match the profile section's "Save Changes" button. (2) Generate signin link modal no longer shows a redundant Cancel button in its footer — the X in the modal header already dismisses it. (3) Connections "Manage connections" card no longer renders a divider between the section title and the first item (dropped `list-group list-group-flush` from `#connections-list`); the per-item `border-top` already handles inter-item separation, matching the Sign-in methods card's pattern.
|
|
358
|
-
|
|
359
|
-
---
|
|
360
|
-
## [1.3.10] - 2026-05-24
|
|
361
|
-
|
|
362
|
-
### Added
|
|
363
|
-
|
|
364
|
-
- **Generate signin link** — advanced-user feature on `/account#security` that creates a temporary `/signin?authCustomToken=<token>` URL via `POST /backend-manager/user/token`. Centered text-link trigger sits under the Active sessions card (same Bootstrap utility classes as Cancel subscription: `btn btn-link btn-sm text-muted text-decoration-underline opacity-50`). Opens a danger-bordered modal with a typed-phrase gate (`I will not share this link`) that enables the red Generate button. On success, the warning view swaps in-place for the link inside a readonly monospaced input + Copy button, with a prominent 1-hour expiry warning. Token never persists outside the input — `show.bs.modal` resets state every open. Backend route already existed (`functions/routes/user/token/post.js`) and the existing `handleCustomTokenSignin` flow in `src/assets/js/libs/auth.js` consumes the resulting URL.
|
|
365
|
-
|
|
366
|
-
### Changed
|
|
367
|
-
|
|
368
|
-
- **Account-page marketing toggle now requires an explicit "Save preferences" button.** v1.3.9 made the toggle auto-submit on change. Cleaner UX: user flips the toggle, sees the new state visually, then clicks Save when they actually want to commit. Markup adds a `<button type="submit" class="btn btn-primary">Save preferences</button>` to the `#marketing-emails-form` (classy account template). JS drops the `addEventListener('change', () => formManager.submit())` line. On failure, the toggle stays where the user left it (no auto-revert) — they see the error message via FormManager and can hit Save again.
|
|
369
|
-
|
|
370
|
-
### Removed
|
|
371
|
-
|
|
372
|
-
- **`.cancel-trigger-link` SCSS class.** Replaced with Bootstrap utilities on both the existing Cancel subscription button and the new Generate signin link button (`text-muted opacity-50` instead of the custom `opacity: 0.7` + hover transition + `0.8125rem` font size). Avoids inventing project-specific classes when Bootstrap utilities cover the same styling.
|
|
373
|
-
|
|
374
|
-
---
|
|
375
|
-
## [1.3.9] - 2026-05-24
|
|
376
|
-
|
|
377
|
-
### Fixed
|
|
378
|
-
|
|
379
|
-
- **Account-page marketing toggle showed "Failed to update email preferences" even on successful unsubscribe.** Root cause: `pages/account/sections/notifications.js` was checking `response.data?.success !== true`, but `authorizedFetch` returns the JSON body directly (no `data` wrapper), and BEM's `assistant.respond({ success: true })` writes the object at the response root via `res.json(response)`. So `response.success === true` and `response.data?.success === undefined` — the check fired even though the backend had successfully written `consent.marketing.status = 'revoked'` AND removed the contact from SendGrid + Beehiiv. Frontend UX showed a danger toast and reverted the toggle, but server state was already correct, putting the UI out of sync with reality. Fix: check `response.success` at the root.
|
|
380
|
-
|
|
381
|
-
### Changed
|
|
382
|
-
|
|
383
|
-
- **`pages/account/sections/notifications.js` refactored to use FormManager**, matching the project rule that all user-driven API forms use form-manager for in-flight/success/error UX. The toggle is now wrapped in `<form id="marketing-emails-form">` (classy account template, `src/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html`), and the change event triggers `formManager.submit()`. Success notification uses `formManager.showSuccess()`; failure throws so FormManager surfaces the error toast and the JS reverts the toggle to its last-known-good state. Replaces the ad-hoc `addEventListener('change')` + raw `authorizedFetch` + manual `webManager.utilities().showNotification()` pattern.
|
|
384
|
-
|
|
385
|
-
---
|
|
386
|
-
## [1.3.8] - 2026-05-24
|
|
387
|
-
|
|
388
|
-
### Fixed
|
|
389
|
-
|
|
390
|
-
- **Reverse-signup now keeps the user on `/signin` so they actually see the inline error.** v1.3.7 fixed `isNewUser` detection, but a follow-on race appeared: when Firebase's `getRedirectResult()` returns a fresh-signup user, the auth-state-change listener in `core/auth.js` fires `state.user = <about-to-be-deleted>` BEFORE `reverseAccidentalSignup`'s `await newUser.delete() → signOut()` chain completes. The listener's `policy === 'unauthenticated'` branch then redirects to `/account` (or `authReturnUrl`), and by the time the inline `showError()` call fires, the user is already off the page. Fixed with a `window.__UJM_REVERSING_SIGNUP` flag set synchronously before the delete + cleared after signOut's followup state-change. The listener checks the flag at the top and short-circuits the entire callback — no redirect, no metadata POST, no consent guard, nothing — until the reversal completes and the user lands on `user = null` with the inline error visible on `/signin`.
|
|
391
|
-
|
|
392
|
-
---
|
|
393
|
-
## [1.3.7] - 2026-05-24
|
|
394
|
-
|
|
395
|
-
### Fixed
|
|
396
|
-
|
|
397
|
-
- **Reverse-signup detection on `/signin` was completely broken.** The reverse-signup-on-/signin flow in `src/assets/js/libs/auth.js` (lines 289 and 664) was reading `result.additionalUserInfo?.isNewUser` directly off the `UserCredential` returned by `getRedirectResult()` and `signInWithPopup()`. That property does NOT exist on the v9+ modular SDK — verified against `@firebase/auth`'s `auth-public.d.ts`, which declares `UserCredential` as exactly `{ user, providerId, operationType }`. The legacy compat SDK exposed `additionalUserInfo` as a direct property, hence the v9 migration footgun. On the modular SDK you must call the standalone helper `getAdditionalUserInfo(userCredential): AdditionalUserInfo | null` to access `isNewUser`. Result: `isNewUser` was always `undefined` → always falsy → the `if (isNewUser && !isSignupPage)` reverse gate at line 296 never fired in production. New Google accounts on `/signin` got signed in straight to `/account` with no consent on record, despite the reverse-signup code existing in the bundle since multiple versions ago. Confirmed live on Somiibo (Test 4 of `TODO-CONSENT-LIVETEST.md` failed 3× in a row with `operationType: 'signIn'` and no `[Auth] Reversing accidental signup` log line). Now both sites import `getAdditionalUserInfo` and call it on the result. Added two `console.warn` diagnostic logs (one per site) so future regressions surface immediately — can be removed once we're confident the fix sticks.
|
|
398
|
-
|
|
399
|
-
---
|
|
400
|
-
## [1.3.6] - 2026-05-24
|
|
401
|
-
|
|
402
|
-
### Fixed
|
|
403
|
-
|
|
404
|
-
- **`auth/error-code:-47` now shows a friendly message instead of the raw FirebaseError.** v1.3.5's diagnostic confirmed: on the OAuth redirect path (`signInWithIdp` → 503), Firebase strips the BEM-side `HttpsError` message and delivers `code: 'auth/error-code:-47'` with `customData: {}` — empty. There's nothing to extract because Firebase ate the message client-side. This contradicts Firebase's own [Identity Platform docs](https://cloud.google.com/identity-platform/docs/blocking-functions) which describe a `BLOCKING_FUNCTION_ERROR_RESPONSE` wrapper that SHOULD carry the original message. The wrapper works on the 400 path (email signup, OAuth popup) — our v1.3.4 extractor handles that fine. The 503 path is broken: tracked at [firebase-js-sdk#8054](https://github.com/firebase/firebase-js-sdk/issues/8054), where a Firebase engineer said "503 seems to be the working as design error codes" then the issue was auto-closed as stale 5 weeks later without a fix or workaround. The `-47` code is 1:1 with "blocking-function rejected this signup," so `extractBlockingFunctionMessage()` now returns a generic-but-helpful message covering all three BEM `beforeCreate` reasons (rate limit, disposable email, custom hook reject): "Account creation is temporarily restricted. This can happen if you've recently created too many accounts, or your email is on our blocked list. Please try again later or contact support." The original `customData.serverResponse` path stays as the primary handler — the `-47` catchall is an additive fallback for when Firebase eats the message.
|
|
405
|
-
|
|
406
|
-
---
|
|
407
|
-
## [1.3.5] - 2026-05-24
|
|
408
|
-
|
|
409
|
-
### Added
|
|
410
|
-
|
|
411
|
-
- **Diagnostic logging in `extractBlockingFunctionMessage()` (`src/assets/js/libs/auth.js`).** When BEM's `beforeCreate` rate limit (2 signups/day/IP) fires via Google OAuth redirect, the user just saw "Firebase: Error (auth/error-code:-47)" instead of the helpful "Unable to create account at this time. Please try again later." message. The 1.3.4 extraction handles the standard 400-with-`BLOCKING_FUNCTION_ERROR_RESPONSE` path, but the 503 path (Google's Identity Toolkit returns 503 directly with code -47, no `customData.serverResponse`) flows through to the generic `auth.code` branch. Added a `console.warn` that dumps the full error shape (code, message, customData, serverResponse) so the next failed signup attempt reveals exactly what Firebase delivers — then we can write a matching handler. Diagnostic ships first; fix follows in a subsequent version.
|
|
412
|
-
|
|
413
|
-
---
|
|
414
|
-
## [1.3.4] - 2026-05-22
|
|
415
|
-
|
|
416
|
-
### Added
|
|
417
|
-
|
|
418
|
-
- **Surface BEM blocking-function error messages to users.** Firebase Auth blocking functions (`before-create`, `before-signin`) that throw `HttpsError('resource-exhausted', 'Too many signups...')` get wrapped by Firebase as the opaque `auth/internal-error` (sometimes `auth/error-code:-47`). The actual BEM-side message is buried in `error.customData.serverResponse` inside a `BLOCKING_FUNCTION_ERROR_RESPONSE : ((error : (message : "...")))` wrapper. New `extractBlockingFunctionMessage(error)` helper in `src/assets/js/libs/auth.js` unwraps it. Wired into all 4 auth error sites (OAuth popup, OAuth redirect, email signup, email signin) so users now see "Too many signups from your IP, please try again later" instead of "Firebase: Error (auth/error-code:-47)."
|
|
419
|
-
|
|
420
|
-
---
|
|
421
|
-
## [1.3.3] - 2026-05-21
|
|
422
|
-
|
|
423
|
-
### Changed
|
|
424
|
-
|
|
425
|
-
- **Reordered account page sections** in `src/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html`. Sidebar nav and section blocks now run Profile → Security → Subscription → Billing → Referrals → Team → Notifications → API Keys, pushing the less-frequently-used Team and Notifications sections below Referrals. Section content unchanged.
|
|
426
|
-
|
|
427
|
-
---
|
|
428
|
-
## [1.3.2] - 2026-05-22
|
|
429
|
-
|
|
430
|
-
### Fixed
|
|
431
|
-
|
|
432
|
-
- **Consent guard was running before `sendUserSignupMetadata`, killing every fresh signup.** `src/assets/js/core/auth.js`: on a brand-new signup the user doc exists with `consent.legal.status: 'revoked'` (the schema default written by BEM's `on-create` event); `sendUserSignupMetadata` is what flips it to `'granted'`. Previously the guard ran first and signed the user out before the metadata POST could fire — orphaned every new account. Reordered to run `sendUserSignupMetadata` first, and the guard now skips accounts younger than `SIGNUP_MAX_AGE` (5min) so a transient network error during the POST doesn't lock a user out forever — they can retry. After the grace window, the guard fires normally.
|
|
433
|
-
|
|
434
|
-
---
|
|
435
|
-
## [1.3.1] - 2026-05-21
|
|
436
|
-
|
|
437
|
-
### Changed
|
|
438
|
-
|
|
439
|
-
- **`ENFORCE_CONSENT_GUARD` flipped to `true`** in `src/assets/js/core/auth.js`. The page-load consent guard now silently signs out any authenticated user whose doc has `consent.legal.status !== 'granted'`. Caveat: any pre-consent-system user doc (missing the field, or defaulted to `'revoked'`) will be signed out on page load — run the legacy-user migration first, or live-test against fresh signups.
|
|
440
|
-
|
|
441
|
-
---
|
|
442
|
-
## [1.3.0] - 2026-05-21
|
|
443
|
-
|
|
444
|
-
### Added
|
|
445
|
-
|
|
446
|
-
- **Marketing consent capture on the signup form.** Frontend half of `backend-manager` v5.2.0's consent system. `src/defaults/dist/_layouts/themes/classy/frontend/pages/auth/signup.html` replaces the legal-copy line with two real checkboxes (`consent-legal`, `consent-marketing`) wrapped in a `#consent-group` so validation can highlight the pair as a unit. `consent-legal` is required to submit.
|
|
447
|
-
- **`captureSignupConsent()` + `validateConsent()` in `src/assets/js/libs/auth.js`.** Pulls checkbox state + label text from the FormManager-collected data and writes it to `webManager.storage()` under key `consent` BEFORE Firebase auth fires — survives the post-signup redirect the same way `attribution` does. `validateConsent()` blocks submit via a phantom `__consent` field name and surfaces feedback via the wrapper outline + an inline error message instead of red-X-ing the single legal checkbox.
|
|
448
|
-
- **`reverseAccidentalSignup()` for the Google quirk.** Landing on `/signin` with an unknown Google account auto-creates the Firebase auth user; this reverses that path — deletes the user, signs out, strips `authReturnUrl`, and surfaces an inline form error. Best-effort delete; the page-load consent guard (below, currently OFF) is the backstop if delete fails.
|
|
449
|
-
- **`ENFORCE_CONSENT_GUARD` in `src/assets/js/core/auth.js`.** Page-load guard that silently signs out any authenticated user whose doc has `consent.legal.status !== 'granted'`. Default **FALSE** until the legacy-user migration runs (which sets all existing docs to `granted` + `source: 'imported'`); flipping it on before then would lock every existing user out.
|
|
450
|
-
- **`consent` field on the `sendUserSignupMetadata` payload.** Forwards the storage-survived consent blob to BEM's `/user/signup` route so it can write the canonical `consent.{legal,marketing}` sub-tree on the new user doc.
|
|
451
|
-
- **Marketing-emails toggle on the account page.** `src/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html` + `src/assets/js/pages/account/sections/notifications.js` reworked to read `account.consent.marketing.status` (not the old `preferences.notifications.marketing`) and POST to `/backend-manager/marketing/email-preferences` on change. Shows the original grant date below the toggle.
|
|
452
|
-
|
|
453
|
-
### Changed
|
|
454
|
-
|
|
455
|
-
- **`web-manager` bumped to `^4.2.0`** (was `file:../web-manager` from local dev). Locks in `DEFAULT_ACCOUNT.consent.{legal,marketing}` so `resolveAccount()` always returns a defined consent shape for legacy users.
|
|
456
|
-
- Minor template touchups on `oauth2.html`, `reset.html`, `signin.html`, `token.html`, `signup.html` — heading casing, `filter-adaptive` class on the brandmark logo so it inverts in dark mode.
|
|
457
|
-
|
|
458
|
-
### Fixed
|
|
459
|
-
|
|
460
|
-
- **`_team` seed authors** — corrected LinkedIn/Twitter handles in `christina-hill.md`, `james-oconnor.md`, `marcus-johnson.md`, `priya-sharma.md`, `sarah-rodriguez.md` so the default scaffolded `/team/` page links don't 404.
|
|
461
|
-
|
|
462
|
-
---
|
|
463
|
-
## [1.2.3] - 2026-05-19
|
|
464
|
-
|
|
465
|
-
### Added
|
|
466
|
-
|
|
467
|
-
- **Markdown table styles in blog posts** — `article .blog-post-content table` in `src/assets/css/pages/blog/post.scss` now renders any plain `| col | col |` markdown table with a rounded outer border, uppercase letter-spaced thead on a tinted background, zebra-striped tbody rows, hover highlight, and tighter padding/font-size on mobile (≤575.98px).
|
|
468
|
-
|
|
469
|
-
### Changed
|
|
470
|
-
|
|
471
|
-
- **Tidied heading-margin rule** in `src/assets/css/pages/blog/post.scss` — collapsed `h1..h6` onto one selector and removed the commented-out `:first-child` reset.
|
|
472
|
-
|
|
473
|
-
---
|
|
474
|
-
## [1.2.2] - 2026-05-18
|
|
475
|
-
|
|
476
|
-
### Added
|
|
477
|
-
|
|
478
|
-
- **`docs/<topic>.md` deep references** — 17 new files referenced by the v1.2.0 CLAUDE.md reorg that hadn't been committed yet: `ads.md`, `analytics.md`, `appearance.md`, `assets.md`, `audit.md`, `css.md`, `icons.md`, `images.md`, `javascript-libraries.md`, `jekyll-plugin.md`, `layouts-and-pages.md`, `lazy-loading.md`, `local-development.md`, `page-loading.md`, `directory-structure.md`, `seo.md`, `xss-prevention.md`. Restores parity with the cross-links already shipped in [CLAUDE.md](CLAUDE.md).
|
|
479
|
-
- **`src/defaults/docs/README.md`, `src/defaults/test/README.md`, `src/defaults/CHANGELOG.md`** — consumer-project scaffolding files distributed via the `defaults` gulp task.
|
|
480
|
-
|
|
481
|
-
---
|
|
482
|
-
## [1.2.1] - 2026-05-18
|
|
483
|
-
|
|
484
|
-
### Changed
|
|
485
|
-
|
|
486
|
-
- **Default `/extension` page** — removed the redundant downloads-section heading block ("Install" badge + "Available on every browser" headline + "Choose your browser below to get started" subheadline) that duplicated the role of the page hero. Browser-selector pills now sit directly under the hero. Affects `src/defaults/dist/_layouts/themes/classy/frontend/pages/extension/index.html` and drops the now-unused `downloads.superheadline` / `downloads.headline` / `downloads.headline_accent` / `downloads.subheadline` frontmatter keys.
|
|
487
|
-
|
|
488
|
-
---
|
|
489
|
-
## [1.2.0] - 2026-05-12
|
|
490
|
-
|
|
491
|
-
### Added
|
|
492
|
-
|
|
493
|
-
- **Three-layer test framework** (`build` / `page` / `boot`, 60 framework tests passing in ~3s). New under `src/test/`: `assert.js` (Jest-compatible matcher set), `runner.js` (discovery + dispatch + reporter), `index.js` (public API), `runners/{chromium,boot}.js` (Puppeteer launchers with a zero-dep embedded HTTP server in `server.js` for serving `_site/` to Chromium — required because service workers can't register from `file://`), `harness/page/index.html` (stub harness page), `fixtures/consumer-site/` (minimal hand-built `_site/` for framework boot tests). Consumer-test discovery uses the `isFrameworkSelfTest` package-name check to scope framework `boot/` suites to UJM's own runs.
|
|
494
|
-
- **`test` CLI command + `--test` alias** (`src/commands/test.js`) — avoids `-t` collision with the existing `translation` command. Sets `UJ_TEST_MODE=true` + auto-routes `UJ_TEST_BOOT_PROJECT` to the fixture when UJM tests itself. `"test": "node ./bin/ultimate-jekyll test"` added to scripts, `"test": "npx mgr test"` added to projectScripts.
|
|
495
|
-
- **`src/utils/mode-helpers.js`** — `attachTo(Manager)` mixin exposing `isTesting`/`isDevelopment`/`isProduction`/`getVersion`. Wired into `src/build.js` (CJS, build-time Manager) and `src/index.js` (ESM, frontend Manager). Driven by `UJ_TEST_MODE` env in Node + `globalThis.UJ_TEST_MODE` in browser contexts.
|
|
496
|
-
- **`puppeteer` devDep** — peer-optional for consumers (only needed if they write `page`/`boot` tests; `build` layer needs nothing extra).
|
|
497
|
-
- **New `docs/<topic>.md` deep references** — `docs/test-framework.md`, `docs/test-boot-layer.md`, `docs/cross-context-helpers.md`. Plus `docs/_legacy-claude-md.md` as a holding pen for the previous 1832-line CLAUDE.md content awaiting future per-subsystem split.
|
|
498
|
-
- **Consumer-shipped `src/defaults/CLAUDE.md`** with `# ========== Default Values ==========` / `# ========== Custom Values ==========` markers. Framework section stays live-synced across `npx mgr setup` while the Custom section is preserved verbatim — same merge protocol as `.env`/`.gitignore`.
|
|
499
|
-
- **`'CLAUDE.md'` FILE_MAP rule** (`src/gulp/tasks/defaults.js`) with `mergeLines: true` — positioned after the `'**/*.md'` catch-all so the last-match-wins logic in `getFileOptions` activates the merge path.
|
|
500
|
-
|
|
501
|
-
### Changed
|
|
502
|
-
|
|
503
|
-
- **CLAUDE.md reorganized from 1832 to 195 lines** as a TOC hub with one-paragraph-per-subsystem + cross-links. Legacy content stashed in `docs/_legacy-claude-md.md` as a migration source.
|
|
504
|
-
- **README.md updated** with a Testing section (build/page/boot layer overview + example test files) and a Sister Projects callout.
|
|
505
|
-
|
|
506
|
-
### Fixed
|
|
507
|
-
|
|
508
|
-
- **`mergeLineBasedFiles` idempotency bug** — the inline merge function unconditionally inserted a blank line before `CUSTOM_SECTION_MARKER`, causing first-merge after a fresh `jetpack.copy` to grow the file by one newline. Now skips the insert if `mergedDefaultSection` already ends blank. Affects `.env`/`.gitignore`/`CLAUDE.md` equally — first-merge is now a true no-op.
|
|
509
|
-
|
|
510
|
-
---
|
|
511
|
-
## [1.1.10] - 2026-05-10
|
|
512
|
-
### Removed
|
|
513
|
-
- `through2` dependency. Replaced with native `node:stream` `Transform` across 6 gulp task files (`defaults.js`, `distribute.js`, `jsonToHtml.js`, `minifyHtml.js`, `sass.js`, `utils/template-transform.js`). through2@5 became ESM-only with no `require` condition in its exports, breaking CJS require; the built-in `Transform` is a drop-in replacement
|
|
514
|
-
|
|
515
|
-
### Changed
|
|
516
|
-
- Bumped `@babel/preset-env` from ^7.29.2 to ^7.29.5
|
|
517
|
-
- Bumped `dompurify` from ^3.3.3 to ^3.4.2
|
|
518
|
-
- Bumped `dotenv` from ^17.4.1 to ^17.4.2
|
|
519
|
-
- Bumped `fast-xml-parser` from ^5.5.11 to ^5.7.3
|
|
520
|
-
- Bumped `gulp-filter` from ^9.0.1 to ^10.0.0 (Node 22 ESM-CJS interop keeps `require('gulp-filter').default` working)
|
|
521
|
-
- Bumped `html-validate` from ^10.11.3 to ^10.16.0
|
|
522
|
-
- Bumped `libsodium-wrappers` from ^0.8.3 to ^0.8.4
|
|
523
|
-
- Bumped `postcss` from ^8.5.9 to ^8.5.14
|
|
524
|
-
- Bumped `prettier` from ^3.8.2 to ^3.8.3
|
|
525
|
-
- Bumped `web-manager` from ^4.1.40 to ^4.1.41
|
|
526
|
-
- Bumped `webpack` from ^5.106.1 to ^5.106.2
|
|
527
|
-
|
|
528
|
-
---
|
|
529
|
-
## [1.1.9] - 2026-04-23
|
|
530
|
-
### Added
|
|
531
|
-
- Admin users page: "Sign in as user" dropdown option that calls BEM `POST /backend-manager/user/token` to generate a custom auth token, then shows a modal with the sign-in URL (copy button + open-in-new-tab button)
|
|
532
|
-
- Modal opens immediately in a loading state while the token is generated, then swaps to ready/error state
|
|
533
|
-
- Auth signin page: handle `authCustomToken` URL param via Firebase `signInWithCustomToken`, redirecting to `authReturnUrl` (validated) or `/dashboard`
|
|
534
|
-
|
|
535
|
-
### Fixed
|
|
536
|
-
- Billing section: cancel subscription button now appears for suspended paid subscriptions (previously hidden). Logic updated to `isPaid && rawStatus !== 'cancelled' && !resolved.cancelling` so it correctly shows for active, trialing, and suspended paid subs, while hiding for free users, already-cancelled subs, and subs with pending cancellation
|
|
537
|
-
|
|
538
|
-
### Changed
|
|
539
|
-
- Admin users table: dropdown trigger button restyled using `btn-outline-adaptive rounded-circle` for a cleaner look
|
|
540
|
-
|
|
541
|
-
---
|
|
542
|
-
## [1.1.8] - 2026-04-22
|
|
543
|
-
### Changed
|
|
544
|
-
- Widen backend sidebar from 282px to 283px so inner content (after `p-3` horizontal padding) clears the 250px minimum required by Google AdSense units
|
|
545
|
-
- Apply same 283px width to mobile offcanvas sidebar (`#mobileSidebar`) via `--bs-offcanvas-width` to override Bootstrap's default 400px
|
|
546
|
-
- Simplify admin firebase page cell rendering: drop redundant `String()` wrapping around values passed to `escapeHTML()` (already coerces to string internally)
|
|
547
|
-
|
|
548
|
-
---
|
|
549
|
-
## [1.1.7] - 2026-04-10
|
|
550
|
-
### Changed
|
|
551
|
-
- Update dependencies: web-manager to 4.1.39, webpack to 5.106.1, prettier to 3.8.2, libsodium-wrappers to 0.8.3, prepare-package to 2.1.0
|
|
552
|
-
- Add empty `hooks` object to `preparePackage` config in package.json for prepare-package 2.1.0's new hooks feature
|
|
553
|
-
|
|
554
|
-
---
|
|
555
|
-
## [1.1.6] - 2026-04-09
|
|
556
|
-
### Changed
|
|
557
|
-
- Add `hover-flex` prebuilt animation class to pricing page billing cycle toggle (Monthly/Annually) for subtle scale-up on hover
|
|
558
|
-
- Update README and TODO docs to use `npx mgr` instead of `npx uj`
|
|
559
|
-
- Fix `[Billing] Cancel complete` log to read product ID from current account instead of undefined variable
|
|
560
|
-
|
|
561
|
-
---
|
|
562
|
-
## [1.1.5] - 2026-04-09
|
|
563
|
-
### Changed
|
|
564
|
-
- Move pricing and feature limit values from layout frontmatter to default `_config.yml` under `web_manager.payment.products`, making the pricing page fully config-driven
|
|
565
|
-
- Add default `payment.products` array with 4 example plans (basic, plus, pro, max) including limits, prices, and trial config
|
|
566
|
-
- Handle boolean `true` config limits in feature value display (renders feature name only, check icon in comparison table)
|
|
567
|
-
|
|
568
|
-
---
|
|
569
|
-
## [1.1.4] - 2026-04-09
|
|
570
|
-
### Changed
|
|
571
|
-
- Update web-manager from v4.1.37 to v4.1.38
|
|
572
|
-
|
|
573
|
-
---
|
|
574
|
-
## [1.1.3] - 2026-04-08
|
|
575
|
-
### Security
|
|
576
|
-
- Escape all remaining unescaped innerHTML values (formatDate, formatDateTime, formatIncidentStatus, formatTimeAgo, statusLabels, dataStatusMap, numeric values) for defense-in-depth
|
|
577
|
-
- Add `https://` scheme validation to `window.open()` and `href` attributes for push notification URLs in calendar-events
|
|
578
|
-
- Remove `style` from DOMPurify `ALLOWED_ATTR` in campaign email preview to prevent CSS-based data exfiltration
|
|
579
|
-
|
|
580
|
-
---
|
|
581
|
-
## [1.1.2] - 2026-04-08
|
|
582
|
-
### Fixed
|
|
583
|
-
- Fix AdSense minimum width error in dashboard sidebar by increasing sidebar width from 280px to 282px (content area now meets 250px minimum)
|
|
584
|
-
|
|
585
|
-
### Changed
|
|
586
|
-
- Update dependencies: fast-xml-parser, postcss, webpack, wonderful-fetch, prepare-package
|
|
587
|
-
|
|
588
|
-
---
|
|
589
|
-
## [1.1.1] - 2026-04-06
|
|
590
|
-
### Security
|
|
591
|
-
- Fix open redirect via `authReturnUrl` URL parameter in core/auth.js — now validated with `isValidRedirectUrl()`
|
|
592
|
-
- Fix cross-origin redirect via unvalidated postMessage in vert.js — added origin allowlist
|
|
593
|
-
- Replace `new Function()` code execution in redirect.js with safe named modifier lookup
|
|
594
|
-
- Sanitize markdown-it output with DOMPurify in campaign-preview.js (newsletter-safe tag allowlist)
|
|
595
|
-
- Validate OAuth redirect URL scheme in connections.js
|
|
596
|
-
- Escape `classes` parameter in prerendered-icons.js to prevent attribute breakout
|
|
597
|
-
- Defense-in-depth: escape `formatDate()` outputs in security.js, team.js, referrals.js
|
|
598
|
-
- Defense-in-depth: escape cancel/refund reason strings in billing.js, refund.js
|
|
599
|
-
- Defense-in-depth: escape `submittingText` in form-manager.js spinner
|
|
600
|
-
- Document redirect validation, postMessage origin checks, eval prohibition, and DOMPurify rules in CLAUDE.md
|
|
601
|
-
|
|
602
|
-
### Added
|
|
603
|
-
- `dompurify` dependency for HTML sanitization
|
|
604
|
-
|
|
605
|
-
## [1.1.0] - 2026-04-06
|
|
606
|
-
### Added
|
|
607
|
-
- `payment-config.js` shared library for reading payment data from build-time config
|
|
608
|
-
- Pricing layout resolves prices and feature limits from `_config.yml` when not set in frontmatter
|
|
609
|
-
- `oauth2` config injected into client-side Configuration object via `foot.html`
|
|
610
|
-
- Pricing page shows "Switch to This Plan" on other paid plans when user has active subscription
|
|
611
|
-
|
|
612
|
-
### Changed
|
|
613
|
-
- Move `payment` under `web_manager` in default `_config.yml` so it serializes into client-side config
|
|
614
|
-
- Checkout page uses `payment-config.js` instead of fetching `/backend-manager/brand`
|
|
615
|
-
- Account billing section uses config for products/limits/currency instead of brand API
|
|
616
|
-
- Account connections section reads `oauth2` from config instead of brand API
|
|
617
|
-
- Admin dashboard uses config for product list in MRR calculations
|
|
618
|
-
- Remove `/backend-manager/brand` fetch from account page entirely
|
|
619
|
-
- "Everything in [plan]" now uses dynamic previous plan name instead of hardcoded index
|
|
620
|
-
|
|
621
|
-
### Fixed
|
|
622
|
-
- Liquid 4.x compatibility: use loop-based hash lookup instead of bracket notation for config limits
|
|
623
|
-
|
|
624
|
-
## [1.0.22] - 2026-04-05
|
|
625
|
-
### Changed
|
|
626
|
-
- Bump web-manager from ^4.1.36 to ^4.1.37
|
|
627
|
-
- Bump dotenv from ^17.4.0 to ^17.4.1
|
|
628
|
-
- Bump html-validate from ^10.11.2 to ^10.11.3
|
|
629
|
-
|
|
630
|
-
## [1.0.21] - 2026-04-03
|
|
631
|
-
### Fixed
|
|
632
|
-
- Disable cache breaker on Slapform contact form fetch to prevent appending cache-busting query params to POST request
|
|
633
|
-
|
|
634
|
-
## [1.0.20] - 2026-04-03
|
|
635
|
-
### Fixed
|
|
636
|
-
- Fix contact form sending `user: map[]` to Slapform by replacing nested `user` object with flat `uid` string field
|
|
637
|
-
- Autofill visible email input from auth state via `data-wm-bind="@value auth.user.email"` for logged-in users
|
|
638
|
-
- Remove redundant hidden `auth.user.email` field
|
|
639
|
-
|
|
640
|
-
## [1.0.19] - 2026-04-02
|
|
641
|
-
### Security
|
|
642
|
-
- Comprehensive XSS hardening: escape all dynamic data in innerHTML with `webManager.utilities().escapeHTML()`
|
|
643
|
-
- Remove all local `escapeHtml` implementations — single source of truth via web-manager
|
|
644
|
-
- Rebuild `showToast()` and `showNotification()` to use `textContent` instead of `innerHTML`
|
|
645
|
-
- Add `javascript:` protocol blocking in web-manager `@attr` binding directive
|
|
646
|
-
- Add URL scheme validation for vert.js postMessage handler
|
|
647
|
-
- Fix double-escaping in `showSuccess()`/`showError()`/`showNotification()` callers
|
|
648
|
-
- Document zero-trust XSS policy in CLAUDE.md and skills
|
|
649
|
-
|
|
650
|
-
### Changed
|
|
651
|
-
- Refactor webManager from passed parameter to direct singleton import across all modules
|
|
652
|
-
- Remove `init(wm)` pattern and Manager parameter passing throughout page modules
|
|
653
|
-
- Calendar core/events/renderer use direct imports instead of constructor injection
|
|
654
|
-
- Fix file structure and spacing across all JS files (consistent Libraries/Module pattern)
|
|
655
|
-
- Fix alternatives layout markdown code block rendering issue
|
|
656
|
-
|
|
657
|
-
---
|
|
658
|
-
## [1.0.18] - 2026-03-30
|
|
659
|
-
### Changed
|
|
660
|
-
- Removed redundant "Additional gems" comment from Gemfile template output in defaults.js
|
|
661
|
-
|
|
662
|
-
---
|
|
663
|
-
## [1.0.17] - 2026-03-30
|
|
664
|
-
### Added
|
|
665
|
-
- Configurable gems support via `gems` array in `config/ultimate-jekyll-manager.json`
|
|
666
|
-
- Function-based template data in defaults.js for runtime-computed values
|
|
667
|
-
|
|
668
|
-
---
|
|
669
|
-
## [1.0.16] - 2026-03-30
|
|
670
|
-
### Changed
|
|
671
|
-
- Removed @dev-only wrappers from page module loading console.log statements in src/index.js
|
|
672
|
-
|
|
673
|
-
---
|
|
674
|
-
## [1.0.15] - 2026-03-30
|
|
675
|
-
### Changed
|
|
676
|
-
- Bump web-manager from 4.1.32 to 4.1.33 (includes @sentry/* 10.46.0, chatsy 2.0.13)
|
|
677
|
-
|
|
678
|
-
---
|
|
679
|
-
## [1.0.13] - 2026-03-27
|
|
680
|
-
### Added
|
|
681
|
-
- MRR stat card on admin dashboard calculated from brand config prices × subscriber counts
|
|
682
|
-
- `setStatSubValue` helper in admin-helpers.js for displaying sub-metrics on stat cards
|
|
683
|
-
- Green "+N in 30d" sub-values under Total Users and Push Subscribers stat cards
|
|
684
|
-
- New "Active users (30d)" stat card on admin users page
|
|
685
|
-
|
|
686
|
-
### Changed
|
|
687
|
-
- Dashboard charts now use `getCountFromServer` queries per product × frequency instead of fetching all user docs
|
|
688
|
-
- Product list and billing frequencies derived dynamically from `/backend-manager/brand` API
|
|
689
|
-
- Consolidated "New users (30d)" from standalone card into sub-value under Total Users
|
|
690
|
-
|
|
691
|
-
### Fixed
|
|
692
|
-
- Pacman-shaped spinners in stat cards caused by `spinner-border-sm` inheriting `<h3>` font size (added `fs-6`)
|
|
693
|
-
|
|
694
|
-
### Removed
|
|
695
|
-
- `showUnauthenticated()` flows from all admin pages — pages now return early if no user
|
|
696
|
-
|
|
697
|
-
## [1.0.11] - 2026-03-24
|
|
698
|
-
### Added
|
|
699
|
-
- Firestore version + transport test page at `/test/libraries/firestore` for diagnosing SDK connectivity across browsers
|
|
700
|
-
|
|
701
|
-
## [1.0.10] - 2026-03-24
|
|
702
|
-
### Fixed
|
|
703
|
-
- `getUJMConfig()` now throws descriptive errors when config file is missing, empty, or malformed instead of crashing silently
|
|
704
|
-
- Admin dashboard subscription queries now filter by `subscription.status == 'active'` instead of expiry timestamp
|
|
705
|
-
|
|
706
|
-
### Changed
|
|
707
|
-
- Webpack watch path for web-manager changed from `src/` to `dist/`
|
|
708
|
-
|
|
709
|
-
## [1.0.9] - 2026-03-20
|
|
710
|
-
### Changed
|
|
711
|
-
- `authorizedFetch` no longer throws when no user is logged in; logs a warning and proceeds without the Authorization header
|
|
712
|
-
|
|
713
|
-
## [1.0.7] - 2026-03-20
|
|
714
|
-
### Changed
|
|
715
|
-
- Upgrade `web-manager` from ^4.1.29 to ^4.1.30
|
|
716
|
-
|
|
717
|
-
## [1.0.3] - 2026-03-16
|
|
718
|
-
### Added
|
|
719
|
-
- Ensure consuming projects have `"private": true` in package.json during setup to prevent accidental npm publishes
|
|
720
|
-
|
|
721
|
-
## [1.0.1] - 2026-03-15
|
|
722
|
-
### Changed
|
|
723
|
-
- Upgrade `node-powertools` from ^2.3.2 to ^3.0.0
|
|
724
|
-
- Upgrade `web-manager` from ^4.1.26 to ^4.1.28
|
|
725
|
-
- Upgrade `wonderful-fetch` from ^1.3.4 to ^2.0.4
|
|
726
|
-
- Upgrade `prepare-package` from ^1.2.6 to ^2.0.7
|
|
727
|
-
- Add `preparePackage.type: "copy"` configuration
|
|
728
|
-
|
|
729
|
-
## [Unreleased]
|
|
730
|
-
### Changed
|
|
731
|
-
- Migrate "app" terminology to "brand" across frontend and service worker: renamed `appData`/`fetchAppData` to `brandData`/`fetchBrandData`, `appConfig`/`fetchAppConfig` to `brandConfig`/`fetchBrandConfig`, API endpoint from `/backend-manager/app` to `/backend-manager/brand`, and `this.app` to `this.brand` in service worker
|
|
732
|
-
|
|
733
|
-
### Added
|
|
734
|
-
- Abandoned cart tracking on checkout page: creates a Firestore document in `payments-carts/{uid}` when authenticated users begin checkout, with a 15-minute first reminder delay
|
|
735
|
-
- Backend sidebar auto-expands collapsible dropdown sections containing the currently active page link (desktop and mobile)
|
|
736
|
-
- Email preferences page (`/portal/account/email-preferences`) for unsubscribe/resubscribe from marketing emails
|
|
737
|
-
- Email masking on preferences page to prevent forwarded-email abuse (e.g., `ia***b@gm***.com`)
|
|
738
|
-
- HMAC signature verification for unsubscribe links to prevent forged requests
|
|
739
|
-
- Checkout page supports daily, weekly, monthly, and annually billing frequencies with selective UI visibility via wm-bindings
|
|
740
|
-
- Default billing frequency auto-selects the longest available term (annually > monthly > weekly > daily), with URL param override
|
|
741
|
-
- Auth state settles before any authorized fetches fire on checkout, preventing race conditions
|
|
742
|
-
- Quick boot mode (`UJ_QUICK=true`) for faster dev server startup (~5s vs ~20s) by skipping clean, slow setup operations, and deferring webpack/sass compilation until after Jekyll's first build
|
|
743
|
-
- Dev-only warning in FormManager for form fields missing `name` attributes (skipped by validation and `getData()`)
|
|
744
|
-
- FAQPage JSON-LD schema with 3-level fallback chain (`schema.faq_page.items` → `faqs.items` → `alternative.faqs.items`)
|
|
745
|
-
- FAQPage schema enabled on blueprint pages with FAQ sections (pricing, contact, download, extension, alternatives)
|
|
746
|
-
- OG image dimension meta tags (`og:image:width`, `og:image:height`) with 1200×630 defaults
|
|
747
|
-
- Article published/modified time meta tags for blog posts
|
|
748
|
-
- Admin marketing calendar page (`/admin/calendar`) with custom-built interactive calendar for scheduling newsletters and notifications
|
|
749
|
-
- Calendar supports 4 view modes (month, week, day, year) with event CRUD, drag-and-drop, overlapping event layout, and `window.calendarAPI`
|
|
750
|
-
- Real-time red "now" line indicator in day/week views, updates every 60 seconds
|
|
751
|
-
- Viewport-locked admin layout variant (`themes/classy/admin/core/minimal-viewport-locked`) for full-height admin pages
|
|
752
|
-
- Feedback page (`/feedback`) with emoji rating selection, written feedback fields, review prompt modal, and analytics tracking
|
|
753
|
-
- FormManager auto-populates form fields from URL query parameters (skips utm_*, itm_*, cb, fbclid, gclid)
|
|
754
|
-
- Review prompt modal after positive feedback submission with copy-paste textarea and external review site link
|
|
755
|
-
|
|
756
|
-
### Changed
|
|
757
|
-
- Twitter card default from `summary` to configurable `summary_large_image`
|
|
758
|
-
- Rename `site.tracking` config to `site.analytics` with simplified keys (`google-analytics` → `google`, `meta-pixel` → `meta`, `tiktok-pixel` → `tiktok`)
|
|
759
|
-
- Update `webManager.config.tracking['meta-pixel']` to `webManager.config.analytics?.meta` in auth.js
|
|
760
|
-
- Replace hardcoded discount codes with server-side validation via `payments/discount` API endpoint
|
|
761
|
-
- Simplify payment intent payload: remove `auth`, `cancelUrl`, and `verification.status` fields; send `discountCode` from validated state
|
|
762
|
-
- Form submit falls back to first visible payment button when Enter is pressed instead of throwing
|
|
763
|
-
- Clear FormManager dirty state before redirect to avoid "leave site" prompt
|
|
764
|
-
- Use proper adjective forms in subscription terms text (e.g., "annual" instead of "annually")
|
|
765
|
-
- Add discount disclaimer to subscription terms when a discount code is applied
|
|
766
|
-
- Align billing section to backend SSOT: consume unified subscription structure directly (3 statuses, `product.id` as object, `payment.price` in dollars, `cancellation.pending`, `trial.claimed` + `trial.expires`)
|
|
767
|
-
- Use WM bindings (`data-wm-bind`) for billing plan heading, action button visibility, and cancel trigger instead of manual JS DOM manipulation
|
|
768
|
-
- Standardize cancel, delete, and data-request forms to use FormManager built-in `required` validation instead of manual disabled toggle and checkbox throws
|
|
769
|
-
- Test subscriptions now deep-merge into real user data instead of full replacement, preserving actual product/payment info
|
|
770
|
-
- Add `onsubmit="return false"` to all JS-managed forms as a safety net against native submission before FormManager loads
|
|
771
|
-
- Checkout payment method buttons start hidden and are revealed via `data-wm-bind` when payment methods load
|
|
772
|
-
- Remove development-only guard from click prevention logging in body.html
|
|
773
|
-
|
|
774
|
-
### Removed
|
|
775
|
-
- Remove hardcoded `DISCOUNT_CODES` map and `autoApplyWelcomeCoupon` (replaced by server-side validation)
|
|
776
|
-
- Remove `generateCheckoutId` and `state.checkoutId` from checkout session
|
|
777
|
-
- Unexport `resolvePrice` helper (internal-only usage)
|
|
778
|
-
|
|
779
|
-
### Fixed
|
|
780
|
-
- Fix broken `</>` tag in checkout HTML causing page rendering to break
|
|
781
|
-
- Fix checkout price display for APIs returning plain numbers instead of `{amount: N}` objects
|
|
782
|
-
- Fix quantity badge styling (proper circle instead of pill shape)
|
|
783
|
-
- Fix form checkboxes missing `name` attributes causing FormManager to silently skip validation (cancel, delete forms)
|
|
784
|
-
- Fix admin forms (notifications, users) and blog/status forms missing `novalidate`, `onsubmit`, `name` attributes, and `.button-text` spans
|
|
785
|
-
- Fix profile premium badge using removed `trialing` status and `access` field
|
|
786
|
-
- Add dev-only artificial pre-delay support to checkout page for testing form protection timing
|
|
787
|
-
- Fix `btn-check:checked` outline button styling in classy theme — transparent `!important` rule was overriding Bootstrap's checked background due to higher CSS specificity
|
|
788
|
-
|
|
789
|
-
---
|
|
790
|
-
## [1.0.0] - 2024-06-19
|
|
791
|
-
### Added
|
|
792
|
-
- Initial release of the project 🚀
|