@springbrand/message-panel 0.1.3-alpha.16 → 0.1.3-alpha.19

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.
@@ -0,0 +1,5 @@
1
+ <svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g id="Icon Variants">
3
+ <path id="Subtract" d="M8.00033 1.33333C11.6821 1.33351 14.6663 4.31854 14.6663 8.00033C14.6662 11.682 11.682 14.6662 8.00033 14.6663C4.31854 14.6663 1.33351 11.6821 1.33333 8.00033C1.33333 4.31843 4.31843 1.33333 8.00033 1.33333ZM11.138 6.19466C10.8776 5.93464 10.4549 5.93442 10.1947 6.19466L7.33236 9.05697L6.13802 7.86165C5.87764 7.60163 5.4549 7.60141 5.19466 7.86165C4.93469 8.1218 4.93502 8.54369 5.19466 8.80404L6.86165 10.471C7.12195 10.7313 7.54367 10.7312 7.80404 10.471L11.138 7.13802C11.3984 6.87767 11.3984 6.45501 11.138 6.19466Z" fill="#FF9938"/>
4
+ </g>
5
+ </svg>
@@ -1,5 +1,5 @@
1
1
  import { Atom, CaretDown, CaretRight, Copy } from "@phosphor-icons/react";
2
- import { memo, useEffect, useId, useState } from "react";
2
+ import { memo, useId, useState } from "react";
3
3
  import { MarkdownMessage } from "./markdown-message";
