Skip to content

Releases: karthikeyankc/discuss

v0.5.8

Choose a tag to compare

@karthikeyankc karthikeyankc released this 27 Jul 13:28
e858e78

Comment editing

Authors can edit their own comments within 15 minutes of posting. An edit button appears next to the timestamp immediately after a comment is submitted. Clicking it opens an inline edit form in place, reusing the existing form styles. Save patches the comment and re-renders the widget. Cancel restores the original content.

The edit token is stateless — the server derives it from an HMAC-SHA256 of the comment ID and creation timestamp, so nothing extra is stored in the database. The token lives in memory on the widget instance and is cleared on page reload. The 15-minute window is enforced server-side regardless of token validity.

A new edited_at column is added to the comments table. The migration runs automatically on startup and is backwards-compatible with existing installs.

Fixes

  • CORS preflight for cross-origin PATCH: X-Edit-Token was missing from Access-Control-Allow-Headers, which would have caused the preflight check to fail silently for cross-origin edit requests.
  • Edit cancel corrupting comment body: the cancel handler was re-attaching a click listener to the edit button on every cancel. On the next edit, two _startInlineEdit calls fired per click — the second captured an empty serialised form as originalHtml, so cancelling replaced the comment body with a blank textarea. Removing the redundant re-attachment fixes both the blank-text symptom and the Reply/Share/Edit buttons staying visible while the form was open.

Other changes

  • X-App-Version response header on every request, read from package.json at startup.
  • Demo index page hero updated with project-oriented copy and a docs link.
  • Widget-note callouts on all four demo thread pages converted to GitHub-style callouts.
  • README updated with comment editing docs, token design explanation, and v0.5.8 upgrading note.
  • Dependency updates: body-parser, brace-expansion, linkify-it, postcss (resolves 3 high-severity audit advisories).

Upgrading

Schema migration runs automatically on startup — no manual steps needed.

git pull origin main
npm install --production
sudo systemctl restart discuss

v0.5.7

Choose a tag to compare

@karthikeyankc karthikeyankc released this 06 Jul 15:14

Fixed

  • CSS variable light-dark() support: primaryColor: 'var(--my-brand)' now works correctly when the variable's value uses light-dark() or any other browser-resolved syntax (relative oklch(), chained variables, etc.). Previously, getPropertyValue returned the raw token string which the resolver could not parse. The fix applies the variable to a temporary element's color property and reads back the browser-computed rgb() value instead.

