@ciridae/ui 1.0.2 → 1.0.4

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.
Files changed (209) hide show
  1. package/dist/app/auth.d.ts +10 -0
  2. package/dist/app/auth.js +60 -0
  3. package/dist/app/error-boundary.d.ts +18 -0
  4. package/dist/app/error-boundary.js +45 -0
  5. package/dist/app/feedback.d.ts +12 -0
  6. package/dist/app/feedback.js +128 -0
  7. package/dist/app/index.d.ts +8 -0
  8. package/dist/app/index.js +8 -0
  9. package/dist/app/request-errors.d.ts +63 -0
  10. package/dist/app/request-errors.js +172 -0
  11. package/dist/app/sentry.d.ts +18 -0
  12. package/dist/app/sentry.js +52 -0
  13. package/dist/app/telemetry.d.ts +7 -0
  14. package/dist/app/telemetry.js +28 -0
  15. package/dist/app/trail.d.ts +9 -0
  16. package/dist/app/trail.js +18 -0
  17. package/dist/chat/chat-query-keys.d.ts +3 -3
  18. package/dist/chat/contract.d.ts +15 -15
  19. package/dist/chat/conversation-items-query-options.d.ts +114 -114
  20. package/dist/chat/conversation-source.d.ts +89 -0
  21. package/dist/chat/conversation-source.js +1 -0
  22. package/dist/chat/describe-tool-call.d.ts +3 -3
  23. package/dist/chat/describe-tool-call.js +3 -3
  24. package/dist/chat/message-text.d.ts +1 -1
  25. package/dist/chat/message-text.js +116 -48
  26. package/dist/chat/use-conversation-list.d.ts +6 -6
  27. package/dist/ciridae.css +16 -4
  28. package/dist/components/activity-feed.d.ts +3 -2
  29. package/dist/components/activity-feed.js +1 -0
  30. package/dist/components/agent-work.d.ts +3 -3
  31. package/dist/components/app-frame.d.ts +91 -0
  32. package/dist/components/app-frame.js +134 -0
  33. package/dist/components/app-navigation.d.ts +30 -3
  34. package/dist/components/app-navigation.js +105 -14
  35. package/dist/components/badge.d.ts +30 -5
  36. package/dist/components/badge.js +134 -15
  37. package/dist/components/brand-icons.d.ts +3 -3
  38. package/dist/components/bulk-action-bar.d.ts +1 -1
  39. package/dist/components/bulk-action-bar.js +3 -3
  40. package/dist/components/button.d.ts +12 -5
  41. package/dist/components/button.js +77 -44
  42. package/dist/components/calendar.d.ts +1 -1
  43. package/dist/components/calendar.js +44 -17
  44. package/dist/components/centered-page.d.ts +1 -1
  45. package/dist/components/centered-page.js +2 -2
  46. package/dist/components/chart.d.ts +22 -2
  47. package/dist/components/chart.js +251 -51
  48. package/dist/components/chat-client-source.d.ts +16 -0
  49. package/dist/components/chat-client-source.js +297 -0
  50. package/dist/components/chat-page.d.ts +9 -8
  51. package/dist/components/chat-page.js +7 -11
  52. package/dist/components/chat-sheet-controller.d.ts +11 -14
  53. package/dist/components/chat-sheet-controller.js +3 -3
  54. package/dist/components/chat-sheet.d.ts +18 -11
  55. package/dist/components/chat-sheet.js +188 -289
  56. package/dist/components/chat.d.ts +109 -19
  57. package/dist/components/chat.js +520 -124
  58. package/dist/components/checkbox-item.d.ts +6 -3
  59. package/dist/components/checkbox-item.js +9 -5
  60. package/dist/components/checkbox-list.d.ts +1 -1
  61. package/dist/components/chip.d.ts +6 -1
  62. package/dist/components/chip.js +27 -1
  63. package/dist/components/collapsible.d.ts +1 -1
  64. package/dist/components/color-theme.d.ts +1 -1
  65. package/dist/components/column-settings.d.ts +1 -1
  66. package/dist/components/column-settings.js +1 -1
  67. package/dist/components/command-menu.d.ts +5 -1
  68. package/dist/components/command-menu.js +98 -32
  69. package/dist/components/command-palette.d.ts +7 -3
  70. package/dist/components/command-palette.js +20 -11
  71. package/dist/components/copyable-id.d.ts +22 -5
  72. package/dist/components/copyable-id.js +46 -6
  73. package/dist/components/data-display.d.ts +39 -8
  74. package/dist/components/data-display.js +138 -19
  75. package/dist/components/data-grid.d.ts +23 -11
  76. package/dist/components/data-grid.js +61 -26
  77. package/dist/components/date-range-picker.d.ts +1 -1
  78. package/dist/components/date-range-picker.js +9 -20
  79. package/dist/components/date-time.d.ts +1 -1
  80. package/dist/components/dialog-height-context.d.ts +1 -1
  81. package/dist/components/document-viewer.d.ts +2 -2
  82. package/dist/components/drawer.d.ts +11 -6
  83. package/dist/components/drawer.js +91 -54
  84. package/dist/components/dropzone.d.ts +4 -4
  85. package/dist/components/editable-field.d.ts +12 -3
  86. package/dist/components/editable-field.js +57 -27
  87. package/dist/components/email-input.d.ts +1 -1
  88. package/dist/components/email-input.js +6 -5
  89. package/dist/components/environment-frame.d.ts +2 -2
  90. package/dist/components/environment-frame.js +37 -6
  91. package/dist/components/expandable-text.d.ts +1 -1
  92. package/dist/components/feedback-screenshot.d.ts +1 -1
  93. package/dist/components/feedback-sheet.d.ts +18 -3
  94. package/dist/components/feedback-sheet.js +216 -55
  95. package/dist/components/field-context.d.ts +1 -1
  96. package/dist/components/filter-bar.d.ts +3 -3
  97. package/dist/components/filter-bar.js +43 -29
  98. package/dist/components/form-controls.d.ts +12 -3
  99. package/dist/components/form-controls.js +46 -22
  100. package/dist/components/form-layout.d.ts +14 -10
  101. package/dist/components/form-layout.js +68 -23
  102. package/dist/components/glass-menu.d.ts +3 -0
  103. package/dist/components/glass-menu.js +63 -0
  104. package/dist/components/gradient-edge.d.ts +9 -0
  105. package/dist/components/gradient-edge.js +31 -0
  106. package/dist/components/hotkeys.d.ts +2 -2
  107. package/dist/components/icon.d.ts +1 -1
  108. package/dist/components/input.d.ts +7 -5
  109. package/dist/components/input.js +29 -17
  110. package/dist/components/keyboard-shortcuts.d.ts +2 -2
  111. package/dist/components/keycap.d.ts +10 -6
  112. package/dist/components/keycap.js +57 -19
  113. package/dist/components/layout.d.ts +13 -13
  114. package/dist/components/lightbox.d.ts +9 -3
  115. package/dist/components/lightbox.js +13 -9
  116. package/dist/components/link.d.ts +1 -1
  117. package/dist/components/locale.d.ts +42 -2
  118. package/dist/components/locale.js +43 -3
  119. package/dist/components/log-entry.d.ts +40 -0
  120. package/dist/components/log-entry.js +146 -0
  121. package/dist/components/log-list.d.ts +3 -3
  122. package/dist/components/mention-input.d.ts +2 -2
  123. package/dist/components/mention-input.js +4 -3
  124. package/dist/components/menu.d.ts +7 -3
  125. package/dist/components/menu.js +34 -37
  126. package/dist/components/meter.d.ts +10 -2
  127. package/dist/components/meter.js +64 -6
  128. package/dist/components/navigation.d.ts +21 -4
  129. package/dist/components/navigation.js +84 -19
  130. package/dist/components/notification-tray.d.ts +1 -1
  131. package/dist/components/notification-tray.js +5 -4
  132. package/dist/components/overlays.d.ts +6 -4
  133. package/dist/components/overlays.js +69 -40
  134. package/dist/components/page-frame.d.ts +4 -4
  135. package/dist/components/page-frame.js +1 -1
  136. package/dist/components/panel.d.ts +4 -4
  137. package/dist/components/panel.js +18 -12
  138. package/dist/components/profile-header.d.ts +1 -1
  139. package/dist/components/progress.d.ts +1 -1
  140. package/dist/components/project-avatar.d.ts +1 -1
  141. package/dist/components/radio-card.d.ts +2 -2
  142. package/dist/components/radio-card.js +10 -9
  143. package/dist/components/record.d.ts +29 -5
  144. package/dist/components/record.js +147 -18
  145. package/dist/components/resolved-file-url.d.ts +19 -0
  146. package/dist/components/resolved-file-url.js +49 -0
  147. package/dist/components/scroll-area.d.ts +1 -1
  148. package/dist/components/secret-dialog.d.ts +1 -1
  149. package/dist/components/segmented-bar.d.ts +4 -2
  150. package/dist/components/segmented-bar.js +16 -6
  151. package/dist/components/select.d.ts +9 -6
  152. package/dist/components/select.js +103 -127
  153. package/dist/components/settings.d.ts +4 -4
  154. package/dist/components/shell-skeleton.d.ts +1 -1
  155. package/dist/components/shell-skeleton.js +1 -1
  156. package/dist/components/shell.d.ts +21 -21
  157. package/dist/components/shell.js +43 -43
  158. package/dist/components/side-panel.d.ts +3 -3
  159. package/dist/components/sidebar.d.ts +13 -8
  160. package/dist/components/sidebar.js +55 -27
  161. package/dist/components/single-line-text.d.ts +2 -0
  162. package/dist/components/single-line-text.js +16 -0
  163. package/dist/components/site-banner.d.ts +5 -2
  164. package/dist/components/site-banner.js +17 -6
  165. package/dist/components/skeleton.d.ts +8 -7
  166. package/dist/components/skeleton.js +28 -16
  167. package/dist/components/skeletons.d.ts +17 -5
  168. package/dist/components/skeletons.js +71 -8
  169. package/dist/components/stages.d.ts +83 -11
  170. package/dist/components/stages.js +512 -156
  171. package/dist/components/stat-card.d.ts +12 -4
  172. package/dist/components/stat-card.js +92 -37
  173. package/dist/components/status-banner.d.ts +1 -1
  174. package/dist/components/status-banner.js +1 -1
  175. package/dist/components/status.d.ts +2 -2
  176. package/dist/components/steps.d.ts +1 -1
  177. package/dist/components/switch.d.ts +1 -1
  178. package/dist/components/tabs.d.ts +8 -8
  179. package/dist/components/tabs.js +26 -13
  180. package/dist/components/theme-customizer.d.ts +2 -2
  181. package/dist/components/theme-toggle.d.ts +4 -0
  182. package/dist/components/theme-toggle.js +67 -0
  183. package/dist/components/toast.d.ts +10 -3
  184. package/dist/components/toast.js +78 -29
  185. package/dist/components/use-new-version-available.d.ts +6 -0
  186. package/dist/components/use-new-version-available.js +29 -0
  187. package/dist/components/user-chip.d.ts +3 -3
  188. package/dist/components/wizard.d.ts +2 -2
  189. package/dist/feedback/contract.d.ts +136 -136
  190. package/dist/feedback/feedback-controller.d.ts +6 -6
  191. package/dist/feedback/generated/zod.gen.d.ts +28 -28
  192. package/dist/feedback/operator-contract.d.ts +11 -11
  193. package/dist/feedback/operator-controller.d.ts +32 -13
  194. package/dist/feedback/operator-controller.js +44 -11
  195. package/dist/preset.js +289 -33
  196. package/dist/styled-system/styles.css +3294 -1079
  197. package/dist/styled-system/tokens/index.mjs +598 -22
  198. package/dist/styled-system/tokens/tokens.d.ts +11 -11
  199. package/dist/styled-system/types/prop-type.d.ts +2 -2
  200. package/dist/styles.css +16 -4
  201. package/dist/theme/color.d.ts +3 -0
  202. package/dist/theme/color.js +5 -0
  203. package/dist/theme/derive.d.ts +9 -2
  204. package/dist/theme/derive.js +72 -12
  205. package/dist/theme/palettes.d.ts +49 -6
  206. package/dist/theme/palettes.js +84 -15
  207. package/dist/theme/tokens.d.ts +3 -3
  208. package/dist/theme/tokens.js +107 -32
  209. package/package.json +40 -3
