@springbrand/message-panel 0.1.3-alpha.15 → 0.1.3-alpha.17

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>
@@ -151,7 +151,7 @@ export const ThinkingTraceRow = memo(function ThinkingTraceRow({
151
151
  <button
152
152
  type="button"
153
153
  onClick={() => setOpen((current) => !current)}
154
- className="group -ml-0.5 flex min-w-0 items-center gap-1.5 rounded-xl px-1.5 py-1 text-left text-[14px] leading-5 text-kumo-inactive transition-colors hover:text-kumo-default focus-visible:text-kumo-default focus-visible:outline-none"
154
+ className="group -ml-0.5 flex w-fit max-w-[80%] min-w-0 items-center gap-1.5 rounded-xl px-1.5 py-1 text-left text-[14px] leading-5 text-kumo-inactive transition-colors hover:text-kumo-default focus-visible:text-kumo-default focus-visible:outline-none"
155
155
  aria-expanded={open}
156
156
  aria-controls={contentId}
157
157
  >
@@ -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 ? (
@@ -639,25 +590,27 @@ function CloudOsChatInputContent({
639
590
  </svg>
640
591
  </WorkshopIconButton>
641
592
  ) : (
642
- <WorkshopIconButton
593
+ <button
594
+ type="button"
643
595
  onClick={onSubmit}
644
596
  disabled={!canSend}
645
- tone="primary"
646
- className="!h-8 !w-8 disabled:cursor-not-allowed disabled:opacity-30"
597
+ className="grid size-8 shrink-0 place-items-center rounded-full bg-[var(--primary,var(--color-kumo-brand))] text-[var(--primary-foreground,var(--text-color-kumo-inverse))] shadow-sm transition-colors enabled:hover:bg-[var(--primary-hover,var(--color-kumo-brand-hover))] disabled:cursor-not-allowed disabled:opacity-20"
647
598
  aria-label="Send message"
648
599
  >
649
600
  <svg
650
- width="16"
651
- height="16"
601
+ className="size-4"
652
602
  viewBox="0 0 24 24"
653
603
  fill="none"
654
604
  stroke="currentColor"
655
- strokeWidth="2.5"
605
+ strokeWidth="1.8"
606
+ strokeLinecap="round"
607
+ strokeLinejoin="round"
608
+ aria-hidden="true"
656
609
  >
657
- <line x1="12" y1="19" x2="12" y2="5" />
658
- <polyline points="5 12 12 5 19 12" />
610
+ <path d="m5 12 7-7 7 7" />
611
+ <path d="M12 19V5" />
659
612
  </svg>
660
- </WorkshopIconButton>
613
+ </button>
661
614
  )}
662
615
  </div>
663
616
  </div>
@@ -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,
@@ -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.15",
3
+ "version": "0.1.3-alpha.17",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src",