Changed

  • hsl/oklch/rgb resolver tests strengthened: tests now compare --brand-600 against the browser-computed equivalent rather than just checking the output matches a hex pattern. This validates correctness of the conversion math, not just the output format.
  • Thread 4 demo page: --discuss-brand now uses light-dark(#dc2626, #0d9488) — red in light mode, teal in dark mode — to demonstrate CSS variable + light-dark() support end-to-end.

v0.5.6

Choose a tag to compare

@karthikeyankc karthikeyankc released this 06 Jul 12:55

Added

  • Admin UI browser test suite: 35 automated Playwright tests covering auth flow, overview stats, inbox (approve, delete, pin, edit, bulk approve, bulk delete, tab switch), domain settings (general fields, colour picker with WCAG indicators), domain management, and search (results, empty state, short-query guard, API failure).
  • Widget browser test suite expanded: new tests for placeholder option, honeypot_question config, handleSubmit success and failure paths, collapse toggle, and #rgb 3-char hex shorthand. Total browser test count raised from 63 to 73.
  • npm run bundle-size script: prints a formatted table of raw and gzip sizes for client.js and client.css. Run locally after a build to verify bundle weight before shipping.
  • Pre-commit test quality checks: hook now blocks commits that contain focused tests (.only() or waitForTimeout() calls in test files. It also verifies README badge counts match the actual unit and browser test totals.

Fixed

  • Admin search empty state: searching with no matching results now shows "No results for..." as expected. Previously it showed "Search failed. Please try again." because _esc() was called but never defined on the app object — the TypeError was silently swallowed by the surrounding try/catch.
  • Admin hex colour normalisation: typing a hex value without a leading # in the colour picker input now normalises the displayed field value immediately (e.g. 2563eb#2563eb). Previously the field showed the bare value while the PATCH body was sent correctly.
  • Widget mockApi route shadowing: the broad **/api/comments* pattern was registered after the specific **/api/comments/config* pattern, so Playwright's LIFO handler resolution meant config requests were intercepted by the comments handler. The registration order is now correct — config route is registered last and wins.
  • Approve URL assertion race: expect(approveUrl).toContain(...) ran synchronously before the route handler had time to capture the URL. Replaced with expect.poll().
  • Delete test DOM assertion: added a stateful re-fetch mock so loadInbox() returns an empty list after the comment is deleted, allowing the inbox-item count to be asserted as 0.

Changed

  • Toast assertions tightened: tests now assert .toast-success / .toast-error class and exact message text rather than just checking the toast is visible.
  • Dark-mode assertion: replaced per-channel threshold with a perceived-luminance check (0.299r + 0.587g + 0.114b < 0.3). Robust across any dark surface token.
  • Short-query negative assertion: replaced waitForTimeout(600) with a page.waitForRequest race — resolves to false if no request fires within 500 ms instead of sleeping unconditionally.
  • README Customisation section: added placeholder option with code example alongside the existing title, icons, and font entries.
  • Demo pages: thread 2 uses a custom green (#16a34a) to demonstrate primaryColor override; thread 3 uses violet (#7c3aed) with a custom title and placeholder.
  • Docs cleanup: removed stale configuration.md, deployment.md, embed.md, and smtp.md (content consolidated into README in v0.5.0). Fixed dead links in upgrading.md and CHANGELOG.md.

v0.5.5

Choose a tag to compare

@karthikeyankc karthikeyankc released this 05 Jul 16:36
e791eac

Added

  • Playwright browser test suite: 28 automated tests covering colour resolution, dark mode, semantic token hierarchy, widget rendering, form validation, and interactions.
  • placeholder option: pass placeholder to new DiscussWidget({...}) to override the default comment textarea placeholder text.

Changed

  • Semantic CSS tokens: all internal shorthand tokens renamed to explicit semantic names (--text-primary, --surface-base, --brand-600, --border-default, etc.). Rename applied at build time with no runtime aliases and no size overhead.
  • Comment body text uses --text-secondary for visual separation from the commenter name, which stays at --text-primary.

Fixed

  • Dark mode code block bug: <pre> blocks rendered with a near-white background when darkSelector was active.
  • Share highlight dark detection now uses this.darkSelector instead of a hardcoded .dark class check.

v0.5.2

Choose a tag to compare

@karthikeyankc karthikeyankc released this 05 Jul 12:45

Bug fix

darkSelector was silently ignored when using auto-init. If you embedded the widget by dropping in the script tag without a manual new DiscussWidget() call, dark mode had no effect even if you passed darkSelector as an option.

The auto-initialiser now reads the selector from a data-dark-selector attribute on the container:

<div id="discuss-comments" data-dark-selector=".dark"></div>
<script src="https://your-server.com/client.js"></script>

If you initialise manually with new DiscussWidget({ darkSelector: '...' }), nothing changes — that path was already working.

Upgrading from v0.5.1

If you set up dark mode after v0.5.1 and it wasn't working, switch from:

<div id="discuss-comments"></div>
<script src="https://your-server.com/client.js"></script>
<script>new DiscussWidget({ darkSelector: '.dark' });</script>

To:

<div id="discuss-comments" data-dark-selector=".dark"></div>
<script src="https://your-server.com/client.js"></script>

No database changes. No other breaking changes.

v0.5.1

Choose a tag to compare

@karthikeyankc karthikeyankc released this 05 Jul 12:31
990455f

What's new

darkSelector option — pass any CSS selector to new DiscussWidget({ darkSelector: '...' }) and the widget applies its dark colour scheme when that selector matches an ancestor element.

new DiscussWidget({ darkSelector: 'html.dark' });

The widget has no default dark mode. It stays light unless you opt in, which means it no longer goes dark unexpectedly on sites that don't use dark mode.

Works with any class-based or attribute-based dark mode toggle — .dark, [data-theme="dark"], whatever your site uses.

Upgrading

Add darkSelector to your existing embed:

<link rel="stylesheet" href="https://your-server.com/client.css">
<div id="discuss-comments"></div>
<script src="https://your-server.com/client.js"></script>
<script>new DiscussWidget({ darkSelector: 'html.dark' });</script>

No database changes. No breaking changes for existing embeds — they just won't have dark mode until you add the option.

v0.5.0

Choose a tag to compare

@karthikeyankc karthikeyankc released this 04 Jul 18:29
0da3704

What's new

Separate CSS bundle

Widget styles now ship as a standalone client.css file. This cuts client.js from 112 KB to 15 KB (4.7 KB gzip). The stylesheet is 91 KB (12.9 KB gzip), so the total for a styled embed is around 17.6 KB gzip — and browsers can cache the two files independently.

Update your embed snippet to include the stylesheet link:

<link rel="stylesheet" href="https://discuss.example.com/client.css">
<div id="discuss-comments"></div>
<script src="https://discuss.example.com/client.js"></script>

The snippet in your admin dashboard has already been updated. Leaving out the <link> tag is valid if you're writing your own styles.

Icon slots

Swap out the built-in icons on the comment form with your own SVGs, or pass '' to hide one entirely:

new DiscussWidget({
  icons: {
    name:   '<svg>...</svg>',
    email:  '<svg>...</svg>',
    submit: '',  // no icon on the submit button
  }
});

The input adjusts its padding automatically when an icon is removed.

Title option

Override the "Leave a comment" heading with any text:

new DiscussWidget({ title: 'Join the discussion' });

Help menu in the admin sidebar

The version badge is now a proper Help menu with links to documentation, GitHub, changelog, and a bug report form. It also checks GitHub releases once per session and shows a notification if a newer version is available.

Gravatar fix on admin edit

Editing a commenter's email in the admin now updates their avatar immediately without needing a page reload.

Breaking change

The embed snippet must be updated to include the <link rel="stylesheet"> tag. See above.

Full changelog

See CHANGELOG.md for the complete list of changes.

v0.4.0

Choose a tag to compare

@karthikeyankc karthikeyankc released this 02 Jul 14:39
b099114

What's new

Added

  • Export comments - download all comments for a domain as a JSON file from the new Advanced settings tab
  • Advanced settings tab - replaces the delete button in the domain list with a proper Export and Danger Zone section inside domain settings
  • allowed_origins per domain - one origin per line in General settings; allows local dev servers (e.g. http://localhost:4321) to post and load comments for a domain without registering a separate domain (#9)
  • --discuss-font-family CSS variable - pass a custom font family to the embed widget via the host page's CSS (#10)
  • AI usage disclosure - AI.md documents which AI tools were used during development (#7)

Fixed

  • Post URL trailing slash normalization - /post/ and /post now resolve to the same thread. Existing data is migrated automatically on first server start after upgrade.
  • Admin post comments view: config now fetched via admin-authenticated endpoint, eliminating CORS 403 errors when viewing a post's comments from within the admin
  • Admin post comments view: breadcrumb domain name now shown correctly on direct page load or browser refresh
  • Admin post comments view: delete, approve, and pin actions now work correctly
  • Admin posts view: post URLs now show the full domain and path (e.g. example.com/post) rather than just the path
  • CORS middleware: allowed_origins matching now works correctly end-to-end
  • client.js now served with Access-Control-Allow-Origin: * so it can be loaded as a cross-origin script
  • CSS --discuss-font-family variable now inherits correctly through chained var() references (#10)
  • .env file now loaded correctly via Node's native --env-file flag (#8)

Changed

  • Domain delete moved out of the domain list into Advanced > Danger Zone - reduces accidental deletion risk
  • Node.js minimum version bumped to 20.6+ (required for --env-file support)
  • Test suite expanded from 144 to 163 tests; statement coverage raised from 80% to 90%
  • Pre-commit hook now runs the full test suite - any failing test blocks the commit
  • CI adds a coverage gate that fails if coverage drops below 80% statements / 75% branches

Upgrading from v0.3.x

No manual database changes needed. The trailing-slash migration runs automatically on first server start.

Apache users: add AllowEncodedSlashes NoDecode to your VirtualHost and append nocanon to your ProxyPass directive, or admin pages will 404 on direct load or browser refresh. See docs/deployment.md for the full example config.


Thanks to @krisbalintona for reporting issues #7, #8, #9, and #10 that drove most of the fixes and additions in this release.