@@ -0,0 +1,89 @@
1
+ import type { ChatAttachment, ChatSource, ChatStep } from "../components/chat.js";
2
+ import type { ResolveFileUrl } from "../components/resolved-file-url.js";
3
+ import type { ReactNode } from "react";
4
+ /**
5
+ * How the conversation's latest turn stands: `running` while the agent works on it, `failed` or
6
+ * `stopped` once it ended that way, `idle` otherwise, a turn waiting on the user included. The
7
+ * lib draws the ending of the latest turn, so the source leaves it out of `items`.
8
+ */
9
+ export type ChatTurnStatus = "idle" | "running" | "failed" | "stopped";
10
+ /** One entry of the conversation, oldest first. `at` is epoch milliseconds. */
11
+ export type ChatConversationItem = {
12
+ id: string;
13
+ at: number;
14
+ } & ({
15
+ kind: "message";
16
+ role: "user" | "assistant";
17
+ /**
18
+ * The user's text as typed, mentions as `[@label](mention://kind/id)`; the assistant's as
19
+ * Markdown, its link reference definitions drawn as sources under it.
20
+ */
21
+ text: string;
22
+ attachments?: readonly ChatAttachment[];
23
+ /** The service has not taken the user's message yet. */
24
+ sending?: boolean;
25
+ /** The user's message did not send: why, and how to send it again. */
26
+ failure?: {
27
+ message?: string;
28
+ retry: () => void;
29
+ };
30
+ /** Sources the reply cites beyond its Markdown's, such as the values it read, as cards. */
31
+ sources?: readonly ChatSource[];
32
+ /** Buttons under the reply for what it suggests doing on the page. */
33
+ suggestedActions?: ReactNode;
34
+ /** A closing line under the reply, such as what it read. */
35
+ footnote?: ReactNode;
36
+ }
37
+ /**
38
+ * A step the agent took on its way to a reply. The thread folds a run of them into one line,
39
+ * and draws the newest one of the running turn as what the agent is doing.
40
+ */
41
+ | {
42
+ kind: "step";
43
+ step: Omit<ChatStep, "id">;
44
+ }
45
+ /** A line about the conversation, such as how an earlier turn ended. */
46
+ | {
47
+ kind: "line";
48
+ text: ReactNode;
49
+ tone?: "muted" | "error";
50
+ }
51
+ /** Something of the source's own in the agent's flow, such as an approval it waits on. */
52
+ | {
53
+ kind: "card";
54
+ content: ReactNode;
55
+ });
56
+ /** A message the composer sends: its text and the files `upload` returned for it. */
57
+ export type ChatOutgoingMessage = {
58
+ text: string;
59
+ attachments: readonly ChatAttachment[];
60
+ };
61
+ export type ChatConversationSource = {
62
+ items: readonly ChatConversationItem[];
63
+ status: ChatTurnStatus;
64
+ /** The conversation is loading: its items are not there yet. */
65
+ loading?: boolean;
66
+ /** Why the latest turn failed, on its failure; the locale's "Failed" otherwise. */
67
+ failureMessage?: string;
68
+ /** Runs the failed latest turn again, as Retry on its failure. */
69
+ retry?: () => void;
70
+ /**
71
+ * Sends a message, creating the conversation with it when there is none yet. The composer
72
+ * clears as it sends; a rejection gives the text back and shows the error over it.
73
+ */
74
+ send: (message: ChatOutgoingMessage) => Promise<void>;
75
+ /** Uploads a file to go with the next message; without it, the composer takes no files. */
76
+ upload?: (file: File) => Promise<ChatAttachment>;
77
+ /** Loads the link of an attachment with no `url`; see `ResolveFileUrl`. */
78
+ resolveAttachmentUrl?: ResolveFileUrl<ChatAttachment>;
79
+ };
80
+ /** A conversation in `ChatHistory`. `updatedAt` is epoch milliseconds. */
81
+ export type ChatConversationSummary = {
82
+ id: string;
83
+ title: string | null;
84
+ /** The latest message as one line, in place of a missing title. */
85
+ preview?: string | null;
86
+ updatedAt: number;
87
+ /** The agent is working in it: a spinner in place of its time. */
88
+ running?: boolean;
89
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * What a tool call waiting for approval asks to do, read from a tool name that starts with its
3
- * verb: `search_invoices` is "Search invoices", and a one-word noun like `webfetch` "Use webfetch".
2
+ * What a tool call does, read from a tool name that starts with its verb: `search_invoices` is
3
+ * "Search invoices", and a one-word noun like `webfetch` "Use webfetch".
4
4
  */
5
- export declare function describeApprovalRequest(toolName: string): string;
5
+ export declare function describeToolCall(toolName: string): string;
@@ -12,10 +12,10 @@ const singleWordVerbs = new Set([
12
12
  "write",
13
13
  ]);
14
14
  /**
15
- * What a tool call waiting for approval asks to do, read from a tool name that starts with its
16
- * verb: `search_invoices` is "Search invoices", and a one-word noun like `webfetch` "Use webfetch".
15
+ * What a tool call does, read from a tool name that starts with its verb: `search_invoices` is
16
+ * "Search invoices", and a one-word noun like `webfetch` "Use webfetch".
17
17
  */
18
- export function describeApprovalRequest(toolName) {
18
+ export function describeToolCall(toolName) {
19
19
  const [verb = toolName, ...rest] = splitIdentifierWords(toolName).map((word) => word.toLowerCase());
20
20
  const object = rest.join(" ");
21
21
  if (!object && !singleWordVerbs.has(verb))
@@ -20,4 +20,4 @@ export type MessageTextProps = {
20
20
  plain?: boolean;
21
21
  className?: string;
22
22
  };
23
- export declare function MessageText({ content, onMentionSelect, mentionHref, plain, className, }: MessageTextProps): import("@types/react").JSX.Element;
23
+ export declare function MessageText({ content, onMentionSelect, mentionHref, plain, className, }: MessageTextProps): import("react").JSX.Element;
@@ -1,23 +1,57 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Fragment } from "react";
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Markdown } from "@tanstack/markdown/react";
3
+ import { createContext, useContext, useMemo } from "react";
3
4
  import { css, cx } from "#primitives/css";
4
5
  import { parseMentions } from "./contract.js";
5
- /*
6
- * A reply's text as the agent writes it: paragraphs split by blank lines, `- ` lists, **bold**,
7
- * `code`, [links](url), and mentions as chips. Enough for the agent sheet's short answers without
8
- * a Markdown dependency; a full renderer can replace this when replies carry more than that.
9
- * Markdown link reference definitions (`[1]: url "Title"`) draw nothing in place; `splitSources`
10
- * lifts them out as the reply's sources.
6
+ /* TanStack owns Markdown parsing/rendering; the adapter adds our mention chips and bare URLs.
7
+ * Raw HTML stays text. Source definitions are lifted out separately for the reply's source row.
11
8
  */
12
- // Sized by the bubble it sits in.
9
+ // Sized by the bubble it sits in; its paragraphs, lists and tables 12px apart.
13
10
  const text = css({
14
11
  display: "grid",
15
- gap: "control.iconGap",
12
+ gap: "layout.contentGap",
16
13
  minWidth: "0",
17
14
  overflowWrap: "anywhere",
18
- "& p, & ul": { margin: "none" },
19
- "& ul": { paddingInlineStart: "layout.clusterGap" },
15
+ "& :is(p, ul, ol, pre, table, blockquote, h1, h2, h3, h4, h5, h6, hr)": { margin: "none" },
16
+ "& p": { whiteSpace: "pre-line" },
17
+ "& :is(h1, h2, h3, h4, h5, h6)": { fontSize: "inherit", fontWeight: "semibold" },
18
+ "& :is(ul, ol)": { paddingInlineStart: "layout.clusterGap" },
19
+ "& li > :is(ul, ol)": { marginBlockStart: "control.trackInset" },
20
+ "& li + li": { marginBlockStart: "control.trackInset" },
20
21
  "& code": { fontFamily: "mono", fontSize: "code" },
22
+ "& pre": {
23
+ overflowX: "auto",
24
+ paddingBlock: "control.compactBlock",
25
+ paddingInline: "control.compactInline",
26
+ borderRadius: "keycap",
27
+ background: "bg.raised",
28
+ whiteSpace: "pre",
29
+ overflowWrap: "normal",
30
+ },
31
+ "& blockquote": {
32
+ display: "grid",
33
+ gap: "control.iconGap",
34
+ paddingInlineStart: "control.compactInline",
35
+ borderInlineStart: "2px solid token(colors.border.control)",
36
+ color: "fg.muted",
37
+ },
38
+ "& hr": { border: "0", borderTop: "1px solid token(colors.border.control)" },
39
+ "& table": {
40
+ display: "block",
41
+ maxWidth: "100%",
42
+ overflowX: "auto",
43
+ borderCollapse: "collapse",
44
+ fontVariantNumeric: "tabular-nums",
45
+ },
46
+ "& :is(th, td)": {
47
+ paddingBlock: "control.trackInset",
48
+ paddingInline: "control.compactBlock",
49
+ borderBottom: "1px solid token(colors.border.control)",
50
+ textAlign: "start",
51
+ verticalAlign: "top",
52
+ overflowWrap: "normal",
53
+ },
54
+ "& th": { fontWeight: "medium" },
21
55
  "& a:not([data-mention])": { color: "inherit", textDecoration: "underline" },
22
56
  });
23
57
  const mention = css({
@@ -37,16 +71,18 @@ const mention = css({
37
71
  cursor: "default",
38
72
  "&:is(button, a)": { cursor: "pointer", _hover: { background: "bg.raisedHover" } },
39
73
  });
40
- const inlinePattern = /(\*\*[^*]+\*\*|`[^`]+`|\[[^[\]@][^[\]]*\]\([^)\s]+\))/;
41
- const linkPattern = /^\[([^\]]+)\]\(([^)\s]+)\)$/;
42
74
  const sourcePattern = /^\s*\[([^\]]+)\]:\s*(\S+)(?:\s+"([^"]*)")?\s*$/;
75
+ const fencePattern = /^\s*(?:`{3,}|~{3,})/;
43
76
  /** The reply's text without its link reference definitions, and those definitions as sources. */
44
77
  export function splitSources(content) {
45
78
  const sources = [];
79
+ let inFence = false;
46
80
  const body = content
47
81
  .split("\n")
48
82
  .filter((line) => {
49
- const match = sourcePattern.exec(line);
83
+ if (fencePattern.test(line))
84
+ inFence = !inFence;
85
+ const match = inFence ? null : sourcePattern.exec(line);
50
86
  if (!match)
51
87
  return true;
52
88
  const [, id = "", href = "", title] = match;
@@ -71,48 +107,80 @@ const plainText = css({
71
107
  _hover: { background: "transparent", textDecoration: "underline" },
72
108
  },
73
109
  });
110
+ const MentionContext = createContext({});
74
111
  export function MessageText({ content, onMentionSelect, mentionHref, plain = false, className, }) {
112
+ const handlers = useMemo(() => ({ onMentionSelect, mentionHref }), [onMentionSelect, mentionHref]);
75
113
  if (plain) {
76
- return (_jsx("div", { className: cx(text, plainText, className), "data-ciridae-ui": "", children: parseMentions(content).map((part, index) => typeof part === "string" ? part : renderMention(part, index, onMentionSelect, mentionHref)) }));
114
+ return (_jsx("div", { className: cx(text, plainText, className), "data-ciridae-ui": "", children: parseMentions(content).map((part, index) => typeof part === "string" ? part : renderMention(part, index, handlers)) }));
77
115
  }
78
- const blocks = splitSources(content)
79
- .body.split(/\n\s*\n/)
80
- .filter((block) => block.trim().length > 0);
81
- const inline = (line) => renderInline(line, onMentionSelect, mentionHref);
82
- return (_jsx("div", { className: cx(text, className), "data-ciridae-ui": "", children: blocks.map((block, blockIndex) => {
83
- const lines = block.split("\n");
84
- if (lines.every((line) => /^\s*[-*] /.test(line))) {
85
- return (_jsx("ul", { children: lines.map((line, lineIndex) => (_jsx("li", { children: inline(line.replace(/^\s*[-*] /, "")) }, lineIndex))) }, blockIndex));
86
- }
87
- return (_jsx("p", { children: lines.map((line, lineIndex) => (_jsxs(Fragment, { children: [lineIndex > 0 && _jsx("br", {}), inline(line)] }, lineIndex))) }, blockIndex));
88
- }) }));
116
+ return (_jsx("div", { className: cx(text, className), "data-ciridae-ui": "", children: _jsx(MentionContext.Provider, { value: handlers, children: _jsx(Markdown, { headingIds: false, extensions: [chatInline], components: markdownComponents, children: splitSources(content).body }) }) }));
89
117
  }
90
118
  /** A mention as a chip: a link with `mentionHref`, a button with `onMentionSelect`, else a label. */
91
- function renderMention(part, key, onMentionSelect, mentionHref) {
92
- const href = mentionHref?.(part);
119
+ function renderMention(part, key, handlers) {
120
+ const href = handlers.mentionHref?.(part);
93
121
  if (href) {
94
122
  return (_jsx("a", { href: href, className: mention, "data-ciridae-ui": "", "data-mention": "", children: part.label }, key));
95
123
  }
124
+ const { onMentionSelect } = handlers;
96
125
  return onMentionSelect ? (_jsx("button", { type: "button", className: mention, "data-ciridae-ui": "", onClick: () => onMentionSelect(part), children: part.label }, key)) : (_jsx("span", { className: mention, "data-ciridae-ui": "", children: part.label }, key));
97
126
  }
98
- function renderInline(line, onMentionSelect, mentionHref) {
99
- return parseMentions(line).map((part, partIndex) => {
100
- if (typeof part !== "string")
101
- return renderMention(part, partIndex, onMentionSelect, mentionHref);
102
- return part.split(inlinePattern).map((run, runIndex) => {
103
- const key = `${partIndex}-${runIndex}`;
104
- if (run.startsWith("**") && run.endsWith("**")) {
105
- return _jsx("strong", { children: run.slice(2, -2) }, key);
127
+ function MarkdownMention({ id, kind, label }) {
128
+ return renderMention({ id, kind, label }, 0, useContext(MentionContext));
129
+ }
130
+ // Keep heading levels inside the surrounding chat page, and ordinary external links in a new tab.
131
+ const markdownComponents = {
132
+ "chat-mention": MarkdownMention,
133
+ h1: "h3",
134
+ h2: "h4",
135
+ h3: "h5",
136
+ h4: "h6",
137
+ h5: "h6",
138
+ h6: "h6",
139
+ a: ({ href, ...props }) => (_jsx("a", { ...props, href: href, target: /^[a-z][a-z\d+.-]*:/i.test(href ?? "") ? "_blank" : undefined, rel: "noreferrer" })),
140
+ // Images belong in the attachment viewer, not arbitrary remote requests from a reply.
141
+ img: ({ alt }) => _jsx(_Fragment, { children: alt }),
142
+ };
143
+ /** Small inline additions, leaving fences, escapes, nesting and URL safety to TanStack. */
144
+ const chatInline = {
145
+ name: "chat-inline",
146
+ inlineParser: {
147
+ markers: "[h<",
148
+ parse({ source, index, inLink }) {
149
+ if (inLink)
150
+ return undefined;
151
+ const rest = source.slice(index);
152
+ const token = /^\[@[^\]]+\]\(mention:\/\/[a-z][a-z0-9_-]*\/[^)\s]+\)/.exec(rest)?.[0];
153
+ if (token) {
154
+ const [part] = parseMentions(token);
155
+ if (part && typeof part !== "string") {
156
+ return {
157
+ length: token.length,
158
+ node: {
159
+ type: "inlineComponent",
160
+ name: "chat-mention",
161
+ tagName: "chat-mention",
162
+ attributes: {},
163
+ properties: { ...part },
164
+ children: [],
165
+ },
166
+ };
167
+ }
106
168
  }
107
- if (run.startsWith("`") && run.endsWith("`"))
108
- return _jsx("code", { children: run.slice(1, -1) }, key);
109
- const link = linkPattern.exec(run);
110
- if (link) {
111
- const [, label, href] = link;
112
- const external = /^[a-z][a-z\d+.-]*:/i.test(href ?? "");
113
- return (_jsx("a", { href: href, target: external ? "_blank" : undefined, rel: external ? "noreferrer" : undefined, children: label }, key));
169
+ const angle = /^<(https?:\/\/[^<>\s]+)>/.exec(rest);
170
+ if (!angle && /\w/.test(source[index - 1] ?? ""))
171
+ return undefined;
172
+ const url = angle?.[1] ?? /^https?:\/\/[^\s<>]+/.exec(rest)?.[0] ?? "";
173
+ let length = url.length;
174
+ while (!angle && length > 0 && /[.,:;"')\]*_~!?]/.test(url[length - 1] ?? ""))
175
+ length -= 1;
176
+ if (length > 0) {
177
+ const href = url.slice(0, length);
178
+ return {
179
+ length: angle?.[0].length ?? length,
180
+ node: { type: "link", href, children: [{ type: "text", value: href }] },
181
+ };
114
182
  }
115
- return _jsx(Fragment, { children: run }, key);
116
- });
117
- });
118
- }
183
+ return undefined;
184
+ },
185
+ },
186
+ };
@@ -2,17 +2,17 @@ import type { ChatClient } from "./chat-client.js";
2
2
  /** The caller's conversations, newest first, one page at a time. */
3
3
  export declare function useConversationList(client: ChatClient, { enabled }?: {
4
4
  enabled?: boolean;
5
- }): import("@tanstack/react-query/build/legacy").UseInfiniteQueryResult<import("@tanstack/react-query/build/legacy").InfiniteData<{
5
+ }): import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/react-query").InfiniteData<{
6
6
  data: {
7
- id: string & import("zod/index.cjs").$brand<"ConversationId">;
7
+ id: string & import("zod").$brand<"ConversationId">;
8
8
  agent: string;
9
9
  title: string | null;
10
10
  metadata: Record<string, string>;
11
11
  latest_response: {
12
- id: string & import("zod/index.cjs").$brand<"ResponseId">;
13
- conversation_id: string & import("zod/index.cjs").$brand<"ConversationId">;
14
- status: "queued" | "in_progress" | "requires_action" | "completed" | "failed" | "cancelled";
15
- output_item_ids: (string & import("zod/index.cjs").$brand<"ItemId">)[];
12
+ id: string & import("zod").$brand<"ResponseId">;
13
+ conversation_id: string & import("zod").$brand<"ConversationId">;
14
+ status: "failed" | "in_progress" | "completed" | "queued" | "requires_action" | "cancelled";
15
+ output_item_ids: (string & import("zod").$brand<"ItemId">)[];
16
16
  usage: {
17
17
  input_tokens: number;
18
18
  output_tokens: number;
package/dist/ciridae.css CHANGED
@@ -37,11 +37,18 @@
37
37
  }
38
38
  }
39
39
 
40
- /* [data-edge] paints a hairline inside the element's own box, so bordered elements
41
- keep a 1px transparent border for layout and borderless groups get an inset ring. */
40
+ /* [data-edge] paints a hairline inside the element's own box. The design counts that stroke in
41
+ the layout, so an edged element keeps a 1px transparent border under the ring. A border the
42
+ element sets itself wins: a native button resetting the browser's sets `1px solid transparent`,
43
+ and a face the design lays its edge over, such as a text button, sets `border: 0`. */
42
44
  [data-ciridae-ui][data-edge] {
43
45
  position: relative;
44
46
  }
47
+ @layer base {
48
+ :where([data-ciridae-ui][data-edge]) {
49
+ border: 1px solid transparent;
50
+ }
51
+ }
45
52
  [data-ciridae-ui][data-edge]::before {
46
53
  content: "";
47
54
  position: absolute;
@@ -64,8 +71,13 @@
64
71
  [data-ciridae-ui][data-edge="control"]::before {
65
72
  background: var(--colors-border\.control);
66
73
  }
67
- [data-ciridae-ui][data-edge="toolbar"]::before {
68
- background: var(--colors-border\.toolbar);
74
+ /* data-edge="gradient" draws the gradient its element sets in --edge, such as
75
+ token(gradients.border.infoGlass). In the base layer, so a component's own
76
+ ::before background still wins. */
77
+ @layer base {
78
+ :where([data-ciridae-ui][data-edge="gradient"])::before {
79
+ background: var(--edge, none);
80
+ }
69
81
  }
70
82
  /* An open trigger draws its own border in the text color, so the ring steps aside. */
71
83
  [data-ciridae-ui][data-edge][data-popup-open]::before {
@@ -1,6 +1,7 @@
1
1
  import { type ReactNode, type RefObject } from "react";
2
2
  import { type CiridaeLabels } from "./locale.js";
3
3
  import { type UserChipProps } from "./user-chip.js";
4
+ export { LogActor, LogEntry, LogGroupHeader, type LogActorProps, type LogEntryProps, type LogGroupHeaderProps, } from "./log-entry.js";
4
5
  export type ActivityFeedActor = Pick<UserChipProps, "name" | "kind" | "src" | "icon" | "profile">;
5
6
  export type ActivityFeedUpdate = {
6
7
  kind: "update";
@@ -71,10 +72,10 @@ export type ActivityFeedProps = {
71
72
  composerRef?: RefObject<HTMLDivElement | null>;
72
73
  };
73
74
  export type ActivityLabels = CiridaeLabels["activity"];
74
- export declare function ActivityFeed({ items, filter, onComment, tagOptions, placeholder, now, labels: ownLabels, composerRef, }: ActivityFeedProps): import("@types/react").JSX.Element;
75
+ export declare function ActivityFeed({ items, filter, onComment, tagOptions, placeholder, now, labels: ownLabels, composerRef, }: ActivityFeedProps): import("react").JSX.Element;
75
76
  /** All, Comments or Updates, as a compact segmented control. */
76
77
  export declare function ActivityFeedFilter({ value, onValueChange, labels: ownLabels, }: {
77
78
  value: ActivityFeedFilterValue;
78
79
  onValueChange: (value: ActivityFeedFilterValue) => void;
79
80
  labels?: Partial<ActivityLabels>;
80
- }): import("@types/react").JSX.Element;
81
+ }): import("react").JSX.Element;
@@ -14,6 +14,7 @@ import { Toolbar } from "./layout.js";
14
14
  import { useCiridaeLocale } from "./locale.js";
15
15
  import { MentionChip, openMentionPicker, readMentions, } from "./mention-input.js";
16
16
  import { UserName } from "./user-chip.js";
17
+ export { LogActor, LogEntry, LogGroupHeader, } from "./log-entry.js";
17
18
  const FILTERS = [
18
19
  { value: "all", label: "all", empty: "noActivity" },
19
20
  { value: "comments", label: "comments", empty: "noComments" },
@@ -56,7 +56,7 @@ export type AgentWorkProps = {
56
56
  className?: string;
57
57
  };
58
58
  /** What the agent is doing now and what waits on the user; nothing when neither. */
59
- export declare function AgentWork({ tasks, layout, onTaskSelect, className }: AgentWorkProps): import("@types/react").JSX.Element | null;
59
+ export declare function AgentWork({ tasks, layout, onTaskSelect, className }: AgentWorkProps): import("react").JSX.Element | null;
60
60
  export type AgentWorkButtonProps = {
61
61
  tasks: readonly AgentTask[];
62
62
  /** The drawer's title, such as the agent's name. */
@@ -67,10 +67,10 @@ export type AgentWorkButtonProps = {
67
67
  * The agent's work as a header icon button, such as on a phone: spinning while it works, with a
68
68
  * dot while something waits on the user, over a drawer with the column.
69
69
  */
70
- export declare function AgentWorkButton({ tasks, title, className }: AgentWorkButtonProps): import("@types/react").JSX.Element | null;
70
+ export declare function AgentWorkButton({ tasks, title, className }: AgentWorkButtonProps): import("react").JSX.Element | null;
71
71
  export type AgentTaskCardProps = {
72
72
  task: AgentTask;
73
73
  onReply?: (task: AgentTask, reply: AgentTaskReply) => void;
74
74
  };
75
75
  /** The agent asking: a waiting task's question, its details, and Deny / Approve or the answers. */
76
- export declare function AgentTaskCard({ task, onReply }: AgentTaskCardProps): import("@types/react").JSX.Element | null;
76
+ export declare function AgentTaskCard({ task, onReply }: AgentTaskCardProps): import("react").JSX.Element | null;
@@ -0,0 +1,91 @@
1
+ import { type PaletteName } from "../theme/index.js";
2
+ import { type AppNavigationProps } from "./app-navigation.js";
3
+ import { type HotkeyChord } from "./hotkeys.js";
4
+ import { type MenuItem } from "./menu.js";
5
+ import type { ComponentProps, ReactNode } from "react";
6
+ /** The palette the reader shows, which the host keeps, such as in storage, for `ThemeProvider`. */
7
+ export type PaletteChoice = {
8
+ /** `undefined` is the stylesheet's own palette, `ciridae`. */
9
+ value: PaletteName | undefined;
10
+ onValueChange: (palette: PaletteName) => void;
11
+ };
12
+ /** The color theme submenu: system, light or dark. */
13
+ export declare function useColorThemeMenuItem(): MenuItem;
14
+ /** The palette submenu over the kit's palettes. */
15
+ export declare function usePaletteMenuItem(choice: PaletteChoice): MenuItem;
16
+ export type AccountMenuProps = {
17
+ avatar: ReactNode;
18
+ name: string;
19
+ detail?: ReactNode;
20
+ /** The app's own entries, such as Edit profile, above the color theme. */
21
+ items?: readonly MenuItem[];
22
+ /** Adds a palette submenu after the color theme. */
23
+ palette?: PaletteChoice;
24
+ /** The last entry, in the danger color. */
25
+ signOut?: {
26
+ onSelect: () => void;
27
+ loading?: boolean;
28
+ disabled?: boolean;
29
+ };
30
+ /** The avatar alone, for the rail and the phone header. */
31
+ compact?: boolean;
32
+ };
33
+ /** The signed-in account's menu: the app's entries, the color theme and palette, then sign out. */
34
+ export declare function AccountMenu({ avatar, name, detail, items, palette, signOut, compact, }: AccountMenuProps): import("react").JSX.Element;
35
+ export type AssistantLauncherProps = Omit<ComponentProps<"button">, "children"> & {
36
+ /** Such as "Ask Ciridae"; the rail's button is named by it. */
37
+ label: string;
38
+ icon: ReactNode;
39
+ /** The hotkey that opens the assistant, shown at the end; the host binds it. */
40
+ hotkey?: HotkeyChord;
41
+ /** The icon alone, round, for the rail. */
42
+ compact?: boolean;
43
+ };
44
+ /** Opens the app's assistant from the sidebar's foot: a raised pill, or a round icon in the rail. */
45
+ export declare function AssistantLauncher({ label, icon: launcherIcon, hotkey, compact, className, ...props }: AssistantLauncherProps): import("react").JSX.Element;
46
+ /** A footer control drawn for every width: a sidebar row, a rail icon and a phone-header icon. */
47
+ export type AppFrameItem = {
48
+ id: string;
49
+ label: string;
50
+ icon: ReactNode;
51
+ onClick: () => void;
52
+ /** For a toggle, such as an assistant pane; marks the control pressed. */
53
+ pressed?: boolean;
54
+ };
55
+ export type AppAccount = Omit<AccountMenuProps, "compact">;
56
+ export type AppFrameProps = {
57
+ /**
58
+ * The main panel's breadcrumbs, the last one the page itself; the frame then draws the panel and
59
+ * its header. Leave out when each page renders its own `PageFrame`.
60
+ */
61
+ title?: ReactNode;
62
+ /** The header's actions, with `title`. */
63
+ actions?: ReactNode;
64
+ /** Top of the expanded sidebar, such as search or the product name. */
65
+ sidebarTop: ReactNode;
66
+ /** Top of the rail, which stands in for the sidebar at narrower widths. */
67
+ sidebarRail: ReactNode;
68
+ /** The sidebar's sections as data, which `AppNavigation` draws in the sidebar and the rail. */
69
+ navigation?: Pick<AppNavigationProps, "sections" | "activeId" | "renderLink">;
70
+ /** The sidebar's own content, in place of `navigation`. */
71
+ sidebar?: ReactNode;
72
+ /** Expanded-only content at the top of the footer. */
73
+ sidebarFooter?: ReactNode;
74
+ items?: readonly AppFrameItem[];
75
+ /** Content before the shared status link and build SHA in the expanded footer. */
76
+ footnote?: ReactNode;
77
+ /** The bundled commit. A non-null SHA enables checks against /version.json (requires QueryClientProvider). */
78
+ buildSha?: string | null;
79
+ /** The app's status page, shown in the footer, rail and phone header. */
80
+ statusUrl?: string;
81
+ /** More icons for the rail's footer, after `items`. */
82
+ railActions?: ReactNode;
83
+ /** More icons for the phone header, with `title`, before `items` and the account. */
84
+ compactActions?: ReactNode;
85
+ account?: AppAccount;
86
+ /** Dialogs, sheets and other overlays the frame's controls open. */
87
+ overlays?: ReactNode;
88
+ children: ReactNode;
89
+ };
90
+ /** The app's frame: the sidebar for every width, its footer and account, and the page. */
91
+ export declare function AppFrame({ title, actions, sidebarTop, sidebarRail, navigation, sidebar, sidebarFooter, items, footnote, buildSha, statusUrl, railActions, compactActions, account, overlays, children, }: AppFrameProps): import("react").JSX.Element;