lemma-sdk 0.2.18 → 0.2.20

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.
package/README.md CHANGED
@@ -179,7 +179,32 @@ Import the bundled stylesheet once anywhere in your app:
179
179
  import "lemma-sdk/react/styles.css";
180
180
  ```
181
181
 
182
- The stylesheet includes the SDK theme tokens and semantic assistant classes. You do not need the Lemma app's internal Tailwind setup just to render the assistant correctly.
182
+ The stylesheet includes the SDK theme tokens and the complete semantic assistant UI. The assistant components do not depend on the host app's Tailwind version or Tailwind content scanning.
183
+
184
+ If you alias the package to local SDK source in Vite, make sure the alias points at the React source and stylesheet:
185
+
186
+ ```ts
187
+ // vite.config.ts
188
+ import path from "node:path";
189
+
190
+ export default {
191
+ resolve: {
192
+ alias: {
193
+ "lemma-sdk/react/styles.css": path.resolve(__dirname, "../lemma-typescript/src/react/styles.css"),
194
+ "lemma-sdk/react": path.resolve(__dirname, "../lemma-typescript/src/react/index.ts"),
195
+ "lemma-sdk": path.resolve(__dirname, "../lemma-typescript/src/index.ts"),
196
+ },
197
+ },
198
+ };
199
+ ```
200
+
201
+ Quick checklist for developers:
202
+
203
+ - import `lemma-sdk/react/styles.css` once
204
+ - give the assistant container a real height
205
+ - if the assistant is inside flex/grid, add `min-height: 0` on the relevant parent
206
+ - if you use `AssistantEmbedded`, pass `theme` directly there
207
+ - if you use `AssistantExperienceView`, wrap it in `AssistantThemeScope`
183
208
 
184
209
  The assistant UI renders markdown by default:
185
210
 
@@ -188,11 +213,22 @@ The assistant UI renders markdown by default:
188
213
  - links open safely in a new tab by default
189
214
  - lists, tables, blockquotes, inline code, and fenced code blocks are styled out of the box
190
215
 
216
+ #### Recommended path
217
+
218
+ For most apps, start with `AssistantEmbedded`.
219
+
220
+ - use `AssistantEmbedded` when you want the SDK to handle the controller lifecycle and render the ready-made assistant UI
221
+ - use `AssistantExperienceView` when you still want the SDK's default assistant UI, but you need to own the controller lifecycle yourself
222
+ - use `useAssistantController` plus primitives only when you are intentionally building a custom shell or custom layout
223
+
224
+ If you are unsure, use `AssistantEmbedded` first. It is the path we recommend and the one we expect most SDK consumers to ship.
225
+
191
226
  #### Choose an integration level
192
227
 
193
228
  ##### 1. `AssistantEmbedded` for the fastest setup
194
229
 
195
230
  Use `AssistantEmbedded` when you want a ready-made assistant surface with the SDK defaults.
231
+ This is the recommended integration for most users.
196
232
 
197
233
  ```tsx
198
234
  import "lemma-sdk/react/styles.css";
