@springbrand/message-panel 0.1.1 → 0.1.3-alpha.0

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/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@springbrand/message-panel",
3
- "version": "0.1.1",
3
+ "version": "0.1.3-alpha.0",
4
4
  "type": "module",
5
5
  "files": [
6
- "demo",
7
- "src"
6
+ "src",
7
+ "demo"
8
8
  ],
9
9
  "publishConfig": {
10
10
  "access": "public"
@@ -30,8 +30,7 @@
30
30
  "radix-ui": "^1.6.2",
31
31
  "streamdown": "^2.5.0",
32
32
  "thinking-orbs": "0.1.1",
33
- "use-stick-to-bottom": "^1.1.6",
34
- "@springbrand/agent-runtime": "0.1.1"
33
+ "use-stick-to-bottom": "^1.1.6"
35
34
  },
36
35
  "devDependencies": {
37
36
  "@types/react": "^19.2.17",
@@ -72,6 +72,8 @@ export interface CamelChatMessagesProps {
72
72
  isHydrating?: boolean;
73
73
  isRecovering?: boolean;
74
74
  isServerStreaming?: boolean;
75
+ /** Show the opt-in activity bar for recovering or server-streaming states. */
76
+ showActivityStatus?: boolean;
75
77
  hasPendingSteer?: boolean;
76
78
  startedWithPending?: boolean;
77
79
  error?: unknown;
@@ -1282,6 +1284,7 @@ export function CamelChatMessages({
1282
1284
  isHydrating = false,
1283
1285
  isRecovering = false,
1284
1286
  isServerStreaming = false,
1287
+ showActivityStatus = false,
1285
1288
  hasPendingSteer = false,
1286
1289
  startedWithPending = false,
1287
1290
  error,
@@ -1336,7 +1339,7 @@ export function CamelChatMessages({
1336
1339
  data-status={status}
1337
1340
  aria-busy={isActive || isHydrating}
1338
1341
  >
1339
- {(isRecovering || isServerStreaming) && (
1342
+ {showActivityStatus && (isRecovering || isServerStreaming) && (
1340
1343
  <div
1341
1344
  className="absolute inset-x-0 top-0 z-10 flex h-7 items-center justify-center gap-2 bg-background/85 text-xs text-muted-foreground backdrop-blur-sm"
1342
1345
  role="status"
@@ -162,7 +162,7 @@ interface CamelPromptInputProps {
162
162
  status: "ready" | "submitted" | "streaming" | "error";
163
163
  attachments: readonly CamelAttachmentView[];
164
164
  commands: readonly ComposerCommand[];
165
- onFilesSelected: (files: readonly File[]) => void;
165
+ onFilesSelected?: (files: readonly File[]) => void;
166
166
  onAttachmentRemove: (id: string) => void;
167
167
  onAttachmentRetry: (id: string) => void;
168
168
  tools?: ReactNode;
@@ -353,6 +353,7 @@ function CamelPromptInputContent({
353
353
  };
354
354
 
355
355
  const handlePaste = (event: ClipboardEvent<HTMLTextAreaElement>) => {
356
+ if (!onFilesSelected) return;
356
357
  const files = Array.from(event.clipboardData.files);
357
358
  if (files.length > 0) {
358
359
  event.preventDefault();
@@ -362,11 +363,12 @@ function CamelPromptInputContent({
362
363
 
363
364
  const handleFiles = (event: ChangeEvent<HTMLInputElement>) => {
364
365
  const files = Array.from(event.currentTarget.files ?? []);
365
- if (files.length > 0) onFilesSelected(files);
366
+ if (files.length > 0) onFilesSelected?.(files);
366
367
  event.currentTarget.value = "";
367
368
  };
368
369
 
369
370
  const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
371
+ if (!onFilesSelected) return;
370
372
  event.preventDefault();
371
373
  event.stopPropagation();
372
374
  setIsDragOver(true);
@@ -382,6 +384,7 @@ function CamelPromptInputContent({
382
384
  };
383
385
 
384
386
  const handleDrop = (event: DragEvent<HTMLDivElement>) => {
387
+ if (!onFilesSelected) return;
385
388
  event.preventDefault();
386
389
  event.stopPropagation();
387
390
  setIsDragOver(false);
@@ -407,14 +410,16 @@ function CamelPromptInputContent({
407
410
  data-camel-prompt-input=""
408
411
  data-drop-scope="document"
409
412
  >
410
- <input
411
- ref={fileInputRef}
412
- type="file"
413
- multiple
414
- onChange={handleFiles}
415
- className="hidden"
416
- aria-hidden="true"
417
- />
413
+ {onFilesSelected && (
414
+ <input
415
+ ref={fileInputRef}
416
+ type="file"
417
+ multiple
418
+ onChange={handleFiles}
419
+ className="hidden"
420
+ aria-hidden="true"
421
+ />
422
+ )}
418
423
  <ComposerTriggerPopover
419
424
  char="/"
420
425
  {...slash}
@@ -481,16 +486,18 @@ function CamelPromptInputContent({
481
486
  className="justify-between px-3 pb-3"
482
487
  >
483
488
  <div className="flex items-center gap-1.5">
484
- <InputGroupButton
485
- type="button"
486
- size="icon-sm"
487
- variant="ghost"
488
- onClick={() => fileInputRef.current?.click()}
489
- className="rounded-full text-muted-foreground hover:bg-muted hover:text-foreground"
490
- aria-label="Upload file"
491
- >
492
- <PlusIcon className="size-4" />
493
- </InputGroupButton>
489
+ {onFilesSelected && (
490
+ <InputGroupButton
491
+ type="button"
492
+ size="icon-sm"
493
+ variant="ghost"
494
+ onClick={() => fileInputRef.current?.click()}
495
+ className="rounded-full text-muted-foreground hover:bg-muted hover:text-foreground"
496
+ aria-label="Upload file"
497
+ >
498
+ <PlusIcon className="size-4" />
499
+ </InputGroupButton>
500
+ )}
494
501
  {tools}
495
502
  </div>
496
503
  <div className="flex items-center gap-1">
@@ -1,7 +1,8 @@
1
1
  import type { UIMessage } from "ai";
2
- import { USER_STOP_REASON } from "@springbrand/agent-runtime/contracts";
3
2
  import { resolvePartKind, toolNameOfPart } from "../parts/kind";
4
3
 
4
+ const USER_STOP_REASON = "Stopped by user";
5
+
5
6
  type UnknownRecord = Record<string, unknown>;
6
7
 
7
8
  export type CamelPart = UIMessage["parts"][number];
package/src/contracts.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { ComponentType, HTMLAttributes, ReactNode } from "react";
2
- import type { ApprovalDecision } from "@springbrand/agent-runtime/contracts";
3
2
 
4
3
  export type MessagePanelStatus = "ready" | "submitted" | "streaming" | "error";
5
4
  export type MessageRole = "user" | "assistant" | "system" | "tool";
@@ -9,7 +8,7 @@ export interface ToolApprovalView {
9
8
  approved?: boolean;
10
9
  }
11
10
 
12
- export type ToolApprovalDecision = ApprovalDecision["decision"];
11
+ export type ToolApprovalDecision = "deny" | "allow_once" | "allow_level";
13
12
 
14
13
  export interface MessageAdapter<TMessage> {
15
14
  getId: (message: TMessage, index: number) => string;