@hackcove/ui-react 0.10.0 → 0.10.2

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.
@@ -1,11 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { CONTROL_WIDTH, controlClass } from "./control.js";
3
3
  import { cx } from "./cx.js";
4
- /** Where a leading icon sits, and its size, by the control's size. */
4
+ /**
5
+ * Where a leading icon sits, and its size, by the control's size. The wrapper is a flex box centred
6
+ * on the field, so the icon's own box centres too, and its one child is sized whether it is a bare
7
+ * `<svg>` or an `Icon`.
8
+ */
5
9
  const ICON = {
6
- sm: "left-2 [&>svg]:h-3.5 [&>svg]:w-3.5",
7
- md: "left-3 [&>svg]:h-4 [&>svg]:w-4",
8
- lg: "left-4 [&>svg]:h-5 [&>svg]:w-5",
10
+ sm: "left-2 [&>*]:size-3.5",
11
+ md: "left-3 [&>*]:size-4",
12
+ lg: "left-4 [&>*]:size-5",
9
13
  };
10
14
  /** The prefix text's inset and size, by the control's size. */
11
15
  const PREFIX = {
@@ -13,9 +17,17 @@ const PREFIX = {
13
17
  md: "pl-3 text-sm",
14
18
  lg: "pl-4 text-base",
15
19
  };
16
- /** The relative wrapper of an `Input` that holds more than the field: a leading `icon` and, for search, the clear button. Not exported from the package. */
20
+ /**
21
+ * The relative wrapper of an `Input` that holds more than the field: a leading `icon` and, for search, the clear button. Not exported from the package.
22
+ *
23
+ * Inside a Card or row (`liftInteractive`, which is `isolate`) the field is lifted to `z-10`, and
24
+ * its opaque background would paint over a z-auto icon. So the icon ranks `z-20` there. It is
25
+ * scoped to an `isolate` ancestor so a field elsewhere on the page never outranks AppShell's
26
+ * sticky header (`z-10`). The clear button is a lifted `button` and comes after the field, so it
27
+ * already sits above it.
28
+ */
17
29
  export function InputShell({ icon, size = "md", width = "full", children, }) {
18
- return (_jsxs("div", { className: cx("relative", width === "auto" && "inline-block", width !== "full" && CONTROL_WIDTH[width]), children: [icon !== undefined && (_jsx("span", { "aria-hidden": "true", className: cx("pointer-events-none absolute top-1/2 -translate-y-1/2 text-slate-400", ICON[size]), children: icon })), children] }));
30
+ return (_jsxs("div", { className: cx("relative", width === "auto" && "inline-block", width !== "full" && CONTROL_WIDTH[width]), children: [icon !== undefined && (_jsx("span", { "aria-hidden": "true", className: cx("pointer-events-none absolute top-1/2 flex -translate-y-1/2 items-center leading-none text-slate-400 in-[.isolate]:z-20", ICON[size]), children: icon })), children] }));
19
31
  }
20
32
  /**
21
33
  * The field box of an `Input` with a `prefix`: the control look and the focus ring sit on the
@@ -0,0 +1,16 @@
1
+ import type { ReactElement, ReactNode } from "react";
2
+ import type { Passthrough } from "./props.js";
3
+ /**
4
+ * One tool call in a chat: a closed panel whose line says which tool, what it was doing and how it
5
+ * went; opening it shows the arguments and the result. The status is a word, not only a colour.
6
+ */
7
+ export declare function ToolCallCard({ name, status, summary, children, ...rest }: {
8
+ /** The tool's name, set in the monospace face. */
9
+ name: string;
10
+ /** How the call went: `running`, `done` or `error`, drawn as Running, Done or Failed. */
11
+ status: "running" | "done" | "error";
12
+ /** One line on what it did; a long one is cut off with an ellipsis. */
13
+ summary?: string | undefined;
14
+ /** The arguments and the result, shown while open. */
15
+ children: ReactNode;
16
+ } & Passthrough): ReactElement;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Disclosure } from "./Disclosure.js";
3
+ import { Pill } from "./Pill.js";
4
+ const STATUS = {
5
+ running: { word: "Running", tone: "slate" },
6
+ done: { word: "Done", tone: "emerald" },
7
+ error: { word: "Failed", tone: "red" },
8
+ };
9
+ /**
10
+ * One tool call in a chat: a closed panel whose line says which tool, what it was doing and how it
11
+ * went; opening it shows the arguments and the result. The status is a word, not only a colour.
12
+ */
13
+ export function ToolCallCard({ name, status, summary, children, ...rest }) {
14
+ const { word, tone } = STATUS[status];
15
+ return (_jsx(Disclosure, { ...rest, bordered: true, summary: _jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-2", children: [_jsx("span", { className: "max-w-1/2 shrink-0 truncate font-mono", children: name }), _jsx("span", { className: "min-w-0 flex-1 truncate font-normal text-slate-500", children: summary }), _jsx("span", { className: "shrink-0", children: _jsx(Pill, { tone: tone, children: word }) })] }), children: children }));
16
+ }
package/dist/control.js CHANGED
@@ -31,7 +31,7 @@ export function controlClass(size = "md", width = "full") {
31
31
  return [
32
32
  CONTROL_WIDTH[width],
33
33
  CONTROL_SIZE[size],
34
- `rounded-lg border border-slate-300 bg-white text-slate-900 placeholder:text-slate-400 transition hover:border-violet-400 disabled:opacity-50 ${focusRing}`,
34
+ `rounded-lg border border-slate-300 bg-white text-slate-900 placeholder:text-slate-400 text-ellipsis transition hover:border-violet-400 disabled:opacity-50 ${focusRing}`,
35
35
  ]
36
36
  .filter(Boolean)
37
37
  .join(" ");
@@ -4,13 +4,14 @@
4
4
  * highlighted code exactly as it recolours everything else.
5
5
  *
6
6
  * The grammars are deliberately shallow — enough for examples, not a compiler front end. `text`
7
- * returns the code as one plain run, so nothing is coloured where no language is named.
7
+ * returns the code as one plain run, so nothing is coloured where no language is named. The
8
+ * languages: `text`, `tsx`, `ts`, `js`, `json`, `bash`, `console` (a shell session: `$ ` commands
9
+ * scanned as bash, everything else output), `css`, `html` (with `<script>` scanned as JS and
10
+ * `<style>` as CSS) and `yaml`.
8
11
  */
9
- export type CodeLanguage = "text" | "tsx" | "ts" | "json" | "bash" | "css";
10
- export type TokenKind = "plain" | "comment" | "keyword" | "string" | "number" | "tag" | "attr" | "function";
11
- export type Token = {
12
- text: string;
13
- kind: TokenKind;
14
- };
12
+ import type { Token } from "./highlightKit.js";
13
+ export { consoleCommands } from "./highlightConsole.js";
14
+ export type { Token, TokenKind } from "./highlightKit.js";
15
+ export type CodeLanguage = "text" | "tsx" | "ts" | "js" | "json" | "bash" | "console" | "css" | "html" | "yaml";
15
16
  /** The coloured runs of `code`. The text of every token joined back together is `code`. */
16
17
  export declare function highlight(code: string, language: CodeLanguage): Token[];
package/dist/highlight.js CHANGED
@@ -4,8 +4,15 @@
4
4
  * highlighted code exactly as it recolours everything else.
5
5
  *
6
6
  * The grammars are deliberately shallow — enough for examples, not a compiler front end. `text`
7
- * returns the code as one plain run, so nothing is coloured where no language is named.
7
+ * returns the code as one plain run, so nothing is coloured where no language is named. The
8
+ * languages: `text`, `tsx`, `ts`, `js`, `json`, `bash`, `console` (a shell session: `$ ` commands
9
+ * scanned as bash, everything else output), `css`, `html` (with `<script>` scanned as JS and
10
+ * `<style>` as CSS) and `yaml`.
8
11
  */
12
+ import { scanConsole } from "./highlightConsole.js";
13
+ import { scanHtml } from "./highlightHtml.js";
14
+ import { scanYaml } from "./highlightYaml.js";
15
+ export { consoleCommands } from "./highlightConsole.js";
9
16
  /** The coloured runs of `code`. The text of every token joined back together is `code`. */
10
17
  export function highlight(code, language) {
11
18
  const tokens = [];
@@ -22,9 +29,16 @@ export function highlight(code, language) {
22
29
  emit("plain", code);
23
30
  else if (language === "bash")
24
31
  scanBash(code, emit);
32
+ else if (language === "console")
33
+ scanConsole(code, emit, scanBash);
25
34
  else if (language === "css")
26
35
  scanCss(code, emit);
27
- // JSON is JS with no code around it: keys are strings, `true`/`false`/`null` are keywords.
36
+ else if (language === "html")
37
+ scanHtml(code, emit, scanJs, scanCss);
38
+ else if (language === "yaml")
39
+ scanYaml(code, emit);
40
+ // JS, TS and TSX share one scanner. JSON is JS with no code around it: keys are strings,
41
+ // `true`/`false`/`null` are keywords.
28
42
  else
29
43
  scanJs(code, emit);
30
44
  return tokens;
@@ -0,0 +1,9 @@
1
+ import type { Emit, Scanner } from "./highlightKit.js";
2
+ /** A shell session: `$ ` is the prompt, the command after it is bash, other lines are output. */
3
+ export declare function scanConsole(code: string, emit: Emit, bash: Scanner): void;
4
+ /**
5
+ * What a console block's copy button copies: each command without its `$ `, plus the lines that
6
+ * continue it as written (the trailing `\\` and the indentation kept, so the paste runs), joined by
7
+ * newlines. Output is left out. A block with no `$ ` line has no commands, so it is copied as given.
8
+ */
9
+ export declare function consoleCommands(code: string): string;
@@ -0,0 +1,42 @@
1
+ import { splitLines } from "./highlightKit.js";
2
+ const CONSOLE_PROMPT = "$ ";
3
+ /**
4
+ * The lines of a console session, marked: a line that starts with `$ ` is a command (`prompt`), and
5
+ * so is every line after a command line that ends with `\\`, which continues it. The rest is output.
6
+ */
7
+ function consoleLines(code) {
8
+ let continued = false;
9
+ return splitLines(code).map(({ body, eol }) => {
10
+ const prompt = !continued && body.startsWith(CONSOLE_PROMPT);
11
+ const command = prompt || continued;
12
+ continued = command && body.endsWith("\\");
13
+ return { body, eol, command, prompt };
14
+ });
15
+ }
16
+ /** A shell session: `$ ` is the prompt, the command after it is bash, other lines are output. */
17
+ export function scanConsole(code, emit, bash) {
18
+ for (const { body, eol, command, prompt } of consoleLines(code)) {
19
+ if (prompt)
20
+ emit("prompt", CONSOLE_PROMPT);
21
+ const text = prompt ? body.slice(CONSOLE_PROMPT.length) : body;
22
+ if (command)
23
+ bash(text, emit);
24
+ else
25
+ emit("plain", text);
26
+ emit("plain", eol);
27
+ }
28
+ }
29
+ /**
30
+ * What a console block's copy button copies: each command without its `$ `, plus the lines that
31
+ * continue it as written (the trailing `\\` and the indentation kept, so the paste runs), joined by
32
+ * newlines. Output is left out. A block with no `$ ` line has no commands, so it is copied as given.
33
+ */
34
+ export function consoleCommands(code) {
35
+ const lines = consoleLines(code);
36
+ if (!lines.some((line) => line.prompt))
37
+ return code;
38
+ return lines
39
+ .filter((line) => line.command)
40
+ .map((line) => (line.prompt ? line.body.slice(CONSOLE_PROMPT.length) : line.body))
41
+ .join("\n");
42
+ }
@@ -0,0 +1,7 @@
1
+ import type { Emit, Scanner } from "./highlightKit.js";
2
+ /**
3
+ * HTML: comments, the doctype, tags with their attributes (a start tag may span lines), and text,
4
+ * which is plain. The content of `<script>` and `<style>` runs to the first closing tag of that
5
+ * name, wherever it falls, and is scanned with `script` (JS) or `style` (CSS).
6
+ */
7
+ export declare function scanHtml(code: string, emit: Emit, script: Scanner, style: Scanner): void;
@@ -0,0 +1,132 @@
1
+ import { sticky } from "./highlightKit.js";
2
+ const OPEN = /<(\/?)([A-Za-z][\w:-]*)/y;
3
+ const DOCTYPE = /<!doctype\b[^>]*>?/iy;
4
+ const SPACE = /\s+/y;
5
+ const ATTR = /[^\s"'<>/=]+/y;
6
+ const UNQUOTED = /[^\s>"'=<`]+/y;
7
+ /** `>` or `/>` where the tag ends. */
8
+ function tagEnd(code, at) {
9
+ if (code.startsWith("/>", at))
10
+ return "/>";
11
+ return code.charAt(at) === ">" ? ">" : undefined;
12
+ }
13
+ /** The value after an `=`: a quoted string (which may span lines) or a bare run, which stays plain. */
14
+ function takeValue(scan) {
15
+ const { code, at } = scan;
16
+ const c = code.charAt(at);
17
+ if (c === '"' || c === "'") {
18
+ const close = code.indexOf(c, at + 1);
19
+ const end = close === -1 ? code.length : close + 1;
20
+ scan.emit("string", code.slice(at, end));
21
+ scan.at = end;
22
+ return true;
23
+ }
24
+ const bare = sticky(UNQUOTED, code, at);
25
+ if (bare === undefined)
26
+ return false;
27
+ scan.emit("plain", bare);
28
+ scan.at += bare.length;
29
+ return true;
30
+ }
31
+ /** One piece of a tag's insides: space, `=`, a value, an attribute name, or a stray character. */
32
+ function tagStep(scan) {
33
+ const { code, at } = scan;
34
+ const space = sticky(SPACE, code, at);
35
+ if (space !== undefined) {
36
+ scan.emit("plain", space);
37
+ scan.at += space.length;
38
+ return;
39
+ }
40
+ if (code.charAt(at) === "=") {
41
+ scan.emit("plain", "=");
42
+ scan.afterEquals = true;
43
+ scan.at += 1;
44
+ return;
45
+ }
46
+ const wasAfterEquals = scan.afterEquals;
47
+ scan.afterEquals = false;
48
+ if (wasAfterEquals && takeValue(scan))
49
+ return;
50
+ const name = sticky(ATTR, code, at);
51
+ scan.emit(name === undefined ? "plain" : "attr", name ?? code.charAt(at));
52
+ scan.at += name?.length ?? 1;
53
+ }
54
+ /** The rest of a tag from just past its name; says where it ended and how. */
55
+ function scanTag(code, from, emit) {
56
+ const scan = { code, emit, at: from, afterEquals: false };
57
+ while (scan.at < code.length) {
58
+ const end = tagEnd(code, scan.at);
59
+ if (end !== undefined) {
60
+ emit("plain", end);
61
+ return { end: scan.at + end.length, closed: true, selfClosed: end === "/>" };
62
+ }
63
+ tagStep(scan);
64
+ }
65
+ return { end: code.length, closed: false, selfClosed: false };
66
+ }
67
+ /** A comment (which may span lines) or a doctype at `at`: where it ends and its colour. */
68
+ function special(code, at) {
69
+ if (code.startsWith("<!--", at)) {
70
+ const close = code.indexOf("-->", at + 4);
71
+ return { end: close === -1 ? code.length : close + 3, kind: "comment" };
72
+ }
73
+ const doctype = sticky(DOCTYPE, code, at);
74
+ return doctype === undefined ? undefined : { end: at + doctype.length, kind: "keyword" };
75
+ }
76
+ /** The `<script>` or `<style>` start tag whose content is another language, if this is one. */
77
+ function rawOf(name, tag, embedded) {
78
+ if (!tag.closed || tag.selfClosed)
79
+ return undefined;
80
+ if (name === "script")
81
+ return { name, scan: embedded.script };
82
+ return name === "style" ? { name, scan: embedded.style } : undefined;
83
+ }
84
+ /** The content of a `<script>` or `<style>`, up to the first closing tag of that name, wherever it falls. */
85
+ function rawContent(code, at, raw, emit) {
86
+ const closing = new RegExp(`</${raw.name}(?=[\\s/>]|$)`, "gi");
87
+ closing.lastIndex = at;
88
+ const end = closing.exec(code)?.index ?? code.length;
89
+ raw.scan(code.slice(at, end), emit);
90
+ return end;
91
+ }
92
+ /** A tag at a `<`, or a lone `<` that opens nothing. Returns the index to carry on from. */
93
+ function scanElement(code, at, emit, embedded) {
94
+ OPEN.lastIndex = at;
95
+ const open = OPEN.exec(code);
96
+ if (open === null) {
97
+ emit("plain", "<");
98
+ return at + 1;
99
+ }
100
+ const [whole, slash = "", tagName = ""] = open;
101
+ emit("plain", `<${slash}`);
102
+ emit("tag", tagName);
103
+ const tag = scanTag(code, at + whole.length, emit);
104
+ const raw = slash === "" ? rawOf(tagName.toLowerCase(), tag, embedded) : undefined;
105
+ return raw === undefined ? tag.end : rawContent(code, tag.end, raw, emit);
106
+ }
107
+ /** Markup at a `<`: a comment, a doctype or a tag. Returns the index to carry on from. */
108
+ function scanMarkup(code, at, emit, embedded) {
109
+ const found = special(code, at);
110
+ if (found === undefined)
111
+ return scanElement(code, at, emit, embedded);
112
+ emit(found.kind, code.slice(at, found.end));
113
+ return found.end;
114
+ }
115
+ /**
116
+ * HTML: comments, the doctype, tags with their attributes (a start tag may span lines), and text,
117
+ * which is plain. The content of `<script>` and `<style>` runs to the first closing tag of that
118
+ * name, wherever it falls, and is scanned with `script` (JS) or `style` (CSS).
119
+ */
120
+ export function scanHtml(code, emit, script, style) {
121
+ let at = 0;
122
+ while (at < code.length) {
123
+ if (code.charAt(at) === "<") {
124
+ at = scanMarkup(code, at, emit, { script, style });
125
+ continue;
126
+ }
127
+ const next = code.indexOf("<", at);
128
+ const end = next === -1 ? code.length : next;
129
+ emit("plain", code.slice(at, end));
130
+ at = end;
131
+ }
132
+ }
@@ -0,0 +1,16 @@
1
+ /** The pieces the scanners in `highlight*.ts` share. */
2
+ export type TokenKind = "plain" | "comment" | "keyword" | "string" | "number" | "tag" | "attr" | "function" | "prompt";
3
+ export type Token = {
4
+ text: string;
5
+ kind: TokenKind;
6
+ };
7
+ export type Emit = (kind: TokenKind, text: string) => void;
8
+ /** A scanner for a whole run of code, given to a scanner of a language that holds another. */
9
+ export type Scanner = (code: string, emit: Emit) => void;
10
+ /** The text a sticky `pattern` matches at `at`, or `undefined`. */
11
+ export declare function sticky(pattern: RegExp, code: string, at: number): string | undefined;
12
+ /** Each line of `code` without its line break, and the break (`\n`, `\r\n` or nothing at the end). */
13
+ export declare function splitLines(code: string): {
14
+ body: string;
15
+ eol: string;
16
+ }[];
@@ -0,0 +1,23 @@
1
+ /** The pieces the scanners in `highlight*.ts` share. */
2
+ /** The text a sticky `pattern` matches at `at`, or `undefined`. */
3
+ export function sticky(pattern, code, at) {
4
+ pattern.lastIndex = at;
5
+ return pattern.exec(code)?.[0];
6
+ }
7
+ /** Each line of `code` without its line break, and the break (`\n`, `\r\n` or nothing at the end). */
8
+ export function splitLines(code) {
9
+ const out = [];
10
+ let at = 0;
11
+ for (;;) {
12
+ const newline = code.indexOf("\n", at);
13
+ const end = newline === -1 ? code.length : newline;
14
+ const bodyEnd = end > at && code.charAt(end - 1) === "\r" ? end - 1 : end;
15
+ out.push({
16
+ body: code.slice(at, bodyEnd),
17
+ eol: code.slice(bodyEnd, newline === -1 ? end : end + 1),
18
+ });
19
+ if (newline === -1)
20
+ return out;
21
+ at = newline + 1;
22
+ }
23
+ }
@@ -0,0 +1,3 @@
1
+ import type { Emit } from "./highlightKit.js";
2
+ /** Line by line. The lines of a block scalar are one plain run: no comment or key is read in them. */
3
+ export declare function scanYaml(code: string, emit: Emit): void;
@@ -0,0 +1,143 @@
1
+ import { splitLines, sticky } from "./highlightKit.js";
2
+ const YAML_MARKER = /-(?: +|$)/y;
3
+ const YAML_KEYWORDS = /^(?:true|false|null|~)$/;
4
+ const YAML_NUMBER = /^-?\d[\d._]*$/;
5
+ const YAML_BLOCK_HEADER = /^[|>](?:[1-9][-+]?|[-+][1-9]?)?$/;
6
+ /**
7
+ * The index past the quoted scalar at `at` in a YAML line, or `undefined`. A single-quoted scalar
8
+ * escapes a quote by doubling it (`'it''s'`), a double-quoted one with a backslash.
9
+ */
10
+ function quotedEnd(line, at) {
11
+ const quote = line.charAt(at);
12
+ if (quote !== '"' && quote !== "'")
13
+ return undefined;
14
+ let i = at + 1;
15
+ while (i < line.length) {
16
+ const c = line.charAt(i);
17
+ if (quote === '"' && c === "\\")
18
+ i += 2;
19
+ else if (c !== quote)
20
+ i += 1;
21
+ else if (quote === "'" && line.charAt(i + 1) === "'")
22
+ i += 2;
23
+ else
24
+ return i + 1;
25
+ }
26
+ return line.length;
27
+ }
28
+ /** What follows a quoted scalar: spaces, then perhaps a comment. */
29
+ function trailing(rest, emit) {
30
+ const comment = /^(\s+)(#.*)$/.exec(rest);
31
+ if (comment === null)
32
+ emit("plain", rest);
33
+ else {
34
+ emit("plain", comment[1] ?? "");
35
+ emit("comment", comment[2] ?? "");
36
+ }
37
+ }
38
+ /** A quoted key: the closing quote must be followed by `:` and a space or the line end. */
39
+ function quotedKey(line, at) {
40
+ const end = quotedEnd(line, at);
41
+ if (end === undefined || end - at < 2 || line.charAt(end - 1) !== line.charAt(at))
42
+ return undefined;
43
+ return /^:(?:\s|$)/.test(line.slice(end)) ? { end, kind: "string" } : undefined;
44
+ }
45
+ /** An unquoted key: the text up to the first `:` that is followed by a space or the line end. */
46
+ function plainKey(line, at) {
47
+ if ("#[{|>".includes(line.charAt(at)))
48
+ return undefined;
49
+ const colon = /:(?:\s|$)/.exec(line.slice(at));
50
+ if (colon === null || colon.index === 0)
51
+ return undefined;
52
+ const text = line.slice(at, at + colon.index);
53
+ if (/(?:^|\s)#/.test(text))
54
+ return undefined;
55
+ return { end: at + text.trimEnd().length, kind: "attr" };
56
+ }
57
+ /** The mapping key at `at`: where its text ends (before the `:`) and its colour. */
58
+ function keyAt(line, at) {
59
+ const first = line.charAt(at);
60
+ if (first === "")
61
+ return undefined;
62
+ return first === '"' || first === "'" ? quotedKey(line, at) : plainKey(line, at);
63
+ }
64
+ /** The colour of an unquoted scalar read whole. */
65
+ function scalarKind(scalar) {
66
+ if (YAML_NUMBER.test(scalar))
67
+ return "number";
68
+ return YAML_KEYWORDS.test(scalar) ? "keyword" : "plain";
69
+ }
70
+ /** A value: a quoted string, or an unquoted scalar up to a comment. True when it opens a block scalar. */
71
+ function emitValue(line, from, emit) {
72
+ const value = line.slice(from);
73
+ if (value === "")
74
+ return false;
75
+ if (value.startsWith("#")) {
76
+ emit("comment", value);
77
+ return false;
78
+ }
79
+ const quoted = quotedEnd(line, from);
80
+ if (quoted !== undefined) {
81
+ emit("string", line.slice(from, quoted));
82
+ trailing(line.slice(quoted), emit);
83
+ return false;
84
+ }
85
+ const comment = /\s#/.exec(value);
86
+ const scalar = line.slice(from, comment === null ? line.length : from + comment.index).trimEnd();
87
+ emit(scalarKind(scalar), scalar);
88
+ const commentAt = comment === null ? line.length : from + comment.index + 1;
89
+ emit("plain", line.slice(from + scalar.length, commentAt));
90
+ emit("comment", line.slice(commentAt));
91
+ return YAML_BLOCK_HEADER.test(scalar);
92
+ }
93
+ /** The `- ` markers from `at`: where the content starts, and the column of the last dash. */
94
+ function emitMarkers(line, at, emit) {
95
+ let column = at;
96
+ let next = at;
97
+ for (;;) {
98
+ const marker = sticky(YAML_MARKER, line, next);
99
+ if (marker === undefined)
100
+ return { at: next, column };
101
+ column = next;
102
+ emit("plain", marker);
103
+ next += marker.length;
104
+ }
105
+ }
106
+ /**
107
+ * One YAML line. Returns the column its block scalar's lines must be indented past, when it ends
108
+ * in `|` or `>`: the key's own column, after any `- ` markers (a key-less item uses its dash's).
109
+ */
110
+ function scanLine(line, emit) {
111
+ const indent = line.length - line.trimStart().length;
112
+ emit("plain", line.slice(0, indent));
113
+ if (line.charAt(indent) === "#") {
114
+ emit("comment", line.slice(indent));
115
+ return undefined;
116
+ }
117
+ const markers = emitMarkers(line, indent, emit);
118
+ let { at, column } = markers;
119
+ const key = keyAt(line, at);
120
+ if (key !== undefined) {
121
+ column = at;
122
+ emit(key.kind, line.slice(at, key.end));
123
+ const colon = line.indexOf(":", key.end);
124
+ emit("plain", line.slice(key.end, colon + 1));
125
+ at = colon + 1;
126
+ }
127
+ const gap = line.length - at - line.slice(at).trimStart().length;
128
+ emit("plain", line.slice(at, at + gap));
129
+ return emitValue(line, at + gap, emit) ? column : undefined;
130
+ }
131
+ /** Line by line. The lines of a block scalar are one plain run: no comment or key is read in them. */
132
+ export function scanYaml(code, emit) {
133
+ let block;
134
+ for (const { body, eol } of splitLines(code)) {
135
+ const indent = body.length - body.trimStart().length;
136
+ if (block !== undefined && (body.trim() === "" || indent > block)) {
137
+ emit("plain", body + eol);
138
+ continue;
139
+ }
140
+ block = scanLine(body, emit);
141
+ emit("plain", eol);
142
+ }
143
+ }
package/dist/icons.d.ts CHANGED
@@ -4,10 +4,10 @@ import type { ReactElement } from "react";
4
4
  * two units of padding (its ink lives in 2..22), is drawn as an outline with a 1.5 stroke, round
5
5
  * caps and round joins, and never fills: `GLYPH` below is the only place those are set, so an
6
6
  * icon's own shapes carry no `fill`, `stroke` or `stroke-width` (`Icon.test.tsx` holds them to
7
- * that). A name is the idea the icon stands for, not a product.
7
+ * that). A name is the idea or action the icon stands for, not a product.
8
8
  */
9
- declare const NAMES: readonly ["diff", "baseline", "review", "ci", "self-host", "privacy", "capture", "branch", "theme", "speed"];
10
- /** The names of the icon set, one per idea. */
9
+ declare const NAMES: readonly ["diff", "baseline", "review", "ci", "self-host", "privacy", "capture", "branch", "theme", "speed", "search", "chevron-right", "chevron-down", "close", "external-link", "settings", "bell", "check", "plus"];
10
+ /** The names of the icon set: feature ideas, then UI glyphs. */
11
11
  export type IconName = (typeof NAMES)[number];
12
12
  /** The icon `name` as an `<svg>`: the set's one grid, stroke, caps and joins, drawn in `currentColor`. */
13
13
  export declare function iconGlyph(name: IconName): ReactElement;
package/dist/icons.js CHANGED
@@ -4,7 +4,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
4
4
  * two units of padding (its ink lives in 2..22), is drawn as an outline with a 1.5 stroke, round
5
5
  * caps and round joins, and never fills: `GLYPH` below is the only place those are set, so an
6
6
  * icon's own shapes carry no `fill`, `stroke` or `stroke-width` (`Icon.test.tsx` holds them to
7
- * that). A name is the idea the icon stands for, not a product.
7
+ * that). A name is the idea or action the icon stands for, not a product.
8
8
  */
9
9
  const NAMES = [
10
10
  "diff",
@@ -17,6 +17,15 @@ const NAMES = [
17
17
  "branch",
18
18
  "theme",
19
19
  "speed",
20
+ "search",
21
+ "chevron-right",
22
+ "chevron-down",
23
+ "close",
24
+ "external-link",
25
+ "settings",
26
+ "bell",
27
+ "check",
28
+ "plus",
20
29
  ];
21
30
  const GLYPHS = {
22
31
  /** Before and after, side by side: a minus on the left, a plus on the right. */
@@ -39,6 +48,24 @@ const GLYPHS = {
39
48
  theme: (_jsx("path", { d: "M12 3.5S5.75 9.9 5.75 14.25a6.25 6.25 0 0 0 12.5 0C18.25 9.9 12 3.5 12 3.5ZM9 14.5a3 3 0 0 0 3 3" })),
40
49
  /** Fast: a bolt. */
41
50
  speed: _jsx("path", { d: "M13.25 3 5 13.5h6.25L10.75 21 19 10.5h-6.25Z" }),
51
+ /** Finding something: a magnifying glass, a circle with its handle to the lower right. */
52
+ search: (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "10.5", cy: "10.5", r: "6.5" }), _jsx("path", { d: "M15.5 15.5 20.5 20.5" })] })),
53
+ /** A link or next step: an open chevron pointing right. */
54
+ "chevron-right": _jsx("path", { d: "m9 5.5 6.5 6.5L9 18.5" }),
55
+ /** A disclosure or menu: an open chevron pointing down. */
56
+ "chevron-down": _jsx("path", { d: "m5.5 9 6.5 6.5L18.5 9" }),
57
+ /** Dismiss: two strokes crossing in an X. */
58
+ close: _jsx("path", { d: "m5.5 5.5 13 13M18.5 5.5l-13 13" }),
59
+ /** Leaves the site: a box open at its top right, with an arrow out to the top right. */
60
+ "external-link": (_jsxs(_Fragment, { children: [_jsx("path", { d: "M10.5 6H7a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h8a3 3 0 0 0 3-3v-3.5" }), _jsx("path", { d: "M14 4h6v6M20 4l-9.5 9.5" })] })),
61
+ /** Preferences: a cog, a ring of teeth round a hole. */
62
+ settings: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M10.59 5.35L10.75 3.09L13.25 3.09L13.41 5.35A6.8 6.8 0 0 1 15.7 6.3L17.42 4.81L19.19 6.58L17.7 8.3A6.8 6.8 0 0 1 18.65 10.59L20.91 10.75L20.91 13.25L18.65 13.41A6.8 6.8 0 0 1 17.7 15.7L19.19 17.42L17.42 19.19L15.7 17.7A6.8 6.8 0 0 1 13.41 18.65L13.25 20.91L10.75 20.91L10.59 18.65A6.8 6.8 0 0 1 8.3 17.7L6.58 19.19L4.81 17.42L6.3 15.7A6.8 6.8 0 0 1 5.35 13.41L3.09 13.25L3.09 10.75L5.35 10.59A6.8 6.8 0 0 1 6.3 8.3L4.81 6.58L6.58 4.81L8.3 6.3A6.8 6.8 0 0 1 10.59 5.35Z" }), _jsx("circle", { cx: "12", cy: "12", r: "2.75" })] })),
63
+ /** Notifications: a bell with its clapper. */
64
+ bell: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M6 9.5a6 6 0 0 1 12 0c0 5 2 6.5 2 6.5H4s2-1.5 2-6.5Z" }), _jsx("path", { d: "M10 19.5a2.2 2.2 0 0 0 4 0" })] })),
65
+ /** Done or selected: a single tick. */
66
+ check: _jsx("path", { d: "m4.5 12.5 5 5L19.5 7" }),
67
+ /** Add or create: two strokes crossing, upright. */
68
+ plus: _jsx("path", { d: "M12 5v14M5 12h14" }),
42
69
  };
43
70
  /** The icon `name` as an `<svg>`: the set's one grid, stroke, caps and joins, drawn in `currentColor`. */
44
71
  export function iconGlyph(name) {
package/dist/index.d.ts CHANGED
@@ -33,6 +33,9 @@ export { TableEditCell } from "./TableEditCell.js";
33
33
  export { EmptyState } from "./EmptyState.js";
34
34
  export { Step, Steps } from "./Steps.js";
35
35
  export { Timeline, TimelineItem } from "./Timeline.js";
36
+ export { ChatComposer } from "./ChatComposer.js";
37
+ export { ChatMessage } from "./ChatMessage.js";
38
+ export { ChatThread } from "./ChatThread.js";
36
39
  export { Code } from "./Code.js";
37
40
  export { CodeBlock } from "./CodeBlock.js";
38
41
  export type { CodeLanguage } from "./highlight.js";
@@ -73,7 +76,8 @@ export { Image, type ImageAspect, type ImageProps, type ImageWidth } from "./Ima
73
76
  export { MediaTile, type MediaTileProps } from "./MediaTile.js";
74
77
  export { ScrollStrip, type ScrollStripProps } from "./ScrollStrip.js";
75
78
  export { TextLink, type TextLinkSize } from "./TextLink.js";
76
- export { Caption, Display, Lead, Overline, PageHeading, SectionHeading, SubsectionHeading, type SubsectionHeadingSize, Text, type TextTone, type TypographyColor, type TypographyProps, } from "./typography.js";
79
+ export { ToolCallCard } from "./ToolCallCard.js";
80
+ export { Caption, Display, Lead, Overline, PageHeading, Prose, SectionHeading, SubsectionHeading, type SubsectionHeadingSize, Text, type TextTone, type TypographyColor, type TypographyProps, } from "./typography.js";
77
81
  export { DateField, type DateFieldProps } from "./DateField.js";
78
82
  export { fromISODate, toISODate } from "./isoDate.js";
79
83
  export type { CalendarDate } from "@internationalized/date";
package/dist/index.js CHANGED
@@ -33,6 +33,9 @@ export { TableEditCell } from "./TableEditCell.js";
33
33
  export { EmptyState } from "./EmptyState.js";
34
34
  export { Step, Steps } from "./Steps.js";
35
35
  export { Timeline, TimelineItem } from "./Timeline.js";
36
+ export { ChatComposer } from "./ChatComposer.js";
37
+ export { ChatMessage } from "./ChatMessage.js";
38
+ export { ChatThread } from "./ChatThread.js";
36
39
  export { Code } from "./Code.js";
37
40
  export { CodeBlock } from "./CodeBlock.js";
38
41
  export { CodeExample } from "./CodeExample.js";
@@ -69,6 +72,7 @@ export { Image } from "./Image.js";
69
72
  export { MediaTile } from "./MediaTile.js";
70
73
  export { ScrollStrip } from "./ScrollStrip.js";
71
74
  export { TextLink } from "./TextLink.js";
72
- export { Caption, Display, Lead, Overline, PageHeading, SectionHeading, SubsectionHeading, Text, } from "./typography.js";
75
+ export { ToolCallCard } from "./ToolCallCard.js";
76
+ export { Caption, Display, Lead, Overline, PageHeading, Prose, SectionHeading, SubsectionHeading, Text, } from "./typography.js";
73
77
  export { DateField } from "./DateField.js";
74
78
  export { fromISODate, toISODate } from "./isoDate.js";