@@ -208,7 +244,14 @@ function SupportAssistant() {
208
244
  title="Support Assistant"
209
245
  subtitle="Ask questions about this pod."
210
246
  placeholder="Message Support Assistant"
247
+ emptyStateSuggestions={[
248
+ { text: "Summarize this conversation", icon: "✦" },
249
+ { text: "Help me draft a response", icon: "✎" },
250
+ { text: "List the next steps", icon: "→" },
251
+ ]}
211
252
  showConversationList
253
+ showModelPicker={false}
254
+ radius="lg"
212
255
  theme="auto"
213
256
  />
214
257
  </div>
@@ -219,13 +262,18 @@ function SupportAssistant() {
219
262
  Important notes:
220
263
 
221
264
  - `theme` accepts `"auto" | "light" | "dark"`
265
+ - `radius` lets you pick the built-in rounding scale from `"none"` through `"xl"`
266
+ - `showModelPicker={false}` hides the built-in model chooser when you do not want model controls visible
222
267
  - `theme="auto"` follows the host app when it uses common selectors like `.dark`, `[data-theme="dark"]`, `[data-mode="dark"]`, and also falls back to `prefers-color-scheme`
223
268
  - the parent container must have a real height; if it lives inside flex/grid, `min-height: 0` is usually needed too
224
269
  - attachments are queued into the composer and sent with the next message by default
270
+ - `emptyStateSuggestions` lets you replace the built-in prompt chips shown before the first message
271
+ - prefer this component unless you specifically need to own controller state or replace the built-in layout
225
272
 
226
273
  ##### 2. `AssistantExperienceView` for the default UI with your own controller
227
274
 
228
275
  Use `AssistantExperienceView` when you want the built-in assistant layout, but you need to own the controller lifecycle yourself.
276
+ This is the second-best default when `AssistantEmbedded` is too opinionated for your integration.
229
277
 
230
278
  ```tsx
231
279
  import "lemma-sdk/react/styles.css";
@@ -249,6 +297,11 @@ function ControlledAssistant() {
249
297
  title="Support Assistant"
250
298
  subtitle="Direct use of the default assistant experience."
251
299
  placeholder="Message Support Assistant"
300
+ emptyStateSuggestions={[
301
+ { text: "Summarize the current context" },
302
+ { text: "Help me write a reply" },
303
+ { text: "What should I do next?" },
304
+ ]}
252
305
  showConversationList
253
306
  chromeStyle="subtle"
254
307
  statusPlacement="inline"
@@ -263,13 +316,19 @@ Useful props on `AssistantExperienceView`:
263
316
  - `showConversationList`: show the built-in conversation sidebar
264
317
  - `chromeStyle`: `"elevated" | "subtle" | "flat"`
265
318
  - `statusPlacement`: `"inline" | "composer" | "none"`
319
+ - `radius`: `"none" | "sm" | "md" | "lg" | "xl"`
320
+ - `showModelPicker`: show or hide the built-in model selector
321
+ - `showNewConversationButton`: show or hide the built-in reset/new-conversation button
322
+ - `emptyStateSuggestions`: replace the built-in generic prompt suggestions used by the default empty state
266
323
  - `renderMessageContent`: override markdown rendering for custom message content
267
324
  - `renderToolInvocation`: replace the default tool activity renderer
268
325
  - `renderPresentedFile` and `renderPendingFile`: customize attachment rendering
326
+ - prefer this over building from primitives if you still want the SDK's default assistant experience
269
327
 
270
328
  ##### 3. `useAssistantController` + primitives for a custom shell
271
329
 
272
330
  Use the primitives when you want full control over layout and app chrome.
331
+ This is the advanced path and should be the exception, not the starting point.
273
332
 
274
333
  ```tsx
275
334
  import "lemma-sdk/react/styles.css";
@@ -279,6 +338,7 @@ import {
279
338
  AssistantMessageViewport,
280
339
  AssistantShellLayout,
281
340
  AssistantThemeScope,
341
+ EmptyState,
282
342
  MessageGroup,
283
343
  PlanSummaryStrip,
284
344
  ThinkingIndicator,
@@ -313,6 +373,19 @@ function CustomAssistantShell() {
313
373
  {activeToolBanner ? <div>{activeToolBanner.summary}</div> : null}
314
374
 
315
375
  <AssistantMessageViewport>
376
+ {assistant.messages.length === 0 ? (
377
+ <EmptyState
378
+ suggestions={[
379
+ { text: "Summarize this for me" },
380
+ { text: "Help me draft a reply" },
381
+ { text: "Brainstorm next steps" },
382
+ ]}
383
+ onSendMessage={(text) => {
384
+ void assistant.sendMessage(text);
385
+ }}
386
+ />
387
+ ) : null}
388
+
316
389
  {rows.map((row, index) => (
317
390
  <MessageGroup
318
391
  key={row.id}
@@ -355,6 +428,14 @@ Useful primitives exported from `lemma-sdk/react`:
355
428
  - `PlanSummaryStrip`
356
429
  - `ThinkingIndicator`
357
430
 
431
+ Guidance:
432
+
433
+ - prefer `AssistantEmbedded` over this path when the SDK layout is acceptable
434
+ - prefer `AssistantExperienceView` over this path when you only need controller ownership, theming control, or a few render overrides
435
+ - reach for primitives only when you are replacing the layout itself or deeply integrating the assistant into app-specific chrome
436
+
437
+ Default empty-state suggestions are intentionally generic so they work across support, internal tools, content, and general assistant use cases. Override them with `emptyStateSuggestions` when you want task-specific prompts.
438
+
358
439
  #### Theming
359
440
 
360
441
  Use `AssistantThemeScope` around custom assistant layouts:
@@ -103,6 +103,7 @@ export type { FunctionRunListResponse } from './models/FunctionRunListResponse.j
103
103
  export type { FunctionRunResponse } from './models/FunctionRunResponse.js';
104
104
  export { FunctionRunStatus } from './models/FunctionRunStatus.js';
105
105
  export { FunctionStatus } from './models/FunctionStatus.js';
106
+ export { FunctionType } from './models/FunctionType.js';
106
107
  export type { HTTPValidationError } from './models/HTTPValidationError.js';
107
108
  export type { IconUploadRequest } from './models/IconUploadRequest.js';
108
109
  export type { IconUploadResponse } from './models/IconUploadResponse.js';
@@ -18,6 +18,7 @@ export { FlowRunStatus } from './models/FlowRunStatus.js';
18
18
  export { FlowStartType } from './models/FlowStartType.js';
19
19
  export { FunctionRunStatus } from './models/FunctionRunStatus.js';
20
20
  export { FunctionStatus } from './models/FunctionStatus.js';
21
+ export { FunctionType } from './models/FunctionType.js';
21
22
  export { OrganizationInvitationStatus } from './models/OrganizationInvitationStatus.js';
22
23
  export { OrganizationRole } from './models/OrganizationRole.js';
23
24
  export { PodAppMode } from './models/PodAppMode.js';
@@ -6,4 +6,8 @@ export type BulkCreateRecordsRequest = {
6
6
  * List of record payload objects to insert.
7
7
  */
8
8
  records: Array<Record<string, any>>;
9
+ /**
10
+ * When true, insert records and update existing rows that conflict on the table primary key.
11
+ */
12
+ upsert?: boolean;
9
13
  };
@@ -1,4 +1,5 @@
1
1
  import type { ApplicationAccessConfig } from './ApplicationAccessConfig.js';
2
+ import type { FunctionType } from './FunctionType.js';
2
3
  import type { TableAccessEntry } from './TableAccessEntry.js';
3
4
  /**
4
5
  * Request to create a function.
@@ -15,4 +16,5 @@ export type CreateFunctionRequest = {
15
16
  input_schema?: Record<string, any>;
16
17
  name: string;
17
18
  output_schema?: Record<string, any>;
19
+ type?: FunctionType;
18
20
  };
@@ -1,5 +1,6 @@
1
1
  import type { ApplicationAccessConfig } from './ApplicationAccessConfig.js';
2
2
  import type { FunctionStatus } from './FunctionStatus.js';
3
+ import type { FunctionType } from './FunctionType.js';
3
4
  import type { TableAccessEntry } from './TableAccessEntry.js';
4
5
  /**
5
6
  * Function response.
@@ -21,6 +22,7 @@ export type FunctionResponse = {
21
22
  output_schema: Record<string, any>;
22
23
  pod_id: string;
23
24
  status: FunctionStatus;
25
+ type: FunctionType;
24
26
  updated_at: any;
25
27
  user_id: string;
26
28
  };
@@ -9,9 +9,11 @@ export type FunctionRunResponse = {
9
9
  function_id: string;
10
10
  id: string;
11
11
  input_data?: (Record<string, any> | null);
12
+ job_id?: (string | null);
12
13
  logs?: (string | null);
13
14
  output_data?: (Record<string, any> | null);
14
15
  started_at: any;
15
16
  status: FunctionRunStatus;
17
+ user_email?: (string | null);
16
18
  user_id: string;
17
19
  };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Execution mode for a function.
3
+ */
4
+ export declare enum FunctionType {
5
+ API = "API",
6
+ JOB = "JOB"
7
+ }
@@ -0,0 +1,12 @@
1
+ /* generated using openapi-typescript-codegen -- do not edit */
2
+ /* istanbul ignore file */
3
+ /* tslint:disable */
4
+ /* eslint-disable */
5
+ /**
6
+ * Execution mode for a function.
7
+ */
8
+ export var FunctionType;
9
+ (function (FunctionType) {
10
+ FunctionType["API"] = "API";
11
+ FunctionType["JOB"] = "JOB";
12
+ })(FunctionType || (FunctionType = {}));
@@ -1,4 +1,5 @@
1
1
  import type { ApplicationAccessConfig } from './ApplicationAccessConfig.js';
2
+ import type { FunctionType } from './FunctionType.js';
2
3
  import type { TableAccessEntry } from './TableAccessEntry.js';
3
4
  /**
4
5
  * Request to update a function.
@@ -11,4 +12,5 @@ export type UpdateFunctionRequest = {
11
12
  config?: (Record<string, any> | null);
12
13
  description?: (string | null);
13
14
  icon_url?: (string | null);
15
+ type?: (FunctionType | null);
14
16
  };
@@ -7,52 +7,42 @@ export function AssistantThemeScope({ className, children, theme = "auto", ...pr
7
7
  return (_jsx("div", { "data-lemma-theme": theme, className: cx("lemma-assistant-theme", className), ...props, children: children }));
8
8
  }
9
9
  export const AssistantMessageViewport = forwardRef(function AssistantMessageViewport({ className, innerClassName, children, ...props }, ref) {
10
- return (_jsx("div", { ref: ref, className: cx("lemma-assistant-viewport", "min-h-0 flex-1 overflow-y-auto bg-[var(--bg-surface)] px-4 py-4", className), ...props, children: _jsx("div", { className: cx("lemma-assistant-viewport-inner", "mx-auto flex w-full max-w-5xl flex-col gap-3", innerClassName), children: children }) }));
10
+ return (_jsx("div", { ref: ref, className: cx("lemma-assistant-viewport", className), ...props, children: _jsx("div", { className: cx("lemma-assistant-viewport-inner", innerClassName), children: children }) }));
11
11
  });
12
12
  export function AssistantShellLayout({ sidebar, sidebarVisible = false, main, className, }) {
13
13
  const hasSidebar = !!sidebar;
14
- return (_jsxs("div", { className: cx("lemma-assistant-shell", hasSidebar && "lemma-assistant-shell--with-sidebar", hasSidebar && sidebarVisible && "lemma-assistant-shell--sidebar-visible", "mx-auto h-full w-full min-h-0 font-sans antialiased", className), children: [sidebar && sidebarVisible ? (_jsx("div", { className: "lemma-assistant-shell-sidebar", children: sidebar })) : null, main] }));
14
+ return (_jsxs("div", { className: cx("lemma-assistant-shell", hasSidebar && "lemma-assistant-shell--with-sidebar", hasSidebar && sidebarVisible && "lemma-assistant-shell--sidebar-visible", className), children: [sidebar && sidebarVisible ? (_jsx("div", { className: "lemma-assistant-shell-sidebar", children: sidebar })) : null, main] }));
15
15
  }
16
16
  export function AssistantHeader({ title, subtitle, badge, controls, tone = "subtle", className, }) {
17
- return (_jsxs("div", { "data-tone": tone, className: cx("lemma-assistant-header", "flex items-center justify-between border-b border-[color:color-mix(in_srgb,_var(--border-default)_80%,_transparent)] px-4 py-3", className), children: [_jsxs("div", { className: "lemma-assistant-header-copy flex items-center gap-2.5", children: [badge ? (_jsx("div", { className: "lemma-assistant-header-badge flex h-7 w-7 items-center justify-center rounded-full bg-[linear-gradient(135deg,var(--brand-primary),var(--brand-secondary))] shadow-[var(--shadow-xs)]", children: badge })) : null, _jsxs("div", { className: "lemma-assistant-header-titles", children: [_jsx("h3", { className: "lemma-assistant-header-title text-[13px] font-semibold leading-tight text-[var(--text-primary)]", children: title }), subtitle ? (_jsx("p", { className: "lemma-assistant-header-subtitle text-[11px] text-[var(--text-tertiary)]", children: subtitle })) : null] })] }), controls ? (_jsx("div", { className: "lemma-assistant-header-controls flex items-center gap-1", children: controls })) : null] }));
17
+ return (_jsxs("div", { "data-tone": tone, className: cx("lemma-assistant-header", className), children: [_jsxs("div", { className: "lemma-assistant-header-copy", children: [badge ? (_jsx("div", { className: "lemma-assistant-header-badge", children: badge })) : null, _jsxs("div", { className: "lemma-assistant-header-titles", children: [_jsx("h3", { className: "lemma-assistant-header-title", children: title }), subtitle ? (_jsx("p", { className: "lemma-assistant-header-subtitle", children: subtitle })) : null] })] }), controls ? (_jsx("div", { className: "lemma-assistant-header-controls", children: controls })) : null] }));
18
18
  }
19
19
  export function AssistantConversationList({ conversations, activeConversationId, onSelectConversation, onNewConversation, renderConversationLabel, title = "Conversations", newLabel = "New", className, }) {
20
- return (_jsxs("aside", { className: cx("lemma-assistant-conversation-list", "flex h-full min-h-0 flex-col overflow-hidden rounded-2xl border border-[color:color-mix(in_srgb,_var(--border-default)_80%,_transparent)] bg-[var(--bg-surface)] shadow-[var(--shadow-lg)]", className), children: [_jsx("div", { className: "lemma-assistant-conversation-list-header border-b border-[color:color-mix(in_srgb,_var(--border-default)_80%,_transparent)] px-4 py-3", children: _jsxs("div", { className: "lemma-assistant-conversation-list-header-row flex items-center justify-between gap-3", children: [_jsxs("div", { className: "lemma-assistant-conversation-list-copy", children: [_jsx("div", { className: "lemma-assistant-conversation-list-title text-[13px] font-semibold text-[var(--text-primary)]", children: title }), _jsxs("div", { className: "lemma-assistant-conversation-list-meta mt-1 text-[11px] text-[var(--text-tertiary)]", children: [conversations.length, " total"] })] }), onNewConversation ? (_jsx("button", { type: "button", onClick: onNewConversation, className: "lemma-assistant-conversation-list-new rounded-full border border-[var(--border-default)] bg-[var(--bg-surface)] px-3 py-1.5 text-[11px] font-medium text-[var(--text-secondary)] hover:text-[var(--text-primary)]", children: newLabel })) : null] }) }), _jsx("div", { className: "lemma-assistant-conversation-list-items min-h-0 flex-1 overflow-y-auto p-3 space-y-2", children: conversations.map((conversation) => {
20
+ return (_jsxs("aside", { className: cx("lemma-assistant-conversation-list", className), children: [_jsx("div", { className: "lemma-assistant-conversation-list-header", children: _jsxs("div", { className: "lemma-assistant-conversation-list-header-row", children: [_jsxs("div", { className: "lemma-assistant-conversation-list-copy", children: [_jsx("div", { className: "lemma-assistant-conversation-list-title", children: title }), _jsxs("div", { className: "lemma-assistant-conversation-list-meta", children: [conversations.length, " total"] })] }), onNewConversation ? (_jsx("button", { type: "button", onClick: onNewConversation, className: "lemma-assistant-conversation-list-new", children: newLabel })) : null] }) }), _jsx("div", { className: "lemma-assistant-conversation-list-items", children: conversations.map((conversation) => {
21
21
  const isActive = conversation.id === activeConversationId;
22
- return (_jsxs("button", { type: "button", onClick: () => onSelectConversation(conversation.id), className: cx("lemma-assistant-conversation-list-item", "w-full rounded-xl border px-3 py-2.5 text-left transition-colors", isActive
23
- ? "lemma-assistant-conversation-list-item-active border-[color:color-mix(in_srgb,_var(--brand-primary)_44%,_var(--border-default))] bg-[color:color-mix(in_srgb,_var(--brand-glow)_42%,_var(--bg-surface))]"
24
- : "border-[var(--border-default)] bg-[var(--bg-surface)] hover:bg-[var(--bg-subtle)]"), children: [_jsx("div", { className: "lemma-assistant-conversation-list-item-title truncate text-[12px] font-medium text-[var(--text-primary)]", children: renderConversationLabel
22
+ return (_jsxs("button", { type: "button", onClick: () => onSelectConversation(conversation.id), className: cx("lemma-assistant-conversation-list-item", isActive && "lemma-assistant-conversation-list-item-active"), children: [_jsx("div", { className: "lemma-assistant-conversation-list-item-title", children: renderConversationLabel
25
23
  ? renderConversationLabel({ conversation, isActive })
26
- : (conversation.title || "Untitled conversation") }), _jsx("div", { className: "lemma-assistant-conversation-list-item-status mt-1 text-[10px] uppercase tracking-[0.08em] text-[var(--text-tertiary)]", children: (conversation.status || "waiting").toLowerCase() })] }, conversation.id));
24
+ : (conversation.title || "Untitled conversation") }), _jsx("div", { className: "lemma-assistant-conversation-list-item-status", children: (conversation.status || "waiting").toLowerCase() })] }, conversation.id));
27
25
  }) })] }));
28
26
  }
29
27
  export function AssistantModelPicker({ value, options, disabled, autoLabel = "Auto", getOptionLabel, onChange, className, }) {
30
28
  const autoValue = "__AUTO__";
31
- return (_jsxs("select", { value: value ?? autoValue, onChange: (event) => onChange(event.target.value === autoValue ? null : event.target.value), disabled: disabled, className: cx("lemma-assistant-model-picker", "h-8 rounded-full border border-[color:color-mix(in_srgb,_var(--border-default)_80%,_transparent)] bg-[var(--bg-surface)] px-3 text-[11px] text-[var(--text-secondary)]", className), "aria-label": "Conversation model", title: "Conversation model", children: [_jsx("option", { value: autoValue, children: autoLabel }), options.map((option) => (_jsx("option", { value: option, children: getOptionLabel ? getOptionLabel(option) : option }, option)))] }));
29
+ return (_jsxs("select", { value: value ?? autoValue, onChange: (event) => onChange(event.target.value === autoValue ? null : event.target.value), disabled: disabled, className: cx("lemma-assistant-model-picker", className), "aria-label": "Conversation model", title: "Conversation model", children: [_jsx("option", { value: autoValue, children: autoLabel }), options.map((option) => (_jsx("option", { value: option, children: getOptionLabel ? getOptionLabel(option) : option }, option)))] }));
32
30
  }
33
31
  export function AssistantAskOverlay({ questionNumber, totalQuestions, question, options, selectedOptions, canContinue, continueLabel, onSelectOption, onContinue, onSkip, mode = "single_select", }) {
34
- return (_jsxs("div", { className: "lemma-assistant-ask-overlay space-y-2", children: [_jsxs("div", { className: "lemma-assistant-ask-overlay-header flex items-start justify-between gap-3", children: [_jsxs("div", { className: "lemma-assistant-ask-overlay-copy", children: [_jsxs("div", { className: "lemma-assistant-ask-overlay-kicker text-[11px] uppercase tracking-[0.12em] text-[var(--text-tertiary)]", children: ["Question ", questionNumber, " of ", totalQuestions] }), _jsx("p", { className: "lemma-assistant-ask-overlay-question mt-1 text-[14px] font-medium leading-6 text-[var(--text-primary)]", children: question })] }), onSkip ? (_jsx("button", { type: "button", onClick: onSkip, className: "lemma-assistant-ask-overlay-skip rounded-md px-2 py-1 text-[12px] text-[var(--text-tertiary)] transition-colors hover:bg-[var(--bg-subtle)] hover:text-[var(--text-primary)]", children: "Skip" })) : null] }), _jsx("div", { className: "lemma-assistant-ask-overlay-options max-h-[260px] space-y-1.5 overflow-y-auto pr-1", children: options.map((option, optionIndex) => {
32
+ return (_jsxs("div", { className: "lemma-assistant-ask-overlay", children: [_jsxs("div", { className: "lemma-assistant-ask-overlay-header", children: [_jsxs("div", { className: "lemma-assistant-ask-overlay-copy", children: [_jsxs("div", { className: "lemma-assistant-ask-overlay-kicker", children: ["Question ", questionNumber, " of ", totalQuestions] }), _jsx("p", { className: "lemma-assistant-ask-overlay-question", children: question })] }), onSkip ? (_jsx("button", { type: "button", onClick: onSkip, className: "lemma-assistant-ask-overlay-skip", children: "Skip" })) : null] }), _jsx("div", { className: "lemma-assistant-ask-overlay-options", children: options.map((option, optionIndex) => {
35
33
  const isSelected = selectedOptions.includes(option);
36
34
  const rankLabel = mode === "rank_priorities" && isSelected
37
35
  ? selectedOptions.indexOf(option) + 1
38
36
  : null;
39
- return (_jsx("button", { type: "button", onClick: () => onSelectOption(option), className: cx("lemma-assistant-ask-overlay-option", "w-full rounded-lg border px-2.5 py-2 text-left text-[13px] transition-colors", isSelected
40
- ? "lemma-assistant-ask-overlay-option-selected border-[color:color-mix(in_srgb,_var(--brand-primary)_64%,_var(--border-subtle))] bg-[color:color-mix(in_srgb,_var(--brand-primary)_14%,_transparent)] text-[var(--text-primary)]"
41
- : "border-[var(--border-default)] bg-[var(--bg-canvas)] text-[var(--text-secondary)] hover:bg-[var(--bg-subtle)] hover:text-[var(--text-primary)]"), children: _jsxs("span", { className: "lemma-assistant-ask-overlay-option-label inline-flex items-center gap-2", children: [rankLabel ? (_jsx("span", { className: "inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-[var(--brand-primary)] px-1 text-[10px] font-semibold text-[var(--text-on-brand)]", children: rankLabel })) : (_jsx("span", { className: cx("inline-block h-2.5 w-2.5 rounded-full border", isSelected
42
- ? "border-[var(--brand-primary)] bg-[var(--brand-primary)]"
43
- : "border-[var(--border-default)] bg-transparent") })), option] }) }, `${option}-${optionIndex}`));
44
- }) }), onContinue ? (_jsx("div", { className: "lemma-assistant-ask-overlay-actions flex justify-end", children: _jsx("button", { type: "button", onClick: onContinue, disabled: !canContinue, className: cx("lemma-assistant-ask-overlay-continue", "rounded-md px-2.5 py-1.5 text-[12px] font-medium transition-colors", canContinue
45
- ? "bg-[var(--brand-primary)] text-[var(--text-on-brand)] hover:bg-[color:color-mix(in_srgb,_var(--brand-primary)_88%,_var(--text-primary))]"
46
- : "bg-[var(--bg-subtle)] text-[var(--text-tertiary)]"), children: continueLabel }) })) : null] }));
37
+ return (_jsx("button", { type: "button", onClick: () => onSelectOption(option), className: cx("lemma-assistant-ask-overlay-option", isSelected && "lemma-assistant-ask-overlay-option-selected"), children: _jsxs("span", { className: "lemma-assistant-ask-overlay-option-label", children: [rankLabel ? (_jsx("span", { className: "lemma-assistant-ask-overlay-option-rank", children: rankLabel })) : (_jsx("span", { className: cx("lemma-assistant-ask-overlay-option-indicator", isSelected && "lemma-assistant-ask-overlay-option-indicator-selected") })), option] }) }, `${option}-${optionIndex}`));
38
+ }) }), onContinue ? (_jsx("div", { className: "lemma-assistant-ask-overlay-actions", children: _jsx("button", { type: "button", onClick: onContinue, disabled: !canContinue, className: cx("lemma-assistant-ask-overlay-continue", canContinue && "lemma-assistant-ask-overlay-continue-enabled"), children: continueLabel }) })) : null] }));
47
39
  }
48
40
  export function AssistantComposer({ floating, status, pendingFiles, children, tone = "default", className, }) {
49
- return (_jsxs("div", { "data-tone": tone, "data-has-status": status ? "true" : "false", "data-has-pending-files": pendingFiles ? "true" : "false", "data-has-floating": floating ? "true" : "false", className: cx("lemma-assistant-composer", "relative rounded-2xl border border-[color:color-mix(in_srgb,_var(--border-default)_80%,_transparent)] bg-[var(--bg-surface)] p-2 shadow-[var(--shadow-md)]", className), children: [floating ? (_jsx("div", { className: "lemma-assistant-composer-floating absolute bottom-[calc(100%+8px)] left-0 right-0 z-20", children: floating })) : null, status ? (_jsx("div", { className: "lemma-assistant-composer-status-rail min-h-[34px] px-2 pb-1", children: _jsx("div", { className: "lemma-assistant-composer-status flex min-h-[26px] items-center transition-opacity duration-200", children: status }) })) : null, pendingFiles ? (_jsx("div", { className: "lemma-assistant-composer-pending flex flex-wrap items-center gap-1.5 px-1 pb-1.5", children: pendingFiles })) : null, _jsx("div", { className: "lemma-assistant-composer-body", children: children })] }));
41
+ return (_jsxs("div", { "data-tone": tone, "data-has-status": status ? "true" : "false", "data-has-pending-files": pendingFiles ? "true" : "false", "data-has-floating": floating ? "true" : "false", className: cx("lemma-assistant-composer", className), children: [floating ? (_jsx("div", { className: "lemma-assistant-composer-floating", children: floating })) : null, status ? (_jsx("div", { className: "lemma-assistant-composer-status-rail", children: _jsx("div", { className: "lemma-assistant-composer-status", children: status }) })) : null, pendingFiles ? (_jsx("div", { className: "lemma-assistant-composer-pending", children: pendingFiles })) : null, _jsx("div", { className: "lemma-assistant-composer-body", children: children })] }));
50
42
  }
51
43
  export function AssistantPendingFileChip({ label, onRemove, className, }) {
52
- return (_jsxs("span", { className: cx("lemma-assistant-pending-file-chip", "inline-flex max-w-full items-center gap-1.5 rounded-full bg-[var(--bg-subtle)] px-2 py-1 text-[11px] text-[var(--text-secondary)]", className), children: [_jsx("span", { className: "lemma-assistant-pending-file-chip-label truncate max-w-[180px]", children: label }), onRemove ? (_jsx("button", { type: "button", onClick: onRemove, className: "lemma-assistant-pending-file-chip-remove inline-flex h-4 w-4 items-center justify-center rounded-full hover:bg-[var(--bg-canvas)]", title: "Remove file", children: "\u00D7" })) : null] }));
44
+ return (_jsxs("span", { className: cx("lemma-assistant-pending-file-chip", className), children: [_jsx("span", { className: "lemma-assistant-pending-file-chip-label", children: label }), onRemove ? (_jsx("button", { type: "button", onClick: onRemove, className: "lemma-assistant-pending-file-chip-remove", title: "Remove file", children: "\u00D7" })) : null] }));
53
45
  }
54
46
  export function AssistantStatusPill({ label, subtle = false, className, }) {
55
- return (_jsxs("div", { className: cx("lemma-assistant-status-pill", "inline-flex min-h-[30px] max-w-full items-center gap-2 rounded-full px-3 py-1.5 text-[12px] transition-all duration-200", subtle
56
- ? "lemma-assistant-status-pill-subtle border border-[color:color-mix(in_srgb,_var(--border-default)_72%,_transparent)] bg-[color:color-mix(in_srgb,_var(--bg-surface)_90%,_transparent)] text-[var(--text-tertiary)]"
57
- : "border border-[color:color-mix(in_srgb,_var(--brand-primary)_24%,_var(--border-default))] bg-[color:color-mix(in_srgb,_var(--brand-glow)_28%,_var(--bg-surface))] text-[var(--text-secondary)]", className), children: [_jsxs("span", { className: "lemma-assistant-status-pill-dot relative inline-flex h-2.5 w-2.5 shrink-0", children: [_jsx("span", { className: "lemma-assistant-status-pill-dot-ping absolute inline-flex h-full w-full animate-ping rounded-full bg-[var(--brand-primary)]/45" }), _jsx("span", { className: "lemma-assistant-status-pill-dot-core relative inline-flex h-2.5 w-2.5 rounded-full bg-[var(--brand-primary)]" })] }), _jsx("span", { className: "lemma-assistant-status-pill-label truncate", children: label })] }));
47
+ return (_jsxs("div", { className: cx("lemma-assistant-status-pill", subtle && "lemma-assistant-status-pill-subtle", className), children: [_jsxs("span", { className: "lemma-assistant-status-pill-dot", children: [_jsx("span", { className: "lemma-assistant-status-pill-dot-ping" }), _jsx("span", { className: "lemma-assistant-status-pill-dot-core" })] }), _jsx("span", { className: "lemma-assistant-status-pill-label", children: label })] }));
58
48
  }
@@ -1,6 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { AssistantRenderableMessage, AssistantToolInvocation } from "../useAssistantController.js";
3
- import type { AssistantControllerView, AssistantConversationRenderArgs, AssistantMessageRenderArgs, AssistantPendingFileRenderArgs, AssistantPresentedFileRenderArgs, AssistantToolRenderArgs } from "./assistant-types.js";
3
+ import type { AssistantControllerView, AssistantConversationRenderArgs, AssistantMessageRenderArgs, AssistantPendingFileRenderArgs, AssistantPresentedFileRenderArgs, AssistantToolRenderArgs, EmptyStateSuggestion } from "./assistant-types.js";
4
4
  type PlanStatus = "pending" | "in_progress" | "completed";
5
5
  export interface PlanStepState {
6
6
  step: string;
@@ -35,17 +35,22 @@ export interface ActiveToolBanner {
35
35
  }
36
36
  export type AssistantChromeStyle = "elevated" | "subtle" | "flat";
37
37
  export type AssistantStatusPlacement = "inline" | "composer" | "none";
38
+ export type AssistantRadiusScale = "none" | "sm" | "md" | "lg" | "xl";
38
39
  export interface AssistantExperienceViewProps {
39
40
  controller: AssistantControllerView;
40
41
  title?: ReactNode;
41
42
  subtitle?: ReactNode;
42
43
  placeholder?: string;
43
44
  emptyState?: ReactNode;
45
+ emptyStateSuggestions?: EmptyStateSuggestion[];
44
46
  draft?: string;
45
47
  onDraftChange?: (value: string) => void;
46
48
  showConversationList?: boolean;
47
49
  chromeStyle?: AssistantChromeStyle;
48
50
  statusPlacement?: AssistantStatusPlacement;
51
+ radius?: AssistantRadiusScale;
52
+ showModelPicker?: boolean;
53
+ showNewConversationButton?: boolean;
49
54
  onNavigateResource?: (resourceType: string, resourceId: string, meta?: Record<string, unknown>) => void;
50
55
  renderConversationLabel?: (args: AssistantConversationRenderArgs) => ReactNode;
51
56
  renderMessageContent?: (args: AssistantMessageRenderArgs) => ReactNode;
@@ -65,9 +70,12 @@ export declare function PlanSummaryStrip({ plan, onHide }: {
65
70
  onHide: () => void;
66
71
  }): import("react/jsx-runtime").JSX.Element;
67
72
  export declare function ThinkingIndicator(): import("react/jsx-runtime").JSX.Element | null;
68
- export declare function EmptyState({ onSendMessage }: {
73
+ export interface EmptyStateProps {
69
74
  onSendMessage: (msg: string) => void;
70
- }): import("react/jsx-runtime").JSX.Element;
75
+ suggestions?: EmptyStateSuggestion[];
76
+ }
77
+ export declare const DEFAULT_EMPTY_STATE_SUGGESTIONS: EmptyStateSuggestion[];
78
+ export declare function EmptyState({ onSendMessage, suggestions, }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
71
79
  export declare function MessageGroup({ message, conversationId, onNavigateResource, onWidgetSendPrompt, isStreaming, showAssistantHeader, renderMessageContent, renderPresentedFile, renderToolInvocation, }: {
72
80
  message: AssistantRenderableMessage;
73
81
  conversationId?: string | null;
@@ -79,5 +87,5 @@ export declare function MessageGroup({ message, conversationId, onNavigateResour
79
87
  renderPresentedFile?: (args: AssistantPresentedFileRenderArgs) => ReactNode;
80
88
  renderToolInvocation?: (args: AssistantToolRenderArgs) => ReactNode;
81
89
  }): import("react/jsx-runtime").JSX.Element;
82
- export declare function AssistantExperienceView({ controller, title, subtitle, placeholder, emptyState, draft: controlledDraft, onDraftChange, showConversationList, chromeStyle, statusPlacement, onNavigateResource, renderConversationLabel, renderMessageContent, renderPresentedFile, renderPendingFile, renderToolInvocation, }: AssistantExperienceViewProps): import("react/jsx-runtime").JSX.Element;
90
+ export declare function AssistantExperienceView({ controller, title, subtitle, placeholder, emptyState, emptyStateSuggestions, draft: controlledDraft, onDraftChange, showConversationList, chromeStyle, statusPlacement, radius, showModelPicker, showNewConversationButton, onNavigateResource, renderConversationLabel, renderMessageContent, renderPresentedFile, renderPendingFile, renderToolInvocation, }: AssistantExperienceViewProps): import("react/jsx-runtime").JSX.Element;
83
91
  export {};