ReviewThread
A conversation anchored to lines of a change.
- AS
anacommentedyesterday
The same
> 0xffff ? 2 : 1check shows up again on line 14. Worth acodePointLength()helper? - SX
sergiocommented2 hours ago
Agreed, the string lexer needs it too. I'll add it to unicode.ts.
View code
<Frame name="thread-1841" src="/acme/delta/pull/412/threads/1841">
<ReviewThread
frame="thread-1841"
comments={[
{ id: "discussion_r1841", avatar: <Avatar>…</Avatar>, author: "ana", action: "commented", time: "yesterday", body: <p>…</p> },
{ id: "discussion_r1847", avatar: <Avatar>…</Avatar>, author: "sergio", action: "commented", time: "2 hours ago", body: <p>…</p> },
]}
replyAction="/acme/delta/pull/412/threads/1841/replies"
resolveAction="/acme/delta/pull/412/threads/1841/resolve"
fields={<input type="hidden" name="csrf" value={token} />}
labels={{
reply: "Reply",
replyPlaceholder: "Reply…",
resolve: "Resolve conversation",
unresolve: "Unresolve conversation",
outdated: "Outdated",
pending: "Pending",
suggestion: "Suggested change",
applySuggestion: "Apply suggestion",
columns,
}}
/>
</Frame>Demo apps
Whole pages built only from @sdxc/ui, each for an invented product with its own palette and type. They open in a new tab.
- Issue trackerCairnCairn, an invented issue tracker: grouped issue lists with filters in the address, a drag-and-drop board, projects on a roadmap, cycles with burn-up charts and an inbox./demo/apps/cairn
- Social networkChorusChorus, an invented social network: a live timeline with pictures, link cards, polls and threads, profiles, grouped notifications, search and messages./demo/apps/chorus
- Code hostDeltaDelta, a code host whose one repository is its own source: a file tree beside the code, line links, blame and the commits list, every file and commit read from git when the site is built./demo/apps/delta
- CMSInkwellInkwell, a demo CMS built from @sdxc/ui/app: posts with count tabs and quick edit, an editor with a quiet save status and a loud Publish, media and settings./demo/apps/inkwell
- Ops dashboardPulsePulse, an invented ops dashboard: Northwind's services with live filters, a peek sheet and a bulk bar, metrics that switch one chart, incidents, deploys and settings./demo/apps/pulse
- Support inboxRelayRelay, an invented support inbox: mailboxes, conversations and the open conversation on one screen, with contacts, reports and settings./demo/apps/relay
- Chat assistantTernTern, an invented chat assistant: conversations with streamed replies, code and tables, projects, a canvas, sharing and settings./demo/apps/tern
Every demo, landings and docs sites alike, is listed in the demo gallery.
Installation
An ordinary dependency: install it, import the theme once, and import the component where it is used.
npm add @sdxc/ui remiximport "@sdxc/ui/theme.css";Usage
import { ReviewThread } from "@sdxc/ui/app";Open, it shows the excerpt, comments, suggestion and forms; resolved or outdated, it folds into
a closed <details> behind one line. Every form is a plain POST, so it works without script and
needs no island; with the page's runtime, frame reloads only the thread in place.
Examples
Resolved, folded to one line
sergio marked this conversation as resolved
- AS
anacommentedyesterday
Nothing imports
./chars.jsafter this. Can it go? - SX
sergiocommentedyesterday
Deleted in b81d3e7.
View code
<ReviewThread
resolved
resolvedSummary="sergio marked this conversation as resolved"
comments={comments}
resolveAction="/acme/delta/pull/412/threads/1838/resolve"
labels={labels}
/>Outdated, with the code it was about
Outdatedsrc/lexer/identifier.ts
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -5,5 +5,5 @@ import { isIdentifierPart, isIdentifierStart } from "./chars.js"; | ||
| /** Reads one identifier starting at `offset`, or nothing when none starts there. */ | ||
| export function readIdentifier(source: string, offset: number): Token | undefined { | ||
| if (!isIdentifierStart(source.charCodeAt(offset))) return undefined; | ||
| if (!isIdentifierStart(source.codePointAt(offset) ?? 0)) return undefined; | ||
| let end = offset + 1; | ||
- MC
meicommented3 days ago
This reads
𝑥as a start, butend = offset + 1below still lands inside its surrogate pair.
View code
<ReviewThread
outdated
excerpt={{ path: "src/lexer/identifier.ts", hunks: excerpt, language: "ts" }}
comments={comments}
replyAction="/acme/delta/pull/412/threads/1822/replies"
labels={labels}
/>A suggested change and a pending reply
- AS
anasuggested a changeyesterday
Line 14 repeats this check. With a helper in
unicode.tsboth read the same: - SX
sergiocommentedPendingjust now
Applying it, and the same on line 14.
Suggested change
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -10 +10 @@ export function readIdentifier(source: string, offset: number): Token | undefine | ||
| let end = offset + (first > 0xffff ? 2 : 1); | ||
| let end = offset + codePointLength(first); | ||
View code
<ReviewThread
comments={[
{ author: "ana", action: "suggested a change", body: <p>…</p>, … },
{ author: "sergio", action: "commented", pending: true, body: <p>…</p>, … },
]}
suggestion={{
path: "src/lexer/identifier.ts",
hunks: diffLines(before, after, { context: 0 }).hunks,
language: "ts",
applyAction: "/acme/delta/pull/412/threads/1841/suggestions/1/apply",
}}
replyAction="/acme/delta/pull/412/threads/1841/replies"
resolveAction="/acme/delta/pull/412/threads/1841/resolve"
labels={labels}
/>Props
Read from the component's own types, so every prop, every default and every allowed value is listed.
| Prop | Type | Required | Description |
|---|---|---|---|
comments | readonly Comment[] | Yes | The thread's comments, oldest first. |
resolved | boolean | No | The thread is resolved, which folds it to resolvedSummary. |
resolvedSummary | RemixNode | No | The folded line of a resolved thread, such as "ana marked this conversation as resolved". |
outdated | boolean | No | The lines the thread cites have changed since, which folds it and labels it. |
excerpt | Lines | No | The code the thread is about, drawn above the comments: on the timeline, and for an outdated thread, whose original lines no longer show in the diff. |
suggestion | Suggestion | No | A change the reviewer proposes, drawn as a small diff under the comments. |
replyAction | string | No | Where a reply posts its text (as body); omitted, the thread draws no reply box. |
resolveAction | string | No | Where Resolve and Unresolve post resolved=true|false; omitted, no button. |
frame | string | No | The named frame the thread renders in, which every form reloads in place. |
fields | RemixNode | No | Hidden inputs every form carries, such as a CSRF token. |
labels | Labels | Yes | Every string the thread shows or announces, so it reads in any language. |
Also accepts everything in Omit<TagProps<"div">, "children">.