lemma-sdk 0.2.25 → 0.2.27
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 +25 -1
- package/dist/browser/lemma-client.js +176 -2
- package/dist/client.d.ts +2 -0
- package/dist/client.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/namespaces/pod-join-requests.d.ts +16 -0
- package/dist/namespaces/pod-join-requests.js +24 -0
- package/dist/namespaces/pod-members.d.ts +1 -0
- package/dist/namespaces/pod-members.js +3 -0
- package/dist/openapi_client/index.d.ts +9 -1
- package/dist/openapi_client/index.js +4 -0
- package/dist/openapi_client/models/DataStoreFlowStart.d.ts +3 -6
- package/dist/openapi_client/models/DatastoreOperation.d.ts +5 -0
- package/dist/openapi_client/models/DatastoreOperation.js +10 -0
- package/dist/openapi_client/models/FlowInstallEntity.d.ts +2 -2
- package/dist/openapi_client/models/FlowResponse.d.ts +4 -3
- package/dist/openapi_client/models/{FlowStart.d.ts → FlowStart_Input.d.ts} +1 -1
- package/dist/openapi_client/models/FlowStart_Output.d.ts +14 -0
- package/dist/openapi_client/models/FlowStart_Output.js +1 -0
- package/dist/openapi_client/models/PodJoinRequestApproveRequest.d.ts +6 -0
- package/dist/openapi_client/models/PodJoinRequestApproveRequest.js +1 -0
- package/dist/openapi_client/models/PodJoinRequestCreateResponse.d.ts +17 -0
- package/dist/openapi_client/models/PodJoinRequestCreateResponse.js +1 -0
- package/dist/openapi_client/models/PodJoinRequestListResponse.d.ts +7 -0
- package/dist/openapi_client/models/PodJoinRequestListResponse.js +1 -0
- package/dist/openapi_client/models/PodJoinRequestStatus.d.ts +5 -0
- package/dist/openapi_client/models/PodJoinRequestStatus.js +10 -0
- package/dist/openapi_client/models/WorkflowCreateRequest.d.ts +7 -6
- package/dist/openapi_client/models/WorkflowGraphUpdateRequest.d.ts +2 -2
- package/dist/openapi_client/models/WorkflowInstallMode.d.ts +7 -0
- package/dist/openapi_client/models/WorkflowInstallMode.js +12 -0
- package/dist/openapi_client/models/WorkflowUpdateRequest.d.ts +5 -4
- package/dist/openapi_client/services/FilesService.d.ts +1 -1
- package/dist/openapi_client/services/FilesService.js +1 -1
- package/dist/openapi_client/services/PodJoinRequestsService.d.ts +44 -0
- package/dist/openapi_client/services/PodJoinRequestsService.js +93 -0
- package/dist/openapi_client/services/WorkflowsService.d.ts +1 -1
- package/dist/openapi_client/services/WorkflowsService.js +1 -1
- package/dist/react/AuthGuard.d.ts +5 -2
- package/dist/react/AuthGuard.js +126 -3
- package/dist/react/components/AssistantChrome.js +1 -1
- package/dist/react/components/AssistantExperience.d.ts +7 -2
- package/dist/react/components/AssistantExperience.js +272 -32
- package/dist/react/components/assistant-types.d.ts +1 -0
- package/dist/react/styles.css +594 -224
- package/dist/react/useAssistantController.js +2 -1
- package/dist/react/useAssistantRuntime.d.ts +2 -1
- package/dist/react/useAssistantRuntime.js +7 -3
- package/dist/types.d.ts +2 -1
- package/package.json +1 -1
- /package/dist/openapi_client/models/{FlowStart.js → FlowStart_Input.js} +0 -0
|
@@ -29,6 +29,35 @@ function fileNameFromPath(path) {
|
|
|
29
29
|
const parts = normalized.split("/").filter(Boolean);
|
|
30
30
|
return parts[parts.length - 1] || normalized;
|
|
31
31
|
}
|
|
32
|
+
function formatMessageTimestamp(createdAt) {
|
|
33
|
+
if (!(createdAt instanceof Date) || Number.isNaN(createdAt.getTime()))
|
|
34
|
+
return null;
|
|
35
|
+
return {
|
|
36
|
+
text: new Intl.DateTimeFormat(undefined, {
|
|
37
|
+
month: "short",
|
|
38
|
+
day: "numeric",
|
|
39
|
+
hour: "numeric",
|
|
40
|
+
minute: "2-digit",
|
|
41
|
+
}).format(createdAt),
|
|
42
|
+
dateTime: createdAt.toISOString(),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function thinkingLabelsFromSummary(summary) {
|
|
46
|
+
const normalized = summary?.toLowerCase() || "";
|
|
47
|
+
if (normalized.includes("search") || normalized.includes("find") || normalized.includes("query")) {
|
|
48
|
+
return ["Searching…", "Working on it…", "Checking results…"];
|
|
49
|
+
}
|
|
50
|
+
if (normalized.includes("plan") || normalized.includes("step")) {
|
|
51
|
+
return ["Planning next steps…", "Working on it…", "Organizing tasks…"];
|
|
52
|
+
}
|
|
53
|
+
if (normalized.includes("run") || normalized.includes("command") || normalized.includes("exec")) {
|
|
54
|
+
return ["Running checks…", "Working on it…", "Inspecting output…"];
|
|
55
|
+
}
|
|
56
|
+
if (normalized.includes("file") || normalized.includes("present")) {
|
|
57
|
+
return ["Preparing files…", "Working on it…", "Finalizing output…"];
|
|
58
|
+
}
|
|
59
|
+
return ["Working on it…", "Thinking…", "Preparing response…"];
|
|
60
|
+
}
|
|
32
61
|
function toolInvocationKey(tool) {
|
|
33
62
|
return `${tool.toolCallId}:${tool.state}`;
|
|
34
63
|
}
|
|
@@ -222,6 +251,16 @@ function formatCommandPreview(cmd) {
|
|
|
222
251
|
const compact = cmd.replace(/\s+/g, " ").trim();
|
|
223
252
|
return truncateLabel(compact, 64);
|
|
224
253
|
}
|
|
254
|
+
function formatDurationCompact(durationMs) {
|
|
255
|
+
const totalSeconds = Math.max(1, Math.round(durationMs / 1000));
|
|
256
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
257
|
+
const seconds = totalSeconds % 60;
|
|
258
|
+
if (minutes <= 0)
|
|
259
|
+
return `${totalSeconds}s`;
|
|
260
|
+
if (seconds <= 0)
|
|
261
|
+
return `${minutes}m`;
|
|
262
|
+
return `${minutes}m ${seconds}s`;
|
|
263
|
+
}
|
|
225
264
|
function primaryToolArgs(args) {
|
|
226
265
|
const request = asRecord(args.request);
|
|
227
266
|
if (Object.keys(request).length > 0)
|
|
@@ -246,10 +285,13 @@ function commentLabelFromArgs(args) {
|
|
|
246
285
|
const comment = asString(toolArg(args, "comment"));
|
|
247
286
|
return comment ? truncateLabel(comment, 72) : null;
|
|
248
287
|
}
|
|
288
|
+
function toolCallPrimaryLabel(toolName, args) {
|
|
289
|
+
return commentLabelFromArgs(args) || formatToolDisplayName(toolName);
|
|
290
|
+
}
|
|
249
291
|
function formatActiveToolSummary(toolName, args) {
|
|
250
292
|
const lowerName = toolName.toLowerCase();
|
|
293
|
+
const comment = commentLabelFromArgs(args);
|
|
251
294
|
if (lowerName === "exec_command") {
|
|
252
|
-
const comment = commentLabelFromArgs(args);
|
|
253
295
|
if (comment)
|
|
254
296
|
return `Running ${comment}`;
|
|
255
297
|
const cmd = asString(toolArg(args, "cmd"));
|
|
@@ -276,6 +318,8 @@ function formatActiveToolSummary(toolName, args) {
|
|
|
276
318
|
const plan = asArray(toolArg(args, "plan"));
|
|
277
319
|
return `Updating plan (${plan.length} step${plan.length === 1 ? "" : "s"})`;
|
|
278
320
|
}
|
|
321
|
+
if (comment)
|
|
322
|
+
return `Running ${comment}`;
|
|
279
323
|
return `Running ${formatToolDisplayName(toolName)}`;
|
|
280
324
|
}
|
|
281
325
|
function formatToolResultSummary(toolName, args, result) {
|
|
@@ -520,15 +564,33 @@ export function PlanSummaryStrip({ plan, onHide }) {
|
|
|
520
564
|
const hiddenCount = Math.max(0, plan.steps.length - visibleSteps.length);
|
|
521
565
|
return (_jsxs("div", { className: "lemma-assistant-plan-strip", children: [_jsxs("div", { className: "lemma-assistant-plan-strip-header", children: [_jsxs("div", { className: "lemma-assistant-plan-strip-summary", children: [_jsx("span", { className: "lemma-assistant-plan-strip-title", children: "Task plan" }), _jsxs("span", { className: "lemma-assistant-plan-strip-count", children: [plan.completedCount, "/", plan.steps.length, " complete"] }), plan.inProgressCount > 0 ? (_jsxs("span", { className: "lemma-assistant-plan-strip-active", children: [plan.inProgressCount, " active"] })) : null] }), _jsx("button", { type: "button", onClick: onHide, className: "lemma-assistant-plan-strip-hide", children: "Hide" })] }), plan.activeStep ? (_jsxs("div", { className: "lemma-assistant-plan-strip-current", title: plan.activeStep, children: [plan.running ? "Running:" : "Current:", " ", plan.activeStep] })) : null, _jsxs("div", { className: "lemma-assistant-plan-strip-steps", children: [visibleSteps.map((step, index) => (_jsxs("div", { className: "lemma-assistant-plan-strip-step", "data-status": step.status, children: [_jsx("span", { className: cx("lemma-assistant-plan-strip-step-dot", step.status === "completed" && "lemma-assistant-plan-strip-step-dot-completed", step.status === "in_progress" && "lemma-assistant-plan-strip-step-dot-in-progress", step.status === "pending" && "lemma-assistant-plan-strip-step-dot-pending") }), _jsx("span", { className: cx("lemma-assistant-plan-strip-step-label", step.status === "completed" && "lemma-assistant-plan-strip-step-label-completed", step.status === "in_progress" && "lemma-assistant-plan-strip-step-label-in-progress", step.status === "pending" && "lemma-assistant-plan-strip-step-label-pending"), children: step.step })] }, `${step.step}-${index}`))), plan.steps.length > 5 ? (_jsxs("div", { className: "lemma-assistant-plan-strip-footer", children: [_jsx("button", { type: "button", onClick: () => setShowAll((prev) => !prev), className: "lemma-assistant-plan-strip-toggle", children: showAll ? "Show less" : `See all ${plan.steps.length} steps` }), !showAll && hiddenCount > 0 ? (_jsxs("span", { className: "lemma-assistant-plan-strip-hidden-count", children: ["+", hiddenCount, " more"] })) : null] })) : null] })] }));
|
|
522
566
|
}
|
|
523
|
-
|
|
567
|
+
function resolvedThinkingLabels(labels, activeToolSummary) {
|
|
568
|
+
if (labels && labels.length > 0)
|
|
569
|
+
return labels;
|
|
570
|
+
return thinkingLabelsFromSummary(activeToolSummary);
|
|
571
|
+
}
|
|
572
|
+
export function ThinkingIndicator({ activeToolSummary, labels, } = {}) {
|
|
524
573
|
const [show, setShow] = useState(false);
|
|
574
|
+
const labelOptions = useMemo(() => resolvedThinkingLabels(labels, activeToolSummary), [labels, activeToolSummary]);
|
|
575
|
+
const [labelIndex, setLabelIndex] = useState(0);
|
|
525
576
|
useEffect(() => {
|
|
526
577
|
const timer = setTimeout(() => setShow(true), 350);
|
|
527
578
|
return () => clearTimeout(timer);
|
|
528
579
|
}, []);
|
|
580
|
+
useEffect(() => {
|
|
581
|
+
setLabelIndex(0);
|
|
582
|
+
}, [labelOptions]);
|
|
583
|
+
useEffect(() => {
|
|
584
|
+
if (!show || labelOptions.length < 2)
|
|
585
|
+
return;
|
|
586
|
+
const interval = window.setInterval(() => {
|
|
587
|
+
setLabelIndex((prev) => (prev + 1) % labelOptions.length);
|
|
588
|
+
}, 1600);
|
|
589
|
+
return () => clearInterval(interval);
|
|
590
|
+
}, [show, labelOptions]);
|
|
529
591
|
if (!show)
|
|
530
592
|
return null;
|
|
531
|
-
return (_jsx("div", { className: "lemma-assistant-thinking", children: _jsxs("div", { className: "lemma-assistant-thinking-label", children: [_jsx("span", { className: "lemma-assistant-thinking-dot" }), _jsx("span", { className: "lemma-assistant-thinking-text", children: "
|
|
593
|
+
return (_jsx("div", { className: "lemma-assistant-thinking", role: "status", "aria-live": "polite", "aria-label": "Generating response", children: _jsxs("div", { className: "lemma-assistant-thinking-label", children: [_jsx("span", { className: "lemma-assistant-thinking-dot" }), _jsx("span", { className: "lemma-assistant-thinking-text", children: labelOptions[labelIndex] || "Working on it…" })] }) }));
|
|
532
594
|
}
|
|
533
595
|
export const DEFAULT_EMPTY_STATE_SUGGESTIONS = [
|
|
534
596
|
{ text: "Help me get started", icon: "→" },
|
|
@@ -536,11 +598,14 @@ export const DEFAULT_EMPTY_STATE_SUGGESTIONS = [
|
|
|
536
598
|
{ text: "Help me draft a reply", icon: "✎" },
|
|
537
599
|
{ text: "Brainstorm next steps", icon: "⋯" },
|
|
538
600
|
];
|
|
601
|
+
function LemmaMarkIcon({ className }) {
|
|
602
|
+
return (_jsxs("svg", { viewBox: "0 0 20 20", fill: "none", className: className, "aria-hidden": "true", focusable: "false", children: [_jsx("path", { d: "M10 2.5 16.25 5v4.85c0 4.25-2.55 7.05-6.25 8.15-3.7-1.1-6.25-3.9-6.25-8.15V5L10 2.5Z", fill: "currentColor", fillOpacity: "0.18", stroke: "currentColor", strokeWidth: "1.2" }), _jsx("path", { d: "m7.1 10.1 1.8 1.8 4-4.1", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" })] }));
|
|
603
|
+
}
|
|
539
604
|
export function EmptyState({ onSendMessage, suggestions = DEFAULT_EMPTY_STATE_SUGGESTIONS, }) {
|
|
540
|
-
return (_jsxs("div", { className: "lemma-assistant-empty-state", children: [_jsxs("div", { className: "lemma-assistant-empty-state-hero", children: [_jsx("div", { className: "lemma-assistant-empty-state-badge", children: _jsx(
|
|
605
|
+
return (_jsxs("div", { className: "lemma-assistant-empty-state", children: [_jsxs("div", { className: "lemma-assistant-empty-state-hero", children: [_jsx("div", { className: "lemma-assistant-empty-state-badge", children: _jsx(LemmaMarkIcon, { className: "lemma-assistant-empty-state-badge-icon" }) }), _jsx("h4", { className: "lemma-assistant-empty-state-title", children: "How can I help?" }), _jsx("p", { className: "lemma-assistant-empty-state-copy", children: "Ask a question, share context, or start with one of these prompts." })] }), _jsx("div", { className: "lemma-assistant-empty-state-suggestions", children: suggestions.map((suggestion, index) => (_jsxs("button", { onClick: () => onSendMessage(suggestion.text), className: "lemma-assistant-empty-state-suggestion", children: [suggestion.icon ? (_jsx("span", { className: "lemma-assistant-empty-state-suggestion-icon", children: suggestion.icon })) : null, _jsx("span", { className: "lemma-assistant-empty-state-suggestion-text", children: suggestion.text }), _jsx("span", { className: "lemma-assistant-empty-state-suggestion-arrow", children: "\u203A" })] }, `${suggestion.text}-${index}`))) })] }));
|
|
541
606
|
}
|
|
542
607
|
function ReasoningPartCard({ text, isStreaming, durationMs, }) {
|
|
543
|
-
return (_jsxs("details", { className: "lemma-assistant-reasoning", open: isStreaming, children: [
|
|
608
|
+
return (_jsxs("details", { className: "lemma-assistant-reasoning", open: isStreaming, children: [_jsx("summary", { className: "lemma-assistant-reasoning-summary", children: _jsx("span", { className: cx("lemma-assistant-reasoning-label", isStreaming && "lemma-assistant-reasoning-label-streaming"), children: isStreaming ? "Thinking" : `Thought${durationMs ? ` · ${Math.max(1, Math.round(durationMs / 1000))}s` : ""}` }) }), _jsx("div", { className: "lemma-assistant-reasoning-body", children: _jsx("pre", { className: "lemma-assistant-reasoning-text", children: text }) })] }));
|
|
544
609
|
}
|
|
545
610
|
function PresentFilesCard({ filepaths, conversationId, renderPresentedFile, }) {
|
|
546
611
|
const fakeMessage = {
|
|
@@ -561,12 +626,137 @@ function PresentFilesCard({ filepaths, conversationId, renderPresentedFile, }) {
|
|
|
561
626
|
message: fakeMessage,
|
|
562
627
|
}) }, `present-file-${filepath}`))) }));
|
|
563
628
|
}
|
|
629
|
+
function formatToolDetailValue(value) {
|
|
630
|
+
if (value === null)
|
|
631
|
+
return "null";
|
|
632
|
+
if (typeof value === "undefined")
|
|
633
|
+
return "undefined";
|
|
634
|
+
if (typeof value === "string") {
|
|
635
|
+
const trimmed = value.trim();
|
|
636
|
+
return trimmed.length <= 160 ? trimmed : `${trimmed.slice(0, 157)}...`;
|
|
637
|
+
}
|
|
638
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
639
|
+
return String(value);
|
|
640
|
+
}
|
|
641
|
+
if (Array.isArray(value)) {
|
|
642
|
+
if (value.length === 0)
|
|
643
|
+
return "[]";
|
|
644
|
+
const primitives = value.filter((entry) => (typeof entry === "string"
|
|
645
|
+
|| typeof entry === "number"
|
|
646
|
+
|| typeof entry === "boolean"
|
|
647
|
+
|| entry === null));
|
|
648
|
+
if (primitives.length === value.length) {
|
|
649
|
+
const preview = primitives
|
|
650
|
+
.slice(0, 4)
|
|
651
|
+
.map((entry) => (typeof entry === "string" ? `"${entry}"` : String(entry)))
|
|
652
|
+
.join(", ");
|
|
653
|
+
return `[${preview}${value.length > 4 ? ", ..." : ""}]`;
|
|
654
|
+
}
|
|
655
|
+
return `${value.length} item${value.length === 1 ? "" : "s"}`;
|
|
656
|
+
}
|
|
657
|
+
const record = asRecord(value);
|
|
658
|
+
const keys = Object.keys(record);
|
|
659
|
+
if (keys.length === 0)
|
|
660
|
+
return "{}";
|
|
661
|
+
const preview = keys.slice(0, 4).join(", ");
|
|
662
|
+
return `{ ${preview}${keys.length > 4 ? ", ..." : ""} }`;
|
|
663
|
+
}
|
|
664
|
+
function humanizeKey(value) {
|
|
665
|
+
return value
|
|
666
|
+
.replace(/[_-]+/g, " ")
|
|
667
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
668
|
+
.replace(/\s+/g, " ")
|
|
669
|
+
.trim()
|
|
670
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
671
|
+
}
|
|
672
|
+
function isToolPayloadValueMeaningful(value) {
|
|
673
|
+
if (value === null || typeof value === "undefined")
|
|
674
|
+
return false;
|
|
675
|
+
if (typeof value === "string")
|
|
676
|
+
return value.trim().length > 0;
|
|
677
|
+
if (Array.isArray(value))
|
|
678
|
+
return value.length > 0;
|
|
679
|
+
if (typeof value === "object")
|
|
680
|
+
return Object.keys(asRecord(value)).length > 0;
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
function summarizeToolPayload(payload, options) {
|
|
684
|
+
const excluded = new Set((options?.excludeKeys || []).map((key) => key.toLowerCase()));
|
|
685
|
+
return Object.entries(payload)
|
|
686
|
+
.filter(([key, value]) => !excluded.has(key.toLowerCase()) && isToolPayloadValueMeaningful(value))
|
|
687
|
+
.slice(0, 8)
|
|
688
|
+
.map(([key, value]) => ({
|
|
689
|
+
key,
|
|
690
|
+
value: formatToolDetailValue(value),
|
|
691
|
+
}));
|
|
692
|
+
}
|
|
693
|
+
function countSummarizablePayloadEntries(payload, options) {
|
|
694
|
+
const excluded = new Set((options?.excludeKeys || []).map((key) => key.toLowerCase()));
|
|
695
|
+
return Object.entries(payload)
|
|
696
|
+
.filter(([key, value]) => !excluded.has(key.toLowerCase()) && isToolPayloadValueMeaningful(value))
|
|
697
|
+
.length;
|
|
698
|
+
}
|
|
699
|
+
function pickPreferredEntries(entries, preferredKeys, max) {
|
|
700
|
+
const preferredSet = new Set(preferredKeys.map((key) => key.toLowerCase()));
|
|
701
|
+
const preferred = entries.filter((entry) => preferredSet.has(entry.key.toLowerCase()));
|
|
702
|
+
const rest = entries.filter((entry) => !preferredSet.has(entry.key.toLowerCase()));
|
|
703
|
+
return [...preferred, ...rest].slice(0, max);
|
|
704
|
+
}
|
|
564
705
|
function ToolDetailsPanel({ toolName, args, state, result, onNavigateResource, renderToolInvocation, message, activeConversationId, }) {
|
|
565
706
|
const resultData = result || {};
|
|
707
|
+
const primaryLabel = toolCallPrimaryLabel(toolName, args);
|
|
708
|
+
const hasCommentLabel = !!commentLabelFromArgs(args);
|
|
709
|
+
const toolDisplayName = formatToolDisplayName(toolName);
|
|
566
710
|
const canNavigate = state === "result"
|
|
567
711
|
&& resultData.success !== false
|
|
568
712
|
&& typeof resultData.resourceType === "string"
|
|
569
713
|
&& typeof resultData.resourceId === "string";
|
|
714
|
+
const summaryOptions = {
|
|
715
|
+
input: { excludeKeys: ["comment", "request", "wait_config"] },
|
|
716
|
+
output: { excludeKeys: ["success", "completed"] },
|
|
717
|
+
};
|
|
718
|
+
const inputEntries = summarizeToolPayload(args, summaryOptions.input);
|
|
719
|
+
const outputEntries = summarizeToolPayload(resultData, summaryOptions.output);
|
|
720
|
+
const inputHighlights = pickPreferredEntries(inputEntries, [
|
|
721
|
+
"cmd",
|
|
722
|
+
"query",
|
|
723
|
+
"path",
|
|
724
|
+
"filepath",
|
|
725
|
+
"filepaths",
|
|
726
|
+
"table",
|
|
727
|
+
"resource_type",
|
|
728
|
+
"resourceType",
|
|
729
|
+
"resource_id",
|
|
730
|
+
"resourceId",
|
|
731
|
+
], 3);
|
|
732
|
+
const outputHighlights = pickPreferredEntries(outputEntries, [
|
|
733
|
+
"message",
|
|
734
|
+
"stdout",
|
|
735
|
+
"stderr",
|
|
736
|
+
"error",
|
|
737
|
+
"resource_type",
|
|
738
|
+
"resourceType",
|
|
739
|
+
"resource_id",
|
|
740
|
+
"resourceId",
|
|
741
|
+
"exit_code",
|
|
742
|
+
"session_id",
|
|
743
|
+
], 3);
|
|
744
|
+
const detailRows = [
|
|
745
|
+
{
|
|
746
|
+
label: "Tool",
|
|
747
|
+
value: hasCommentLabel ? `${toolDisplayName} (${toolName})` : toolDisplayName,
|
|
748
|
+
},
|
|
749
|
+
...inputHighlights.map((entry) => ({
|
|
750
|
+
label: humanizeKey(entry.key),
|
|
751
|
+
value: entry.value,
|
|
752
|
+
})),
|
|
753
|
+
...outputHighlights.map((entry) => ({
|
|
754
|
+
label: humanizeKey(entry.key),
|
|
755
|
+
value: entry.value,
|
|
756
|
+
})),
|
|
757
|
+
].slice(0, 8);
|
|
758
|
+
const hiddenInputCount = Math.max(0, countSummarizablePayloadEntries(args, summaryOptions.input) - inputEntries.length);
|
|
759
|
+
const hiddenOutputCount = Math.max(0, countSummarizablePayloadEntries(resultData, summaryOptions.output) - outputEntries.length);
|
|
570
760
|
if (renderToolInvocation) {
|
|
571
761
|
return (_jsx("div", { className: "lemma-assistant-tool-details-panel lemma-assistant-tool-details-panel-custom", children: renderToolInvocation({
|
|
572
762
|
invocation: {
|
|
@@ -580,37 +770,56 @@ function ToolDetailsPanel({ toolName, args, state, result, onNavigateResource, r
|
|
|
580
770
|
activeConversationId,
|
|
581
771
|
}) }));
|
|
582
772
|
}
|
|
583
|
-
return (_jsxs("div", { className: "lemma-assistant-tool-details-panel", children: [_jsxs("div", { className: "lemma-assistant-tool-details-header", children: [_jsx("div", { className: "lemma-assistant-tool-details-title", children:
|
|
773
|
+
return (_jsxs("div", { className: "lemma-assistant-tool-details-panel", children: [_jsxs("div", { className: "lemma-assistant-tool-details-header", children: [_jsxs("div", { className: "lemma-assistant-tool-details-heading", children: [_jsx("div", { className: "lemma-assistant-tool-details-title", children: primaryLabel }), hasCommentLabel ? (_jsx("div", { className: "lemma-assistant-tool-details-meta", children: toolDisplayName })) : null] }), canNavigate && onNavigateResource ? (_jsx("button", { type: "button", onClick: () => onNavigateResource(resultData.resourceType, resultData.resourceId, resultData), className: "lemma-assistant-tool-details-link", children: "Open" })) : null] }), _jsx("div", { className: "lemma-assistant-tool-details-stack", children: _jsxs("div", { className: "lemma-assistant-tool-details-section", children: [_jsx("dl", { className: "lemma-assistant-tool-details-list", children: detailRows.map((row, index) => (_jsxs("div", { className: "lemma-assistant-tool-details-list-item", children: [_jsx("dt", { className: "lemma-assistant-tool-details-key", children: row.label }), _jsx("dd", { className: "lemma-assistant-tool-details-value", children: row.value })] }, `${row.label}-${index}`))) }), hiddenInputCount > 0 ? (_jsxs("div", { className: "lemma-assistant-tool-details-more", children: ["+", hiddenInputCount, " more input field", hiddenInputCount === 1 ? "" : "s"] })) : null, hiddenOutputCount > 0 ? (_jsxs("div", { className: "lemma-assistant-tool-details-more", children: ["+", hiddenOutputCount, " more output field", hiddenOutputCount === 1 ? "" : "s"] })) : null, _jsxs("div", { className: "lemma-assistant-tool-details-raw-row", children: [_jsxs("details", { className: "lemma-assistant-tool-details-raw", children: [_jsx("summary", { className: "lemma-assistant-tool-details-raw-summary", children: "Raw input JSON" }), _jsx("div", { className: "lemma-assistant-tool-details-code", children: _jsx("pre", { className: "lemma-assistant-tool-details-code-text", children: JSON.stringify(args, null, 2) }) })] }), Object.keys(resultData).length > 0 ? (_jsxs("details", { className: "lemma-assistant-tool-details-raw", children: [_jsx("summary", { className: "lemma-assistant-tool-details-raw-summary", children: "Raw output JSON" }), _jsx("div", { className: "lemma-assistant-tool-details-code", children: _jsx("pre", { className: "lemma-assistant-tool-details-code-text", children: JSON.stringify(resultData, null, 2) }) })] })) : null] })] }) })] }));
|
|
584
774
|
}
|
|
585
775
|
function InlineToolCall({ invocation, isSelected, onClick, }) {
|
|
586
776
|
const resultData = (invocation.result || {});
|
|
587
777
|
const isExecuting = invocation.state !== "result";
|
|
588
778
|
const isComplete = invocation.state === "result" && resultData.success !== false;
|
|
589
779
|
const isFailed = invocation.state === "result" && resultData.success === false;
|
|
780
|
+
const primaryLabel = toolCallPrimaryLabel(invocation.toolName, invocation.args);
|
|
781
|
+
const statusLabel = isExecuting ? "Working" : isFailed ? "Failed" : "Done";
|
|
782
|
+
const toolMeta = isExecuting ? `${invocation.toolName} · running` : invocation.toolName;
|
|
590
783
|
const summary = isExecuting
|
|
591
|
-
?
|
|
784
|
+
? "Running"
|
|
592
785
|
: isFailed
|
|
593
786
|
? (typeof resultData.error === "string" ? resultData.error : "Tool failed")
|
|
594
787
|
: (formatToolResultSummary(invocation.toolName, invocation.args, resultData) || "Completed");
|
|
595
|
-
return (_jsxs("button", { type: "button", onClick: onClick, className: "lemma-assistant-inline-tool-call", "data-state": isExecuting ? "executing" : isComplete ? "complete" : isFailed ? "failed" : "idle", "data-selected": isSelected ? "true" : "false", children: [
|
|
788
|
+
return (_jsxs("button", { type: "button", onClick: onClick, className: "lemma-assistant-inline-tool-call", "data-state": isExecuting ? "executing" : isComplete ? "complete" : isFailed ? "failed" : "idle", "data-selected": isSelected ? "true" : "false", children: [_jsxs("span", { className: "lemma-assistant-inline-tool-call-rail", "aria-hidden": "true", children: [_jsx("span", { className: "lemma-assistant-inline-tool-call-node" }), _jsx("span", { className: "lemma-assistant-inline-tool-call-stem" })] }), _jsxs("span", { className: "lemma-assistant-inline-tool-call-main", children: [_jsxs("span", { className: "lemma-assistant-inline-tool-call-head", children: [_jsx("span", { className: "lemma-assistant-inline-tool-call-name", children: primaryLabel }), _jsx("span", { className: "lemma-assistant-inline-tool-call-status", children: statusLabel }), _jsx("span", { className: "lemma-assistant-inline-tool-call-caret", children: isSelected ? "⌄" : "›" })] }), _jsx("span", { className: "lemma-assistant-inline-tool-call-meta", children: toolMeta }), _jsx("span", { className: "lemma-assistant-inline-tool-call-summary", children: summary })] })] }));
|
|
596
789
|
}
|
|
597
790
|
function ToolActivityRollup({ detailParts, onNavigateResource, renderToolInvocation, message, activeConversationId, }) {
|
|
598
791
|
const [activeToolCallId, setActiveToolCallId] = useState(null);
|
|
599
792
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
600
793
|
const toolParts = detailParts.filter((part) => part.type === "tool");
|
|
601
794
|
const reasoningParts = detailParts.filter((part) => part.type === "reasoning");
|
|
795
|
+
const totalThoughtDurationMs = reasoningParts.reduce((total, part) => total + (part.durationMs ?? 0), 0);
|
|
796
|
+
const shouldCollapse = detailParts.length > 1;
|
|
602
797
|
const activeInvocation = [...toolParts]
|
|
603
798
|
.reverse()
|
|
604
799
|
.find((part) => part.toolInvocation.state !== "result")
|
|
605
800
|
?.toolInvocation;
|
|
606
801
|
const failedCount = toolParts.filter((part) => (part.toolInvocation.state === "result" && part.toolInvocation.result?.success === false)).length;
|
|
607
802
|
const isWorking = !!activeInvocation || reasoningParts.some((part) => part.state === "streaming");
|
|
803
|
+
const completionSummary = toolParts.length > 0
|
|
804
|
+
? `Completed ${toolParts.length} tool${toolParts.length === 1 ? "" : "s"}`
|
|
805
|
+
: totalThoughtDurationMs > 0
|
|
806
|
+
? `Thought for ${formatDurationCompact(totalThoughtDurationMs)}`
|
|
807
|
+
: "Completed";
|
|
608
808
|
const summary = activeInvocation
|
|
609
809
|
? formatActiveToolSummary(activeInvocation.toolName, activeInvocation.args)
|
|
610
|
-
:
|
|
611
|
-
|
|
810
|
+
: isWorking
|
|
811
|
+
? "Working on it…"
|
|
812
|
+
: `${completionSummary}${failedCount > 0 ? ` · ${failedCount} failed` : ""}`;
|
|
813
|
+
const collapsedSummary = isWorking
|
|
814
|
+
? summary
|
|
815
|
+
: `${totalThoughtDurationMs > 0
|
|
816
|
+
? `Worked for ${formatDurationCompact(totalThoughtDurationMs)}`
|
|
817
|
+
: `Worked through ${detailParts.length} step${detailParts.length === 1 ? "" : "s"}`}${failedCount > 0 ? ` · ${failedCount} failed` : ""}`;
|
|
818
|
+
return (_jsxs("div", { className: "lemma-assistant-tool-rollup", children: [shouldCollapse ? (_jsxs("button", { type: "button", className: "lemma-assistant-tool-rollup-banner", onClick: () => setIsExpanded((prev) => !prev), "aria-expanded": isExpanded, "aria-label": isExpanded ? "Hide tool activity details" : "Show tool activity details", children: [_jsx("span", { className: "lemma-assistant-tool-rollup-banner-line", "aria-hidden": "true" }), _jsxs("span", { className: "lemma-assistant-tool-rollup-banner-copy", children: [isWorking ? _jsx("span", { className: "lemma-assistant-tool-rollup-dot", "aria-hidden": "true" }) : null, _jsx("span", { className: cx("lemma-assistant-tool-rollup-banner-label", isWorking && "lemma-assistant-tool-rollup-banner-label-working"), children: collapsedSummary }), _jsx("span", { className: "lemma-assistant-tool-rollup-banner-caret", "data-expanded": isExpanded ? "true" : "false", "aria-hidden": "true", children: "\u203A" })] }), _jsx("span", { className: "lemma-assistant-tool-rollup-banner-line", "aria-hidden": "true" })] })) : (_jsxs("div", { className: "lemma-assistant-tool-rollup-header", children: [isWorking ? _jsx("span", { className: "lemma-assistant-tool-rollup-dot" }) : null, _jsx("span", { className: cx("lemma-assistant-tool-rollup-summary", isWorking && "lemma-assistant-tool-rollup-summary-working"), children: summary })] })), !shouldCollapse || isExpanded ? (_jsx("div", { className: "lemma-assistant-tool-rollup-details", children: detailParts.map((part) => {
|
|
612
819
|
if (part.type === "reasoning") {
|
|
613
|
-
return (_jsxs("div", { className: "lemma-assistant-tool-rollup-thinking", children: [_jsx("div", { className: "lemma-assistant-tool-rollup-thinking-title", children: part.state === "streaming"
|
|
820
|
+
return (_jsxs("div", { className: "lemma-assistant-tool-rollup-thinking", children: [_jsx("div", { className: "lemma-assistant-tool-rollup-thinking-title", children: part.state === "streaming"
|
|
821
|
+
? "Internal note"
|
|
822
|
+
: `Internal note${part.durationMs ? ` · ${formatDurationCompact(part.durationMs)}` : ""}` }), _jsx("pre", { className: "lemma-assistant-tool-rollup-thinking-text", children: part.text })] }, `thinking-${part.id}`));
|
|
614
823
|
}
|
|
615
824
|
const invocation = part.toolInvocation;
|
|
616
825
|
const isSelected = activeToolCallId === invocation.toolCallId;
|
|
@@ -714,15 +923,16 @@ export function MessageGroup({ message, conversationId, onNavigateResource, onWi
|
|
|
714
923
|
.reverse()
|
|
715
924
|
.find((part) => part.type === "text" && part.text.trim().length > 0)
|
|
716
925
|
?.id;
|
|
926
|
+
const messageTimestamp = formatMessageTimestamp(message.createdAt);
|
|
717
927
|
if (message.role === "user") {
|
|
718
|
-
return (
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
928
|
+
return (_jsxs("div", { className: "lemma-assistant-message lemma-assistant-message-user", children: [_jsx("div", { className: "lemma-assistant-message-user-bubble", children: renderMessageContent({
|
|
929
|
+
message: {
|
|
930
|
+
...message,
|
|
931
|
+
content: message.content,
|
|
932
|
+
parts: undefined,
|
|
933
|
+
toolInvocations: undefined,
|
|
934
|
+
},
|
|
935
|
+
}) }), messageTimestamp ? (_jsx("time", { className: "lemma-assistant-message-timestamp lemma-assistant-message-timestamp-user", dateTime: messageTimestamp.dateTime, children: messageTimestamp.text })) : null] }));
|
|
726
936
|
}
|
|
727
937
|
return (_jsxs("div", { className: "lemma-assistant-message lemma-assistant-message-assistant", children: [showAssistantHeader ? (_jsxs("div", { className: "lemma-assistant-message-header", children: [_jsx("span", { className: "lemma-assistant-message-header-dot" }), "Lemma"] })) : null, _jsxs("div", { className: "lemma-assistant-message-body", children: [blocks.map((block) => {
|
|
728
938
|
if (block.kind === "tools") {
|
|
@@ -760,12 +970,14 @@ export function MessageGroup({ message, conversationId, onNavigateResource, onWi
|
|
|
760
970
|
return null;
|
|
761
971
|
}), presentableFilepaths.length > 0 ? (_jsx(PresentFilesCard, { filepaths: presentableFilepaths, conversationId: conversationId, renderPresentedFile: renderPresentedFile })) : null] })] }));
|
|
762
972
|
}
|
|
763
|
-
export function AssistantExperienceView({ controller, title = "Lemma Assistant", subtitle = "Ask across your workspace and organization.", placeholder = "Message Lemma Assistant", emptyState, emptyStateSuggestions, draft: controlledDraft, onDraftChange, showConversationList = false, chromeStyle = "subtle", statusPlacement = "inline", radius = "
|
|
973
|
+
export function AssistantExperienceView({ controller, title = "Lemma Assistant", subtitle = "Ask across your workspace and organization.", badge, placeholder = "Message Lemma Assistant", emptyState, emptyStateSuggestions, draft: controlledDraft, onDraftChange, showConversationList = false, chromeStyle = "subtle", statusPlacement = "inline", radius = "sm", showModelPicker = false, showNewConversationButton = true, onNavigateResource, renderConversationLabel = defaultConversationLabel, renderMessageContent = defaultMessageContent, renderPresentedFile, renderPendingFile = defaultPendingFile, renderToolInvocation, }) {
|
|
764
974
|
const [draft, setDraft] = useControllableDraft(controlledDraft, onDraftChange);
|
|
765
975
|
const [isPlanHidden, setIsPlanHidden] = useState(false);
|
|
766
976
|
const [dismissedAskToolCallIds, setDismissedAskToolCallIds] = useState([]);
|
|
767
977
|
const [askOverlayState, setAskOverlayState] = useState(null);
|
|
768
978
|
const [isUpdatingModel, setIsUpdatingModel] = useState(false);
|
|
979
|
+
const [showScrollToBottom, setShowScrollToBottom] = useState(false);
|
|
980
|
+
const [thinkingLabelIndex, setThinkingLabelIndex] = useState(0);
|
|
769
981
|
const messagesContainerRef = useRef(null);
|
|
770
982
|
const inputRef = useRef(null);
|
|
771
983
|
const fileInputRef = useRef(null);
|
|
@@ -788,7 +1000,7 @@ export function AssistantExperienceView({ controller, title = "Lemma Assistant",
|
|
|
788
1000
|
return;
|
|
789
1001
|
const minHeight = 48;
|
|
790
1002
|
const maxHeight = 220;
|
|
791
|
-
textarea.style.height = "
|
|
1003
|
+
textarea.style.height = "auto";
|
|
792
1004
|
const nextHeight = Math.min(maxHeight, Math.max(minHeight, textarea.scrollHeight));
|
|
793
1005
|
textarea.style.height = `${nextHeight}px`;
|
|
794
1006
|
textarea.style.overflowY = textarea.scrollHeight > maxHeight ? "auto" : "hidden";
|
|
@@ -801,6 +1013,7 @@ export function AssistantExperienceView({ controller, title = "Lemma Assistant",
|
|
|
801
1013
|
behavior,
|
|
802
1014
|
});
|
|
803
1015
|
isPinnedToBottomRef.current = true;
|
|
1016
|
+
setShowScrollToBottom(false);
|
|
804
1017
|
return;
|
|
805
1018
|
}
|
|
806
1019
|
const el = messagesContainerRef.current;
|
|
@@ -811,13 +1024,16 @@ export function AssistantExperienceView({ controller, title = "Lemma Assistant",
|
|
|
811
1024
|
behavior,
|
|
812
1025
|
});
|
|
813
1026
|
isPinnedToBottomRef.current = true;
|
|
1027
|
+
setShowScrollToBottom(false);
|
|
814
1028
|
}, []);
|
|
815
1029
|
const updatePinnedState = useCallback(() => {
|
|
816
1030
|
const el = messagesContainerRef.current;
|
|
817
1031
|
if (!el)
|
|
818
1032
|
return;
|
|
819
1033
|
const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
820
|
-
|
|
1034
|
+
const isPinned = distanceFromBottom <= 112;
|
|
1035
|
+
isPinnedToBottomRef.current = isPinned;
|
|
1036
|
+
setShowScrollToBottom((prev) => (prev === !isPinned ? prev : !isPinned));
|
|
821
1037
|
if (el.scrollTop > 48)
|
|
822
1038
|
return;
|
|
823
1039
|
if (!controller.hasOlderMessages || controller.isLoadingMessages || controller.isLoadingOlderMessages || loadingOlderFromScrollRef.current)
|
|
@@ -856,8 +1072,10 @@ export function AssistantExperienceView({ controller, title = "Lemma Assistant",
|
|
|
856
1072
|
}, [controller.messages, isConversationBusy, scrollToLatest]);
|
|
857
1073
|
useEffect(() => {
|
|
858
1074
|
isPinnedToBottomRef.current = true;
|
|
1075
|
+
setShowScrollToBottom(false);
|
|
859
1076
|
requestAnimationFrame(() => {
|
|
860
1077
|
scrollToLatest("auto");
|
|
1078
|
+
inputRef.current?.focus();
|
|
861
1079
|
});
|
|
862
1080
|
}, [controller.activeConversationId, scrollToLatest]);
|
|
863
1081
|
useEffect(() => {
|
|
@@ -865,6 +1083,7 @@ export function AssistantExperienceView({ controller, title = "Lemma Assistant",
|
|
|
865
1083
|
}, [draft, resizeComposer]);
|
|
866
1084
|
const displayMessageRows = useMemo(() => buildDisplayMessageRows(controller.messages), [controller.messages]);
|
|
867
1085
|
const activeToolBanner = useMemo(() => getActiveToolBanner(controller.messages), [controller.messages]);
|
|
1086
|
+
const thinkingLabels = useMemo(() => thinkingLabelsFromSummary(activeToolBanner?.summary), [activeToolBanner?.summary]);
|
|
868
1087
|
const planSummary = useMemo(() => latestPlanSummary(controller.messages), [controller.messages]);
|
|
869
1088
|
const pendingAskUserInput = useMemo(() => {
|
|
870
1089
|
const pending = findPendingAskUserInput(controller.messages);
|
|
@@ -896,6 +1115,17 @@ export function AssistantExperienceView({ controller, title = "Lemma Assistant",
|
|
|
896
1115
|
const hasTools = (lastMsg.toolInvocations?.length || 0) > 0 || (lastMsg.parts || []).some((part) => part.type === "tool");
|
|
897
1116
|
return hasText || hasTools;
|
|
898
1117
|
}, [controller.messages]);
|
|
1118
|
+
useEffect(() => {
|
|
1119
|
+
setThinkingLabelIndex(0);
|
|
1120
|
+
}, [activeToolBanner?.summary, isConversationBusy]);
|
|
1121
|
+
useEffect(() => {
|
|
1122
|
+
if (!isConversationBusy || thinkingLabels.length < 2)
|
|
1123
|
+
return;
|
|
1124
|
+
const interval = window.setInterval(() => {
|
|
1125
|
+
setThinkingLabelIndex((prev) => (prev + 1) % thinkingLabels.length);
|
|
1126
|
+
}, 1700);
|
|
1127
|
+
return () => clearInterval(interval);
|
|
1128
|
+
}, [isConversationBusy, thinkingLabels]);
|
|
899
1129
|
const dismissAskOverlay = useCallback((toolCallId) => {
|
|
900
1130
|
setDismissedAskToolCallIds((prev) => (prev.includes(toolCallId) ? prev : [...prev, toolCallId]));
|
|
901
1131
|
setAskOverlayState(null);
|
|
@@ -985,6 +1215,13 @@ export function AssistantExperienceView({ controller, title = "Lemma Assistant",
|
|
|
985
1215
|
scrollToLatest("smooth");
|
|
986
1216
|
await controller.sendMessage(message);
|
|
987
1217
|
}, [controller, isConversationBusy, scrollToLatest, setDraft]);
|
|
1218
|
+
const handleSuggestionSend = useCallback(async (suggestion) => {
|
|
1219
|
+
const message = suggestion.trim();
|
|
1220
|
+
if (!message || isConversationBusy)
|
|
1221
|
+
return;
|
|
1222
|
+
scrollToLatest("smooth");
|
|
1223
|
+
await controller.sendMessage(message);
|
|
1224
|
+
}, [controller, isConversationBusy, scrollToLatest]);
|
|
988
1225
|
const handleUploadSelection = useCallback(async (files) => {
|
|
989
1226
|
const selectedFiles = files ? Array.from(files) : [];
|
|
990
1227
|
if (selectedFiles.length === 0)
|
|
@@ -1027,26 +1264,29 @@ export function AssistantExperienceView({ controller, title = "Lemma Assistant",
|
|
|
1027
1264
|
? effectiveAskOverlayState.answers[effectiveAskOverlayState.currentQuestionIndex] || []
|
|
1028
1265
|
: [];
|
|
1029
1266
|
const canContinueAsk = activeAskAnswers.length > 0;
|
|
1030
|
-
const liveStatusLabel =
|
|
1267
|
+
const liveStatusLabel = thinkingLabels[thinkingLabelIndex] || "Working on it…";
|
|
1031
1268
|
const headerTone = chromeStyle === "elevated" ? "default" : chromeStyle === "flat" ? "flat" : "subtle";
|
|
1032
1269
|
const composerTone = chromeStyle === "flat" ? "flat" : chromeStyle === "subtle" ? "subtle" : "default";
|
|
1033
1270
|
const showInlineStatus = statusPlacement === "inline" && isConversationBusy;
|
|
1034
1271
|
const showComposerStatus = statusPlacement === "composer" && isConversationBusy;
|
|
1272
|
+
const resolvedHeaderBadge = badge === undefined
|
|
1273
|
+
? _jsx(LemmaMarkIcon, { className: "lemma-assistant-experience-header-badge-icon" })
|
|
1274
|
+
: badge;
|
|
1035
1275
|
return (_jsxs("div", { className: "lemma-assistant-experience", "data-chrome-style": chromeStyle, "data-status-placement": statusPlacement, "data-radius": radius, "data-show-model-picker": showModelPicker ? "true" : "false", "data-busy": isConversationBusy ? "true" : "false", "data-has-plan": planSummary ? "true" : "false", "data-has-pending-files": controller.pendingFiles.length > 0 ? "true" : "false", "data-show-conversation-list": showConversationList ? "true" : "false", children: [showConversationList ? (_jsxs("aside", { className: "lemma-assistant-experience-sidebar", children: [_jsx("div", { className: "lemma-assistant-experience-sidebar-header", children: _jsxs("div", { className: "lemma-assistant-experience-sidebar-header-row", children: [_jsxs("div", { className: "lemma-assistant-experience-sidebar-copy", children: [_jsx("div", { className: "lemma-assistant-experience-sidebar-title", children: "Conversations" }), _jsxs("div", { className: "lemma-assistant-experience-sidebar-meta", children: [controller.conversations.length, " total"] })] }), showNewConversationButton ? (_jsx("button", { type: "button", onClick: controller.clearMessages, className: "lemma-assistant-experience-sidebar-new", children: "New" })) : null] }) }), _jsx("div", { className: "lemma-assistant-experience-sidebar-items", children: controller.conversations.map((conversation) => {
|
|
1036
1276
|
const isActive = conversation.id === controller.activeConversationId;
|
|
1037
1277
|
return (_jsxs("button", { type: "button", onClick: () => controller.selectConversation(conversation.id), className: cx("lemma-assistant-experience-sidebar-item", isActive && "lemma-assistant-experience-sidebar-item-active"), children: [_jsx("div", { className: "lemma-assistant-experience-sidebar-item-title", children: renderConversationLabel({ conversation, isActive }) }), _jsx("div", { className: "lemma-assistant-experience-sidebar-item-status", children: (conversation.status || "waiting").toLowerCase() })] }, conversation.id));
|
|
1038
|
-
}) })] })) : null, _jsxs("div", { className: "lemma-assistant-experience-main", children: [_jsxs("div", { className: "lemma-assistant-experience-card", children: [_jsx(AssistantHeader, { className: "lemma-assistant-experience-header", tone: headerTone, title: title, subtitle: subtitle, badge:
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1278
|
+
}) })] })) : null, _jsxs("div", { className: "lemma-assistant-experience-main", children: [_jsxs("div", { className: "lemma-assistant-experience-card", children: [_jsx(AssistantHeader, { className: "lemma-assistant-experience-header", tone: headerTone, title: title, subtitle: subtitle, badge: resolvedHeaderBadge, controls: showModelPicker || showNewConversationButton ? (_jsxs(_Fragment, { children: [showModelPicker ? (_jsx(AssistantModelPicker, { value: controller.conversationModel, options: availableModels, getOptionLabel: (model) => availableModelLabels.get(model) ?? model, onChange: (nextModel) => { void handleModelChange(nextModel); }, disabled: isConversationBusy || isUpdatingModel, autoLabel: "Auto", className: "lemma-assistant-experience-model-picker" })) : null, showNewConversationButton ? (_jsx("button", { type: "button", onClick: controller.clearMessages, title: "New conversation", className: "lemma-assistant-experience-new", children: "\u21BA" })) : null] })) : undefined }), _jsx(AssistantMessageViewport, { className: "lemma-assistant-experience-viewport", ref: messagesContainerRef, onScroll: updatePinnedState, children: _jsxs("div", { className: "lemma-assistant-experience-live-region", "aria-live": "polite", "aria-atomic": "false", children: [controller.messages.length === 0 && !isConversationBusy ? (emptyState || (_jsx(EmptyState, { onSendMessage: (message) => { void handleSuggestionSend(message); }, suggestions: emptyStateSuggestions }))) : null, (controller.isLoadingMessages && controller.messages.length === 0) ? (_jsx("div", { className: "lemma-assistant-experience-loading", children: _jsx("span", { className: "lemma-assistant-experience-loading-text", children: "Loading\u2026" }) })) : null, (controller.isLoadingOlderMessages && controller.messages.length > 0) ? (_jsx("div", { className: "lemma-assistant-experience-loading-older", children: _jsx("span", { className: "lemma-assistant-experience-loading-older-text", children: "Loading older\u2026" }) })) : null, displayMessageRows.map((row, index) => {
|
|
1279
|
+
const previousRow = index > 0 ? displayMessageRows[index - 1] : null;
|
|
1280
|
+
const showAssistantHeader = row.message.role !== "assistant"
|
|
1281
|
+
? false
|
|
1282
|
+
: previousRow?.message.role !== "assistant";
|
|
1283
|
+
const includesLastRawMessage = row.sourceIndexes.includes(controller.messages.length - 1);
|
|
1284
|
+
return (_jsx(MessageGroup, { message: row.message, onNavigateResource: onNavigateResource, onWidgetSendPrompt: handleWidgetSendPrompt, conversationId: controller.activeConversationId, isStreaming: isConversationBusy && includesLastRawMessage && row.message.role === "assistant", showAssistantHeader: showAssistantHeader, renderMessageContent: renderMessageContent, renderPresentedFile: renderPresentedFile, renderToolInvocation: renderToolInvocation }, row.id || index));
|
|
1285
|
+
}), showInlineStatus ? (_jsx("div", { className: "lemma-assistant-experience-inline-status", children: _jsx("div", { className: "lemma-assistant-experience-inline-status-pill", "data-has-content": lastMessageHasContent ? "true" : "false", children: _jsx(AssistantStatusPill, { label: liveStatusLabel, subtle: lastMessageHasContent }) }) })) : null, controller.error ? (_jsx("div", { className: "lemma-assistant-experience-error", children: _jsxs("div", { children: [_jsx("p", { className: "lemma-assistant-experience-error-title", children: "Something went wrong" }), _jsx("p", { className: "lemma-assistant-experience-error-copy", children: controller.error })] }) })) : null, showScrollToBottom ? (_jsx("button", { type: "button", onClick: () => scrollToLatest("smooth"), className: "lemma-assistant-scroll-to-bottom", "aria-label": "Scroll to latest messages", title: "Scroll to latest messages", children: "\u2193" })) : null, (controller.messages.length > 0 || isConversationBusy || !!controller.error) ? (_jsx("div", { "aria-hidden": "true", className: "lemma-assistant-experience-bottom-spacer" })) : null, _jsx("div", { ref: bottomAnchorRef, "aria-hidden": "true", className: "lemma-assistant-experience-bottom-anchor" })] }) })] }), _jsx(AssistantComposer, { className: "lemma-assistant-experience-composer", tone: composerTone, floating: planSummary ? (isPlanHidden ? (_jsxs("button", { type: "button", onClick: () => setIsPlanHidden(false), className: "lemma-assistant-experience-plan-button", children: ["Show plan (", planSummary.completedCount, "/", planSummary.steps.length, ")"] })) : (_jsx(PlanSummaryStrip, { plan: planSummary, onHide: () => setIsPlanHidden(true) }))) : undefined, status: showComposerStatus ? (_jsx(AssistantStatusPill, { label: liveStatusLabel, subtle: true })) : undefined, pendingFiles: controller.pendingFiles.length > 0 ? (_jsx(_Fragment, { children: controller.pendingFiles.map((file) => {
|
|
1046
1286
|
const fileKey = `${file.name}:${file.size}:${file.lastModified}`;
|
|
1047
1287
|
return (_jsx("div", { children: renderPendingFile({
|
|
1048
1288
|
file,
|
|
1049
1289
|
remove: () => controller.removePendingFile(fileKey),
|
|
1050
1290
|
}) }, fileKey));
|
|
1051
|
-
}) })) : undefined, children: activeAskQuestion && effectiveAskOverlayState && pendingAskUserInput ? (_jsx(AssistantAskOverlay, { questionNumber: effectiveAskOverlayState.currentQuestionIndex + 1, totalQuestions: pendingAskUserInput.questions.length, question: activeAskQuestion.question, options: activeAskQuestion.options, selectedOptions: activeAskAnswers, canContinue: canContinueAsk, continueLabel: effectiveAskOverlayState.currentQuestionIndex >= pendingAskUserInput.questions.length - 1 ? "Use answers" : "Continue", onSelectOption: updateAskAnswer, onContinue: activeAskQuestion.type !== "single_select" || pendingAskUserInput.questions.length > 1 ? continueAskQuestions : undefined, onSkip: () => dismissAskOverlay(effectiveAskOverlayState.toolCallId), mode: activeAskQuestion.type })) : (_jsx("div", { className: "lemma-assistant-experience-composer-body", children: _jsxs("div", { className: "lemma-assistant-experience-input-row", children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: "lemma-assistant-experience-file-input", onChange: (event) => { void handleUploadSelection(event.target.files); } }), _jsx("button", { type: "button", onClick: () => fileInputRef.current?.click(), disabled: isConversationBusy || controller.isUploadingFiles, className: "lemma-assistant-experience-upload", "data-disabled": isConversationBusy || controller.isUploadingFiles ? "true" : "false", title: "Upload files", children: controller.isUploadingFiles ? "…" : "+" }), _jsx("textarea", { ref: inputRef, value: draft, onChange: (event) => setDraft(event.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, className: "lemma-assistant-experience-textarea", rows: 1, disabled: isConversationBusy }), _jsx("div", { className: "lemma-assistant-experience-send-wrap", children: _jsx("button", { onClick: isConversationBusy ? controller.stop : () => { void handleSubmit(); }, disabled: !isConversationBusy && !draft.trim(), className: "lemma-assistant-experience-send", "data-state": isConversationBusy ? "busy" : draft.trim() ? "ready" : "idle", title: isConversationBusy ? "Stop generating" : "Send message", children: isConversationBusy ? "■" : "→" }) })] }) })) })] })] }));
|
|
1291
|
+
}) })) : undefined, children: activeAskQuestion && effectiveAskOverlayState && pendingAskUserInput ? (_jsx(AssistantAskOverlay, { questionNumber: effectiveAskOverlayState.currentQuestionIndex + 1, totalQuestions: pendingAskUserInput.questions.length, question: activeAskQuestion.question, options: activeAskQuestion.options, selectedOptions: activeAskAnswers, canContinue: canContinueAsk, continueLabel: effectiveAskOverlayState.currentQuestionIndex >= pendingAskUserInput.questions.length - 1 ? "Use answers" : "Continue", onSelectOption: updateAskAnswer, onContinue: activeAskQuestion.type !== "single_select" || pendingAskUserInput.questions.length > 1 ? continueAskQuestions : undefined, onSkip: () => dismissAskOverlay(effectiveAskOverlayState.toolCallId), mode: activeAskQuestion.type })) : (_jsx("div", { className: "lemma-assistant-experience-composer-body", children: _jsxs("div", { className: "lemma-assistant-experience-input-row", children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: "lemma-assistant-experience-file-input", onChange: (event) => { void handleUploadSelection(event.target.files); } }), _jsx("button", { type: "button", onClick: () => fileInputRef.current?.click(), disabled: isConversationBusy || controller.isUploadingFiles, className: "lemma-assistant-experience-upload", "data-disabled": isConversationBusy || controller.isUploadingFiles ? "true" : "false", title: "Upload files", children: controller.isUploadingFiles ? "…" : "+" }), _jsx("textarea", { ref: inputRef, value: draft, onChange: (event) => setDraft(event.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, className: "lemma-assistant-experience-textarea", rows: 1, disabled: isConversationBusy }), _jsx("div", { className: "lemma-assistant-experience-send-wrap", children: _jsx("button", { onClick: isConversationBusy ? controller.stop : () => { void handleSubmit(); }, disabled: !isConversationBusy && !draft.trim(), className: "lemma-assistant-experience-send", "data-state": isConversationBusy ? "busy" : draft.trim() ? "ready" : "idle", "aria-label": isConversationBusy ? "Stop generating" : "Send message", title: isConversationBusy ? "Stop generating" : "Send message", children: isConversationBusy ? "■" : "→" }) })] }) })) })] })] }));
|
|
1052
1292
|
}
|
|
@@ -65,6 +65,7 @@ export interface EmptyStateSuggestion {
|
|
|
65
65
|
export interface AssistantExperienceCustomizationProps {
|
|
66
66
|
title?: ReactNode;
|
|
67
67
|
subtitle?: ReactNode;
|
|
68
|
+
badge?: ReactNode | null;
|
|
68
69
|
placeholder?: string;
|
|
69
70
|
emptyState?: ReactNode;
|
|
70
71
|
emptyStateSuggestions?: EmptyStateSuggestion[];
|