chore: sweep em dashes from our own source and README (#13)

This commit was merged in pull request #13.
This commit is contained in:
2026-10-03 22:03:00 +00:00
parent 95edbcd20b
commit f5d1ced4d6
7 changed files with 70 additions and 32 deletions
+5 -5
View File
@@ -35,7 +35,7 @@ import { recencyGroup, truncate } from "../utils";
*
* - The search bar is the composer. It is the only always-focused text input in
* a `List`, which is what makes "hit a hotkey, then just start typing" work.
* - `filtering` is off, because the left column is a conversation switcher — the
* - `filtering` is off, because the left column is a conversation switcher. The
* builtin fuzzy filter would hide rows as the user types their question.
* - `Detail` accepts no typed input (`onSearchTextChange` exists only on `List`,
* `Grid`, `Form` and `Dropdown`), so the left column cannot be dropped: it is
@@ -87,7 +87,7 @@ export function ChatView({ initialSession, initialPrompt }: ChatViewProps) {
/**
* Reopening the command continues the most recent conversation, but only if it
* was used recently. Past the configured idle window it starts a fresh chat
* instead — the previous conversation is never removed, it just drops down the
* instead. The previous conversation is never removed, it just drops down the
* sidebar. Setting the preference to "Never" keeps the last chat open forever.
*/
useEffect(() => {
@@ -218,7 +218,7 @@ export function ChatView({ initialSession, initialPrompt }: ChatViewProps) {
/**
* The primary action adapts to the search bar. With text in it, Return sends;
* with it empty — where sending would be a no-op anyway — Return opens the
* with it empty (where sending would be a no-op anyway), Return opens the
* highlighted conversation instead.
*/
const rowActions = (row: SessionRow) => {
@@ -249,8 +249,8 @@ export function ChatView({ initialSession, initialPrompt }: ChatViewProps) {
}
const placeholder = chat.status
? `${chat.status} — press ⏎ to stop`
: "Lam is thinking — press ⏎ to stop";
? `${chat.status}. Press ⏎ to stop`
: "Lam is thinking. Press ⏎ to stop";
return (
<List
+5 -5
View File
@@ -15,8 +15,8 @@ import type { ChatTurn, ThreadMessage } from "./types";
* and a `**Lam**` header on each exchange gave both speakers the same visual
* weight and made the reader scan past chrome to reach the answer.
*
* Raycast solves the same problem with alignment — right-aligned bubbles for
* the user — but markdown offers no alignment control, so styling is the only
* Raycast solves the same problem by aligning messages: right-aligned bubbles
* for the user. Markdown offers no alignment control, so styling is the only
* lever available.
*/
@@ -66,7 +66,7 @@ function renderMessage(message: ThreadMessage, options: TranscriptOptions): stri
// The one thing allowed to break the plain-text rule, because it means
// something went wrong and should not be skimmed past.
if (message.error) sections.push("", `**Error** — ${message.error}`);
if (message.error) sections.push("", `**Error:** ${message.error}`);
if (options.showToolCalls && message.toolCalls.length > 0) {
sections.push("", renderTools(message));
@@ -129,8 +129,8 @@ export function buildThreadMarkdown(
* Unlike the transcript this reads oldest-first: as a standalone document, a
* conversation is easier to read in the order it happened.
*
* This needs labels because the transcript's styling — blockquotes and dimmed
* text — is gone by the time it reaches a clipboard.
* This needs labels because the transcript's styling (blockquotes and dimmed
* text) is gone by the time it reaches a clipboard.
*/
export function sessionToPlainText(turns: ChatTurn[]): string {
if (turns.length === 0) return "";
+1 -1
View File
@@ -58,7 +58,7 @@ export function resolveResumeTimeout(value: string | undefined): number | undefi
/** Human-readable form of {@link ResolvedPreferences.resumeTimeoutMs}, for the setup screen. */
export function describeResumeTimeout(milliseconds: number | undefined): string {
if (milliseconds === undefined) return "Never — always reopen the last chat";
if (milliseconds === undefined) return "Never (always reopen the last chat)";
const minutes = Math.round(milliseconds / MINUTE);
if (minutes < 60) return `After ${minutes} minutes`;
+1 -1
View File
@@ -21,7 +21,7 @@ export function buildThread(turns: ChatTurn[], live: LiveState): ThreadMessage[]
const messages: ThreadMessage[] = [];
// Strictly newest message first. The search bar sits at the top of the window,
// so the most recent thing — an answer, once one has been written — should be
// so the most recent thing, an answer once one has been written, should be
// the first line under it. A consequence is that within an exchange the answer
// comes before its question, which is the intended reading order here.
for (let index = turns.length - 1; index >= 0; index--) {
+1 -1
View File
@@ -35,7 +35,7 @@ export type RecencyGroup = "Today" | "Yesterday" | "This week" | "Older";
* Buckets a timestamp for the conversation sidebar.
*
* Raycast groups its sidebar this way, and it buys the grouping that per-row
* timestamps were trying to convey — without putting a timestamp on every row.
* timestamps were trying to convey, without putting a timestamp on every row.
*/
export function recencyGroup(timestamp: number, now: number = Date.now()): RecencyGroup {
const startOfToday = new Date(now);