@basaltkit/comments adds threaded comments to any resource — a note, a project, a task — with @mentions and resolve/reopen, scoped per tenant. It emits events that bridge cleanly to realtime (live discussion) and notifications (alert the mentioned).
Register commentsPlugin and mount the ready-made REST routes through your adapter. In dev the store is in-memory; in production pass a store backed by @basaltkit/comments-prisma or -sqlite:
add extracts mentions with @([\w-]+) by default. Pass mentionPattern (a regex whose first capture group is the user id) to commentsPlugin to match your own id scheme.
add extracts @mentions from the body (configurable pattern), stores them on the comment, and emits comment:created plus one comment:mentioned per mentioned user. Also: edit, remove, resolve(id, by), reopen(id).
Every mutation emits a hook (comment:created, comment:mentioned, comment:updated, comment:deleted, comment:resolved, comment:reopened), so live updates and notifications wire up with no coupling. Subscribe on app.hooks:
ts
import { REALTIME } from '@basaltkit/realtime'import { NOTIFIER, defineNotification } from '@basaltkit/notifications'import { z } from 'zod'import { app } from './app.js'const realtime = app.container.get(REALTIME)const notifier = app.container.get(NOTIFIER)const CommentMention = defineNotification({ name: 'comment.mention', schema: z.object({ by: z.string() }), channels: ['inApp'], via: { inApp: ({ by }) => ({ title: 'You were mentioned', data: { by } }) },})// push new comments to everyone viewing the resourceapp.hooks.on('comment:created', ({ comment }) => realtime .to(comment.tenantId) .channel(`${comment.resourceType}:${comment.resourceId}`) .emit('comment', comment))// notify the mentioned — one hook fires per mentioned userapp.hooks.on('comment:mentioned', ({ comment, userId }) => notifier.notify({ id: userId }, CommentMention, { by: comment.authorId }))
commentRoutes() (require a logged-in user; author taken from ctx().user): GET /comments?resourceType=&resourceId=, POST /comments, PATCH /comments/:id, DELETE /comments/:id, POST /comments/:id/resolve and /reopen. Editing and deleting are restricted to the comment's author. Everything is tenant-scoped.
Ready-made UI is not needed here — comments render inline in your app — but the same self-contained pattern powers the audit viewer.
Comments
@basaltkit/commentsadds threaded comments to any resource — a note, a project, a task — with @mentions and resolve/reopen, scoped per tenant. It emits events that bridge cleanly to realtime (live discussion) and notifications (alert the mentioned).Setup
Register
commentsPluginand mount the ready-made REST routes through your adapter. In dev the store is in-memory; in production pass astorebacked by@basaltkit/comments-prismaor-sqlite:Custom @mention pattern
addextracts mentions with@([\w-]+)by default. PassmentionPattern(a regex whose first capture group is the user id) tocommentsPluginto match your own id scheme.Add and read
addextracts @mentions from the body (configurable pattern), stores them on the comment, and emitscomment:createdplus onecomment:mentionedper mentioned user. Also:edit,remove,resolve(id, by),reopen(id).Live discussion + mention notifications
Every mutation emits a hook (
comment:created,comment:mentioned,comment:updated,comment:deleted,comment:resolved,comment:reopened), so live updates and notifications wire up with no coupling. Subscribe onapp.hooks:Routes
commentRoutes()(require a logged-in user; author taken fromctx().user):GET /comments?resourceType=&resourceId=,POST /comments,PATCH /comments/:id,DELETE /comments/:id,POST /comments/:id/resolveand/reopen. Editing and deleting are restricted to the comment's author. Everything is tenant-scoped.Ready-made UI is not needed here — comments render inline in your app — but the same self-contained pattern powers the audit viewer.