lemma-sdk 0.2.19 → 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,37 +179,7 @@ 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.
183
-
184
- #### Important for Tailwind apps
185
-
186
- If your app uses Tailwind and installs `lemma-sdk` from npm, Tailwind must scan the SDK package too. Otherwise the assistant can look half-styled: native file inputs may appear, layouts can collapse, spacing disappears, and buttons/header chrome look wrong.
187
-
188
- For Tailwind v3, add the SDK package to `content`:
189
-
190
- ```js
191
- // tailwind.config.js
192
- export default {
193
- content: [
194
- "./index.html",
195
- "./src/**/*.{js,ts,jsx,tsx}",
196
- "./node_modules/lemma-sdk/dist/react/**/*.{js,mjs}",
197
- ],
198
- }
199
- ```
200
-
201
- If you are developing against a local checkout of the SDK source instead of the published npm package, scan the source files too:
202
-
203
- ```js
204
- // tailwind.config.js
205
- export default {
206
- content: [
207
- "./index.html",
208
- "./src/**/*.{js,ts,jsx,tsx}",
209
- "../lemma-typescript/src/react/**/*.{ts,tsx}",
210
- ],
211
- }
212
- ```
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.
213
183
 
214
184
  If you alias the package to local SDK source in Vite, make sure the alias points at the React source and stylesheet:
215
185
 
@@ -233,7 +203,6 @@ Quick checklist for developers:
233
203
  - import `lemma-sdk/react/styles.css` once
234
204
  - give the assistant container a real height
235
205
  - if the assistant is inside flex/grid, add `min-height: 0` on the relevant parent
236
- - if you use Tailwind, scan the SDK package or SDK source
237
206
  - if you use `AssistantEmbedded`, pass `theme` directly there
238
207
  - if you use `AssistantExperienceView`, wrap it in `AssistantThemeScope`
239
208
 
@@ -244,11 +213,22 @@ The assistant UI renders markdown by default:
244
213
  - links open safely in a new tab by default
245
214
  - lists, tables, blockquotes, inline code, and fenced code blocks are styled out of the box
246
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
+
247
226
  #### Choose an integration level
248
227
 
249
228
  ##### 1. `AssistantEmbedded` for the fastest setup
250
229
 
251
230
  Use `AssistantEmbedded` when you want a ready-made assistant surface with the SDK defaults.
231
+ This is the recommended integration for most users.
252
232
 
253
233
  ```tsx
254
234
  import "lemma-sdk/react/styles.css";
@@ -264,7 +244,14 @@ function SupportAssistant() {
264
244
  title="Support Assistant"
265
245
  subtitle="Ask questions about this pod."
266
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
+ ]}
267
252
  showConversationList
253
+ showModelPicker={false}
254
+ radius="lg"
268
255
  theme="auto"
269
256
  />
270
257
  </div>
@@ -275,13 +262,18 @@ function SupportAssistant() {
275
262
  Important notes:
276
263
 
277
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
278
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`
279
268
  - the parent container must have a real height; if it lives inside flex/grid, `min-height: 0` is usually needed too
280
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
281
272
 
282
273
  ##### 2. `AssistantExperienceView` for the default UI with your own controller
283
274
 
284
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.
285
277
 
286
278
  ```tsx
287
279
  import "lemma-sdk/react/styles.css";
@@ -305,6 +297,11 @@ function ControlledAssistant() {
305
297
  title="Support Assistant"
306
298
  subtitle="Direct use of the default assistant experience."
307
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
+ ]}
308
305
  showConversationList
309
306
  chromeStyle="subtle"
310
307
  statusPlacement="inline"
@@ -319,13 +316,19 @@ Useful props on `AssistantExperienceView`:
319
316
  - `showConversationList`: show the built-in conversation sidebar
320
317
  - `chromeStyle`: `"elevated" | "subtle" | "flat"`
321
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
322
323
  - `renderMessageContent`: override markdown rendering for custom message content
323
324
  - `renderToolInvocation`: replace the default tool activity renderer
324
325
  - `renderPresentedFile` and `renderPendingFile`: customize attachment rendering
326
+ - prefer this over building from primitives if you still want the SDK's default assistant experience
325
327
 
326
328
  ##### 3. `useAssistantController` + primitives for a custom shell
327
329
 
328
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.
329
332
 
330
333
  ```tsx
331
334
  import "lemma-sdk/react/styles.css";
@@ -335,6 +338,7 @@ import {
335
338
  AssistantMessageViewport,
336
339
  AssistantShellLayout,
337
340
  AssistantThemeScope,
341
+ EmptyState,
338
342
  MessageGroup,
339
343
  PlanSummaryStrip,
340
344
  ThinkingIndicator,
@@ -369,6 +373,19 @@ function CustomAssistantShell() {
369
373
  {activeToolBanner ? <div>{activeToolBanner.summary}</div> : null}
370
374
 
371
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
+
372
389
  {rows.map((row, index) => (
373
390
  <MessageGroup
374
391
  key={row.id}
@@ -411,6 +428,14 @@ Useful primitives exported from `lemma-sdk/react`:
411
428
  - `PlanSummaryStrip`
412
429
  - `ThinkingIndicator`
413
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
+
414
439
  #### Theming
415
440
 
416
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 {};