4
4
  import {
5
5
  getToolCallSummary,
@@ -136,12 +136,10 @@ export const ThinkingTraceRow = memo(function ThinkingTraceRow({
136
136
  reasoning: string;
137
137
  running: boolean;
138
138
  }) {
139
- const [open, setOpen] = useState(running);
139
+ const [open, setOpen] = useState(false);
140
140
  const contentId = useId();
141
141
  const summary = reasoning.replace(/\s+/g, " ").trim();
142
142
 
143
- useEffect(() => setOpen(running), [running]);
144
-
145
143
  return (
146
144
  <div
147
145
  className="flex min-w-0 flex-col items-start gap-2"
@@ -167,7 +165,12 @@ export const ThinkingTraceRow = memo(function ThinkingTraceRow({
167
165
  </span>
168
166
  <span className="flex-shrink-0 text-kumo-subtle">Think</span>
169
167
  <span aria-hidden="true">·</span>
170
- <span className="min-w-0 flex-1 truncate">{summary}</span>
168
+ <span
169
+ className="min-w-0 flex-1 truncate"
170
+ style={running ? { direction: "rtl" } : undefined}
171
+ >
172
+ <bdi dir="auto">{summary}</bdi>
173
+ </span>
171
174
  </button>
172
175
  {open && (
173
176
  <div
@@ -1,7 +1,5 @@
1
1
  import {
2
2
  Brain,
3
- CaretDown,
4
- Check,
5
3
  File as FileIcon,
6
4
  Plug,
7
5
  Plus,
@@ -37,6 +35,10 @@ import {
37
35
  renderFileView,
38
36
  type FileRenderer,
39
37
  } from "../file-view";
38
+ import {
39
+ CloudOsModelSelect,
40
+ type CloudOsModelOption,
41
+ } from "./cloud-os-model-select";
40
42
 
41
43
  const isComposingKeyEvent = (event: {
42
44
  isComposing?: boolean;
@@ -56,13 +58,6 @@ const isComposingKeyEvent = (event: {
56
58
  * assistant-ui ComposerTriggerPopover。
57
59
  */
58
60
 
59
- export interface CloudOsModelOption {
60
- id: string;
61
- name: string;
62
- disabled?: boolean;
63
- disabledLabel?: string;
64
- }
65
-
66
61
  export interface CloudOsComposerCommand {
67
62
  prefix?: "/" | "$";
68
63
  name: string;
@@ -220,11 +215,6 @@ function CloudOsChatInputContent({
220
215
  (value.trim().length > 0 || hasReadyAttachment) &&
221
216
  !hasUnreadyAttachment;
222
217
 
223
- const selectedModelLabel =
224
- selectedModel === null
225
- ? "No agent"
226
- : (models.find((model) => model.id === selectedModel)?.name ?? selectedModel);
227
-
228
218
  useEffect(() => {
229
219
  const element = textareaRef.current;
230
220
  if (element) autoResizeTextarea(element, minRows, maxRows);
@@ -580,51 +570,12 @@ function CloudOsChatInputContent({
580
570
 
581
571
  <div className="ml-auto flex min-w-0 flex-shrink items-center gap-1.5">
582
572
  {onModelChange && (
583
- <DropdownMenu>
584
- <DropdownMenu.Trigger
585
- render={
586
- <button
587
- type="button"
588
- className="group inline-flex h-8 min-w-0 max-w-[180px] cursor-pointer items-center gap-1.5 rounded-lg px-2 text-[13px] leading-5 tracking-[-0.25px] text-kumo-subtle transition-[background-color,color,transform] duration-150 ease-out hover:bg-kumo-tint hover:text-kumo-default focus-visible:bg-kumo-tint focus-visible:text-kumo-default focus-visible:outline-none active:scale-[0.97] data-[state=open]:bg-kumo-tint data-[state=open]:text-kumo-default"
589
- aria-label="Select model"
590
- >
591
- <span className="min-w-0 truncate">{selectedModelLabel}</span>
592
- <CaretDown
593
- size={12}
594
- weight="bold"
595
- className="flex-shrink-0 text-kumo-inactive transition-transform duration-150 ease-out group-data-[state=open]:rotate-180"
596
- />
597
- </button>
598
- }
599
- />
600
- <DropdownMenu.Content
601
- align="end"
602
- className="themed-floating-shadow-lg !min-w-[190px] rounded-2xl border border-kumo-line/70 bg-kumo-base p-1"
603
- >
604
- {models.map((model) => (
605
- <DropdownMenu.Item
606
- key={model.id}
607
- disabled={model.disabled}
608
- onClick={model.disabled ? undefined : () => onModelChange(model.id)}
609
- className="!h-auto rounded-xl !px-2 !py-1.5 text-[12px] leading-4 font-normal tracking-[-0.15px] text-kumo-subtle transition-colors data-highlighted:bg-kumo-tint/70 data-highlighted:text-kumo-default data-disabled:cursor-not-allowed data-disabled:opacity-50"
610
- >
611
- <span className="min-w-0 flex-1 truncate">{model.name}</span>
612
- {model.disabledLabel && (
613
- <span className="ml-3 flex-shrink-0 text-[10px] text-kumo-inactive">
614
- {model.disabledLabel}
615
- </span>
616
- )}
617
- {selectedModel === model.id && (
618
- <Check
619
- size={12}
620
- weight="bold"
621
- className="ml-3 flex-shrink-0 text-kumo-inactive"
622
- />
623
- )}
624
- </DropdownMenu.Item>
625
- ))}
626
- </DropdownMenu.Content>
627
- </DropdownMenu>
573
+ <CloudOsModelSelect
574
+ models={models}
575
+ selectedModel={selectedModel}
576
+ onModelChange={onModelChange}
577
+ variant="cloud-os"
578
+ />
628
579
  )}
629
580
  {!showStopButton && submitControl}
630
581
  {showStopButton ? (
@@ -0,0 +1,105 @@
1
+ import { CaretDown } from "@phosphor-icons/react";
2
+ import { cn } from "../internal/cn";
3
+ import { DropdownMenu } from "../primitives/dropdown-menu";
4
+
5
+ const selectedIcon = new URL("../assets/model-selected.svg", import.meta.url).href;
6
+
7
+ export interface CloudOsModelOption {
8
+ id: string;
9
+ name: string;
10
+ disabled?: boolean;
11
+ disabledLabel?: string;
12
+ }
13
+
14
+ export interface CloudOsModelSelectProps {
15
+ models: readonly CloudOsModelOption[];
16
+ selectedModel?: string | null;
17
+ onModelChange: (modelId: string) => void;
18
+ disabled?: boolean;
19
+ variant?: "default" | "cloud-os";
20
+ }
21
+
22
+ export function CloudOsModelSelect({
23
+ models,
24
+ selectedModel = null,
25
+ onModelChange,
26
+ disabled = false,
27
+ variant = "default",
28
+ }: CloudOsModelSelectProps) {
29
+ if (models.length === 0) return null;
30
+
31
+ const selectedLabel =
32
+ models.find((model) => model.id === selectedModel)?.name ?? "Model";
33
+ const cloudOs = variant === "cloud-os";
34
+
35
+ return (
36
+ <DropdownMenu>
37
+ <DropdownMenu.Trigger
38
+ render={
39
+ <button
40
+ type="button"
41
+ disabled={disabled}
42
+ aria-label="Select model"
43
+ className={cn(
44
+ "group inline-flex h-8 min-w-0 items-center gap-1.5 rounded-lg transition-[background-color,color,transform] duration-150 ease-out focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-40",
45
+ cloudOs
46
+ ? "max-w-[180px] cursor-pointer px-2 text-[13px] leading-5 tracking-[-0.25px] text-kumo-subtle hover:bg-kumo-tint hover:text-kumo-default focus-visible:bg-kumo-tint focus-visible:text-kumo-default active:scale-[0.97] data-[state=open]:bg-kumo-tint data-[state=open]:text-kumo-default"
47
+ : "max-w-[min(11rem,38vw)] px-2.5 text-xs leading-none text-black/55 hover:bg-black/5 hover:text-black focus-visible:ring-2 focus-visible:ring-black/15",
48
+ )}
49
+ >
50
+ <span className="min-w-0 truncate">{selectedLabel}</span>
51
+ <CaretDown
52
+ size={12}
53
+ weight="bold"
54
+ aria-hidden="true"
55
+ className={cn(
56
+ "shrink-0 transition-transform duration-150 ease-out group-data-[state=open]:rotate-180",
57
+ cloudOs ? "text-kumo-inactive" : "text-black/55",
58
+ )}
59
+ />
60
+ </button>
61
+ }
62
+ />
63
+ <DropdownMenu.Content
64
+ align="end"
65
+ side="top"
66
+ sideOffset={8}
67
+ data-slot="model-select-content"
68
+ className="cos-model-select min-w-[12.5rem] rounded-lg border p-2"
69
+ >
70
+ {models.map((model) => (
71
+ <DropdownMenu.Item
72
+ key={model.id}
73
+ disabled={model.disabled}
74
+ onClick={model.disabled ? undefined : () => onModelChange(model.id)}
75
+ data-slot="model-select-item"
76
+ className="cos-model-select__item gap-2 rounded px-2 py-2.5 text-[14px] leading-[1.4] font-normal transition-colors"
77
+ >
78
+ <span
79
+ className="cos-model-select__name min-w-0 flex-1 truncate"
80
+ data-muted={selectedModel === model.id || model.disabled ? "" : undefined}
81
+ >
82
+ {model.name}
83
+ </span>
84
+ {model.disabledLabel ? (
85
+ <span
86
+ className="cos-model-select__plan min-w-0 flex-1 truncate text-right"
87
+ data-disabled={model.disabled ? "" : undefined}
88
+ >
89
+ {model.disabledLabel}
90
+ </span>
91
+ ) : selectedModel === model.id ? (
92
+ <img
93
+ src={selectedIcon}
94
+ alt=""
95
+ aria-hidden="true"
96
+ data-slot="model-selected-icon"
97
+ className="size-4 shrink-0"
98
+ />
99
+ ) : null}
100
+ </DropdownMenu.Item>
101
+ ))}
102
+ </DropdownMenu.Content>
103
+ </DropdownMenu>
104
+ );
105
+ }
package/cloud-os/index.ts CHANGED
@@ -39,8 +39,12 @@ export type {
39
39
  CloudOsCapabilityView,
40
40
  CloudOsChatInputProps,
41
41
  CloudOsComposerCommand,
42
- CloudOsModelOption,
43
42
  } from "./composer/cloud-os-chat-input";
43
+ export { CloudOsModelSelect } from "./composer/cloud-os-model-select";
44
+ export type {
45
+ CloudOsModelOption,
46
+ CloudOsModelSelectProps,
47
+ } from "./composer/cloud-os-model-select";
44
48
 
45
49
  export {
46
50
  CloudOsWorkspacePanel,
@@ -59,7 +59,7 @@ export function WorkshopIconButton({
59
59
  }: WorkshopIconButtonProps) {
60
60
  const toneClassName =
61
61
  tone === "primary"
62
- ? "bg-kumo-contrast text-kumo-inverse enabled:hover:bg-kumo-strong enabled:hover:text-kumo-inverse"
62
+ ? "bg-[var(--primary,var(--color-kumo-brand))] text-[var(--primary-foreground,var(--text-color-kumo-inverse))] enabled:hover:bg-[var(--primary-hover,var(--color-kumo-brand-hover))]"
63
63
  : danger
64
64
  ? "text-kumo-subtle enabled:hover:bg-kumo-danger-tint enabled:hover:text-kumo-danger"
65
65
  : "text-kumo-subtle enabled:hover:bg-kumo-tint enabled:hover:text-kumo-default";
@@ -341,6 +341,54 @@
341
341
  display: none;
342
342
  }
343
343
 
344
+ .cos-model-select {
345
+ --cos-model-select-bg: #fff;
346
+ --cos-model-select-border: rgb(0 0 0 / 7%);
347
+ --cos-model-select-text: #000;
348
+ --cos-model-select-muted: rgb(0 0 0 / 50%);
349
+ --cos-model-select-disabled: rgb(0 0 0 / 30%);
350
+ --cos-model-select-highlight: rgb(0 0 0 / 3%);
351
+ border-color: var(--cos-model-select-border);
352
+ background: var(--cos-model-select-bg);
353
+ box-shadow: 0 8px 14px rgb(0 0 0 / 6%);
354
+ color: var(--cos-model-select-text);
355
+ font-family: "Inter Variable", Inter, sans-serif;
356
+ }
357
+
358
+ .cos-model-select__item[data-highlighted] {
359
+ background: var(--cos-model-select-highlight);
360
+ }
361
+
362
+ .cos-model-select__item[data-disabled] {
363
+ cursor: not-allowed;
364
+ opacity: 1;
365
+ }
366
+
367
+ .cos-model-select__name {
368
+ color: var(--cos-model-select-text);
369
+ }
370
+
371
+ .cos-model-select__name[data-muted] {
372
+ color: var(--cos-model-select-muted);
373
+ }
374
+
375
+ .cos-model-select__plan {
376
+ color: var(--cos-model-select-muted);
377
+ }
378
+
379
+ .cos-model-select__plan[data-disabled] {
380
+ color: var(--cos-model-select-disabled);
381
+ }
382
+
383
+ [data-cloud-os-portal][data-mode="dark"].cos-model-select {
384
+ --cos-model-select-bg: #343434;
385
+ --cos-model-select-border: rgb(255 255 255 / 10%);
386
+ --cos-model-select-text: #fff;
387
+ --cos-model-select-muted: rgb(255 255 255 / 50%);
388
+ --cos-model-select-disabled: rgb(255 255 255 / 30%);
389
+ --cos-model-select-highlight: rgb(255 255 255 / 7%);
390
+ }
391
+
344
392
  .cos-chat-panel {
345
393
  scrollbar-width: thin;
346
394
  scrollbar-color: var(--color-kumo-line) transparent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@springbrand/message-panel",
3
- "version": "0.1.3-alpha.16",
3
+ "version": "0.1.3-alpha.19",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src",