@nextclaw/ui 0.6.15 → 0.7.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/CHANGELOG.md +12 -0
- package/README.md +2 -0
- package/dist/assets/ChannelsList-DF2U-LY1.js +1 -0
- package/dist/assets/ChatPage-BX39y0U5.js +36 -0
- package/dist/assets/DocBrowser-B9ws5JL7.js +1 -0
- package/dist/assets/{LogoBadge-Cer0jX6t.js → LogoBadge-DvGAzkZ3.js} +1 -1
- package/dist/assets/MarketplacePage-DG5mHWJ8.js +49 -0
- package/dist/assets/ModelConfig-BL_HsOsm.js +1 -0
- package/dist/assets/ProvidersList-CH5z00YT.js +1 -0
- package/dist/assets/RuntimeConfig-BplBgkwo.js +1 -0
- package/dist/assets/SearchConfig-BhaI0fUf.js +1 -0
- package/dist/assets/{SecretsConfig-BnGVZiv4.js → SecretsConfig-CFoimOh9.js} +2 -2
- package/dist/assets/SessionsConfig-BHTAYn9T.js +2 -0
- package/dist/assets/index-BLeJkJ0o.css +1 -0
- package/dist/assets/index-DK4TS5ev.js +8 -0
- package/dist/assets/index-X5J6Mm--.js +1 -0
- package/dist/assets/{index-CkqvHQAt.js → index-uMsNsQX6.js} +1 -1
- package/dist/assets/{label-DkL14Jvl.js → label-D8ly4a2P.js} +1 -1
- package/dist/assets/page-layout-BSYfvwbp.js +1 -0
- package/dist/assets/security-config-DlKEYHNN.js +1 -0
- package/dist/assets/{session-run-status-tZ4ISNj-.js → session-run-status-TkIuGbVw.js} +1 -1
- package/dist/assets/skeleton-CWbsNx2h.js +1 -0
- package/dist/assets/{switch-CgbPbIX3.js → switch-Ce_g9lpN.js} +1 -1
- package/dist/assets/tabs-custom-Cf5azvT5.js +1 -0
- package/dist/assets/useConfirmDialog-A8Ek8Wu7.js +5 -0
- package/dist/assets/vendor-B7ozqnFC.js +412 -0
- package/dist/index.html +3 -3
- package/package.json +9 -5
- package/src/App.tsx +49 -27
- package/src/api/client.ts +1 -0
- package/src/api/config.ts +60 -0
- package/src/api/types.ts +26 -0
- package/src/components/auth/login-page.tsx +69 -0
- package/src/components/chat/ChatConversationPanel.tsx +12 -54
- package/src/components/chat/adapters/chat-input-bar.adapter.test.ts +80 -0
- package/src/components/chat/adapters/chat-input-bar.adapter.ts +329 -0
- package/src/components/chat/adapters/chat-message.adapter.test.ts +137 -0
- package/src/components/chat/adapters/chat-message.adapter.ts +200 -0
- package/src/components/chat/chat-input/chat-input-bar.controller.test.tsx +128 -0
- package/src/components/chat/chat-input/chat-input-bar.controller.ts +105 -0
- package/src/components/chat/containers/chat-input-bar.container.tsx +270 -0
- package/src/components/chat/containers/chat-message-list.container.tsx +67 -0
- package/src/components/chat/index.ts +1 -0
- package/src/components/chat/managers/chat-thread.manager.ts +3 -1
- package/src/components/chat/nextclaw/index.ts +23 -0
- package/src/components/config/runtime-security-card.tsx +276 -0
- package/src/components/config/security-config.tsx +12 -0
- package/src/components/layout/Sidebar.tsx +6 -1
- package/src/components/marketplace/MarketplacePage.test.tsx +170 -0
- package/src/components/marketplace/MarketplacePage.tsx +77 -28
- package/src/hooks/use-auth.ts +111 -0
- package/src/hooks/useMarketplace.ts +9 -0
- package/src/lib/i18n.ts +72 -0
- package/src/test/setup.ts +16 -0
- package/tsconfig.json +3 -2
- package/vite.config.ts +2 -1
- package/vitest.config.ts +16 -0
- package/dist/assets/ChannelsList-DzeVn-JC.js +0 -1
- package/dist/assets/ChatPage-BiFhIm1-.js +0 -36
- package/dist/assets/DocBrowser-By3lF9yN.js +0 -1
- package/dist/assets/MarketplacePage-EZxALdIz.js +0 -49
- package/dist/assets/ModelConfig-AchYxLft.js +0 -1
- package/dist/assets/ProvidersList-BsD-4kKX.js +0 -1
- package/dist/assets/RuntimeConfig-sKOERbFD.js +0 -1
- package/dist/assets/SearchConfig-DAfvDwX6.js +0 -1
- package/dist/assets/SessionsConfig-CzvrKDRs.js +0 -2
- package/dist/assets/card-BAM7vbMg.js +0 -1
- package/dist/assets/index-D9rRqOi8.css +0 -1
- package/dist/assets/index-DJZ5y7t1.js +0 -8
- package/dist/assets/input-BoelTiYL.js +0 -1
- package/dist/assets/page-layout-CERNdqzB.js +0 -1
- package/dist/assets/popover-uwYz3Chm.js +0 -1
- package/dist/assets/tabs-custom-pDyl95el.js +0 -1
- package/dist/assets/useConfirmDialog-DyP6Ac75.js +0 -5
- package/dist/assets/vendor-BKtTvQYU.js +0 -407
- package/src/components/chat/ChatThread.tsx +0 -402
- package/src/components/chat/SkillsPicker.tsx +0 -137
- package/src/components/chat/chat-input/ChatInputBarView.tsx +0 -82
- package/src/components/chat/chat-input/ChatInputBottomToolbar.tsx +0 -83
- package/src/components/chat/chat-input/components/ChatInputModelStateHint.tsx +0 -39
- package/src/components/chat/chat-input/components/ChatInputSelectedSkillsSection.tsx +0 -31
- package/src/components/chat/chat-input/components/ChatInputSlashPanelSection.tsx +0 -112
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputAttachButton.tsx +0 -24
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputModelSelector.tsx +0 -58
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputSendControls.tsx +0 -56
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputSessionTypeSelector.tsx +0 -40
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputThinkingSelector.tsx +0 -74
- package/src/components/chat/chat-input/useChatInputBarController.ts +0 -322
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { SkillsPicker } from '@/components/chat/SkillsPicker';
|
|
2
|
-
import { usePresenter } from '@/components/chat/presenter/chat-presenter-context';
|
|
3
|
-
import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
|
|
4
|
-
import { ChatInputAttachButton } from '@/components/chat/chat-input/components/bottom-toolbar/ChatInputAttachButton';
|
|
5
|
-
import { ChatInputModelSelector } from '@/components/chat/chat-input/components/bottom-toolbar/ChatInputModelSelector';
|
|
6
|
-
import { ChatInputSendControls } from '@/components/chat/chat-input/components/bottom-toolbar/ChatInputSendControls';
|
|
7
|
-
import { ChatInputSessionTypeSelector } from '@/components/chat/chat-input/components/bottom-toolbar/ChatInputSessionTypeSelector';
|
|
8
|
-
import { ChatInputThinkingSelector } from '@/components/chat/chat-input/components/bottom-toolbar/ChatInputThinkingSelector';
|
|
9
|
-
import { t } from '@/lib/i18n';
|
|
10
|
-
|
|
11
|
-
export function ChatInputBottomToolbar() {
|
|
12
|
-
const presenter = usePresenter();
|
|
13
|
-
const snapshot = useChatInputStore((state) => state.snapshot);
|
|
14
|
-
|
|
15
|
-
const hasModelOptions = snapshot.modelOptions.length > 0;
|
|
16
|
-
const isModelOptionsLoading = !snapshot.isProviderStateResolved && !hasModelOptions;
|
|
17
|
-
const selectedModelOption = snapshot.modelOptions.find((option) => option.value === snapshot.selectedModel);
|
|
18
|
-
const shouldShowSessionTypeSelector =
|
|
19
|
-
snapshot.canEditSessionType &&
|
|
20
|
-
(snapshot.sessionTypeOptions.length > 1 ||
|
|
21
|
-
Boolean(snapshot.selectedSessionType && snapshot.selectedSessionType !== 'native'));
|
|
22
|
-
const selectedSessionTypeOption =
|
|
23
|
-
snapshot.sessionTypeOptions.find((option) => option.value === snapshot.selectedSessionType) ??
|
|
24
|
-
(snapshot.selectedSessionType
|
|
25
|
-
? { value: snapshot.selectedSessionType, label: snapshot.selectedSessionType }
|
|
26
|
-
: null);
|
|
27
|
-
const resolvedStopHint =
|
|
28
|
-
snapshot.stopDisabledReason === '__preparing__'
|
|
29
|
-
? t('chatStopPreparing')
|
|
30
|
-
: snapshot.stopDisabledReason?.trim() || t('chatStopUnavailable');
|
|
31
|
-
const selectedModelThinkingCapability = selectedModelOption?.thinkingCapability;
|
|
32
|
-
const thinkingSupportedLevels = selectedModelThinkingCapability?.supported ?? [];
|
|
33
|
-
const shouldShowThinkingSelector = thinkingSupportedLevels.length > 0;
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<div className="flex items-center justify-between px-3 pb-3">
|
|
37
|
-
<div className="flex items-center gap-1">
|
|
38
|
-
<SkillsPicker
|
|
39
|
-
records={snapshot.skillRecords}
|
|
40
|
-
isLoading={snapshot.isSkillsLoading}
|
|
41
|
-
selectedSkills={snapshot.selectedSkills}
|
|
42
|
-
onSelectedSkillsChange={presenter.chatInputManager.selectSkills}
|
|
43
|
-
/>
|
|
44
|
-
<ChatInputSessionTypeSelector
|
|
45
|
-
shouldShowSessionTypeSelector={shouldShowSessionTypeSelector}
|
|
46
|
-
selectedSessionType={snapshot.selectedSessionType}
|
|
47
|
-
selectedSessionTypeOption={selectedSessionTypeOption}
|
|
48
|
-
sessionTypeOptions={snapshot.sessionTypeOptions}
|
|
49
|
-
onSelectedSessionTypeChange={presenter.chatInputManager.selectSessionType}
|
|
50
|
-
canEditSessionType={snapshot.canEditSessionType}
|
|
51
|
-
/>
|
|
52
|
-
<ChatInputModelSelector
|
|
53
|
-
modelOptions={snapshot.modelOptions}
|
|
54
|
-
selectedModel={snapshot.selectedModel}
|
|
55
|
-
selectedModelOption={selectedModelOption}
|
|
56
|
-
onSelectedModelChange={presenter.chatInputManager.selectModel}
|
|
57
|
-
isModelOptionsLoading={isModelOptionsLoading}
|
|
58
|
-
hasModelOptions={hasModelOptions}
|
|
59
|
-
/>
|
|
60
|
-
{shouldShowThinkingSelector ? (
|
|
61
|
-
<ChatInputThinkingSelector
|
|
62
|
-
supportedLevels={thinkingSupportedLevels}
|
|
63
|
-
selectedThinkingLevel={snapshot.selectedThinkingLevel}
|
|
64
|
-
defaultThinkingLevel={selectedModelThinkingCapability?.default ?? null}
|
|
65
|
-
onSelectedThinkingLevelChange={presenter.chatInputManager.selectThinkingLevel}
|
|
66
|
-
/>
|
|
67
|
-
) : null}
|
|
68
|
-
<ChatInputAttachButton />
|
|
69
|
-
</div>
|
|
70
|
-
<ChatInputSendControls
|
|
71
|
-
sendError={snapshot.sendError}
|
|
72
|
-
draft={snapshot.draft}
|
|
73
|
-
hasModelOptions={hasModelOptions}
|
|
74
|
-
sessionTypeUnavailable={snapshot.sessionTypeUnavailable}
|
|
75
|
-
isSending={snapshot.isSending}
|
|
76
|
-
canStopGeneration={snapshot.canStopGeneration}
|
|
77
|
-
resolvedStopHint={resolvedStopHint}
|
|
78
|
-
onSend={presenter.chatInputManager.send}
|
|
79
|
-
onStop={presenter.chatInputManager.stop}
|
|
80
|
-
/>
|
|
81
|
-
</div>
|
|
82
|
-
);
|
|
83
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { t } from '@/lib/i18n';
|
|
2
|
-
|
|
3
|
-
type ChatInputModelStateHintProps = {
|
|
4
|
-
isModelOptionsLoading: boolean;
|
|
5
|
-
isModelOptionsEmpty: boolean;
|
|
6
|
-
onGoToProviders: () => void;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export function ChatInputModelStateHint(props: ChatInputModelStateHintProps) {
|
|
10
|
-
if (!props.isModelOptionsLoading && !props.isModelOptionsEmpty) {
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (props.isModelOptionsLoading) {
|
|
15
|
-
return (
|
|
16
|
-
<div className="px-4 pb-2">
|
|
17
|
-
<div className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-3 py-2">
|
|
18
|
-
<span className="h-3 w-28 animate-pulse rounded bg-gray-200" />
|
|
19
|
-
<span className="h-3 w-16 animate-pulse rounded bg-gray-200" />
|
|
20
|
-
</div>
|
|
21
|
-
</div>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div className="px-4 pb-2">
|
|
27
|
-
<div className="inline-flex items-center gap-2 rounded-lg border border-amber-200 bg-amber-50 px-3 py-1.5 text-xs text-amber-800">
|
|
28
|
-
<span>{t('chatModelNoOptions')}</span>
|
|
29
|
-
<button
|
|
30
|
-
type="button"
|
|
31
|
-
onClick={props.onGoToProviders}
|
|
32
|
-
className="font-semibold text-amber-900 underline-offset-2 hover:underline"
|
|
33
|
-
>
|
|
34
|
-
{t('chatGoConfigureProvider')}
|
|
35
|
-
</button>
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
|
-
);
|
|
39
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { X } from 'lucide-react';
|
|
2
|
-
|
|
3
|
-
type ChatInputSelectedSkillsSectionProps = {
|
|
4
|
-
records: Array<{ spec: string; label: string }>;
|
|
5
|
-
selectedSkills: string[];
|
|
6
|
-
onSelectedSkillsChange: (next: string[]) => void;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export function ChatInputSelectedSkillsSection(props: ChatInputSelectedSkillsSectionProps) {
|
|
10
|
-
if (props.records.length === 0) {
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<div className="px-4 pb-2">
|
|
16
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
17
|
-
{props.records.map((record) => (
|
|
18
|
-
<button
|
|
19
|
-
key={record.spec}
|
|
20
|
-
type="button"
|
|
21
|
-
onClick={() => props.onSelectedSkillsChange(props.selectedSkills.filter((skill) => skill !== record.spec))}
|
|
22
|
-
className="inline-flex max-w-[200px] items-center gap-1.5 rounded-full bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary"
|
|
23
|
-
>
|
|
24
|
-
<span className="truncate">{record.label}</span>
|
|
25
|
-
<X className="h-3 w-3 shrink-0" />
|
|
26
|
-
</button>
|
|
27
|
-
))}
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import type { MutableRefObject } from 'react';
|
|
2
|
-
import type { ChatInputBarSlashItem } from '@/components/chat/chat-input.types';
|
|
3
|
-
import { Popover, PopoverAnchor, PopoverContent } from '@/components/ui/popover';
|
|
4
|
-
import { t } from '@/lib/i18n';
|
|
5
|
-
|
|
6
|
-
type ChatInputSlashPanelSectionProps = {
|
|
7
|
-
slashAnchorRef: MutableRefObject<HTMLDivElement | null>;
|
|
8
|
-
slashListRef: MutableRefObject<HTMLDivElement | null>;
|
|
9
|
-
isSlashPanelOpen: boolean;
|
|
10
|
-
isSlashPanelLoading: boolean;
|
|
11
|
-
resolvedSlashPanelWidth?: number;
|
|
12
|
-
skillSlashItems: ChatInputBarSlashItem[];
|
|
13
|
-
activeSlashIndex: number;
|
|
14
|
-
activeSlashItem: ChatInputBarSlashItem | null;
|
|
15
|
-
onSelectSlashItem: (item: ChatInputBarSlashItem) => void;
|
|
16
|
-
onSlashPanelOpenChange: (open: boolean) => void;
|
|
17
|
-
onSetActiveSlashIndex: (index: number) => void;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export function ChatInputSlashPanelSection({
|
|
21
|
-
slashAnchorRef,
|
|
22
|
-
slashListRef,
|
|
23
|
-
isSlashPanelOpen,
|
|
24
|
-
isSlashPanelLoading,
|
|
25
|
-
resolvedSlashPanelWidth,
|
|
26
|
-
skillSlashItems,
|
|
27
|
-
activeSlashIndex,
|
|
28
|
-
activeSlashItem,
|
|
29
|
-
onSelectSlashItem,
|
|
30
|
-
onSlashPanelOpenChange,
|
|
31
|
-
onSetActiveSlashIndex
|
|
32
|
-
}: ChatInputSlashPanelSectionProps) {
|
|
33
|
-
return (
|
|
34
|
-
<Popover open={isSlashPanelOpen} onOpenChange={onSlashPanelOpenChange}>
|
|
35
|
-
<PopoverAnchor asChild>
|
|
36
|
-
<div ref={slashAnchorRef} className="pointer-events-none absolute left-3 right-3 bottom-full h-0" />
|
|
37
|
-
</PopoverAnchor>
|
|
38
|
-
<PopoverContent
|
|
39
|
-
side="top"
|
|
40
|
-
align="start"
|
|
41
|
-
sideOffset={10}
|
|
42
|
-
className="z-[70] max-w-[calc(100vw-1.5rem)] overflow-hidden rounded-2xl border border-gray-200 bg-white/95 p-0 shadow-2xl backdrop-blur-md"
|
|
43
|
-
onOpenAutoFocus={(event) => event.preventDefault()}
|
|
44
|
-
style={resolvedSlashPanelWidth ? { width: `${resolvedSlashPanelWidth}px` } : undefined}
|
|
45
|
-
>
|
|
46
|
-
<div className="grid min-h-[240px] grid-cols-[minmax(220px,300px)_minmax(0,1fr)]">
|
|
47
|
-
<div
|
|
48
|
-
ref={slashListRef}
|
|
49
|
-
className="max-h-[320px] overflow-y-auto border-r border-gray-200 p-2.5 custom-scrollbar"
|
|
50
|
-
>
|
|
51
|
-
{isSlashPanelLoading ? (
|
|
52
|
-
<div className="p-2 text-xs text-gray-500">{t('chatSlashLoading')}</div>
|
|
53
|
-
) : (
|
|
54
|
-
<>
|
|
55
|
-
<div className="mb-2 px-2 text-[11px] font-semibold uppercase tracking-wide text-gray-500">
|
|
56
|
-
{t('chatSlashSectionSkills')}
|
|
57
|
-
</div>
|
|
58
|
-
{skillSlashItems.length === 0 ? (
|
|
59
|
-
<div className="px-2 text-xs text-gray-400">{t('chatSlashNoResult')}</div>
|
|
60
|
-
) : (
|
|
61
|
-
<div className="space-y-1">
|
|
62
|
-
{skillSlashItems.map((item, index) => {
|
|
63
|
-
const isActive = index === activeSlashIndex;
|
|
64
|
-
return (
|
|
65
|
-
<button
|
|
66
|
-
key={item.key}
|
|
67
|
-
type="button"
|
|
68
|
-
data-slash-index={index}
|
|
69
|
-
onMouseEnter={() => onSetActiveSlashIndex(index)}
|
|
70
|
-
onClick={() => onSelectSlashItem(item)}
|
|
71
|
-
className={`flex w-full items-start gap-2 rounded-lg px-2 py-1.5 text-left transition ${
|
|
72
|
-
isActive ? 'bg-gray-100 text-gray-900' : 'text-gray-700 hover:bg-gray-50'
|
|
73
|
-
}`}
|
|
74
|
-
>
|
|
75
|
-
<span className="truncate text-xs font-semibold">{item.title}</span>
|
|
76
|
-
<span className="truncate text-xs text-gray-500">{item.subtitle}</span>
|
|
77
|
-
</button>
|
|
78
|
-
);
|
|
79
|
-
})}
|
|
80
|
-
</div>
|
|
81
|
-
)}
|
|
82
|
-
</>
|
|
83
|
-
)}
|
|
84
|
-
</div>
|
|
85
|
-
<div className="max-w-[320px] p-3.5">
|
|
86
|
-
{activeSlashItem ? (
|
|
87
|
-
<div className="space-y-3">
|
|
88
|
-
<div className="flex items-center gap-2">
|
|
89
|
-
<span className="inline-flex rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-semibold text-primary">
|
|
90
|
-
{activeSlashItem.subtitle}
|
|
91
|
-
</span>
|
|
92
|
-
<span className="text-sm font-semibold text-gray-900">{activeSlashItem.title}</span>
|
|
93
|
-
</div>
|
|
94
|
-
<p className="text-xs leading-5 text-gray-600">{activeSlashItem.description}</p>
|
|
95
|
-
<div className="space-y-1">
|
|
96
|
-
{activeSlashItem.detailLines.map((line) => (
|
|
97
|
-
<div key={line} className="rounded-md bg-gray-50 px-2 py-1 text-[11px] text-gray-600">
|
|
98
|
-
{line}
|
|
99
|
-
</div>
|
|
100
|
-
))}
|
|
101
|
-
</div>
|
|
102
|
-
<div className="pt-1 text-[11px] text-gray-500">{t('chatSlashSkillHint')}</div>
|
|
103
|
-
</div>
|
|
104
|
-
) : (
|
|
105
|
-
<div className="text-xs text-gray-500">{t('chatSlashHint')}</div>
|
|
106
|
-
)}
|
|
107
|
-
</div>
|
|
108
|
-
</div>
|
|
109
|
-
</PopoverContent>
|
|
110
|
-
</Popover>
|
|
111
|
-
);
|
|
112
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
2
|
-
import { t } from '@/lib/i18n';
|
|
3
|
-
import { Paperclip } from 'lucide-react';
|
|
4
|
-
|
|
5
|
-
export function ChatInputAttachButton() {
|
|
6
|
-
return (
|
|
7
|
-
<TooltipProvider>
|
|
8
|
-
<Tooltip>
|
|
9
|
-
<TooltipTrigger asChild>
|
|
10
|
-
<button
|
|
11
|
-
type="button"
|
|
12
|
-
disabled
|
|
13
|
-
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-gray-400 cursor-not-allowed"
|
|
14
|
-
>
|
|
15
|
-
<Paperclip className="h-4 w-4" />
|
|
16
|
-
</button>
|
|
17
|
-
</TooltipTrigger>
|
|
18
|
-
<TooltipContent side="top">
|
|
19
|
-
<p className="text-xs">{t('chatInputAttachComingSoon')}</p>
|
|
20
|
-
</TooltipContent>
|
|
21
|
-
</Tooltip>
|
|
22
|
-
</TooltipProvider>
|
|
23
|
-
);
|
|
24
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { ChatModelOption } from '@/components/chat/chat-input.types';
|
|
2
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
|
3
|
-
import { t } from '@/lib/i18n';
|
|
4
|
-
import { Sparkles } from 'lucide-react';
|
|
5
|
-
|
|
6
|
-
type ChatInputModelSelectorProps = {
|
|
7
|
-
modelOptions: ChatModelOption[];
|
|
8
|
-
selectedModel: string;
|
|
9
|
-
selectedModelOption?: ChatModelOption;
|
|
10
|
-
onSelectedModelChange: (value: string) => void;
|
|
11
|
-
isModelOptionsLoading: boolean;
|
|
12
|
-
hasModelOptions: boolean;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export function ChatInputModelSelector(props: ChatInputModelSelectorProps) {
|
|
16
|
-
return (
|
|
17
|
-
<Select
|
|
18
|
-
value={props.hasModelOptions ? props.selectedModel : undefined}
|
|
19
|
-
onValueChange={props.onSelectedModelChange}
|
|
20
|
-
disabled={!props.hasModelOptions}
|
|
21
|
-
>
|
|
22
|
-
<SelectTrigger className="h-8 w-auto min-w-[220px] rounded-lg border-0 bg-transparent shadow-none text-xs font-medium text-gray-600 hover:bg-gray-100 focus:ring-0 px-3">
|
|
23
|
-
{props.selectedModelOption ? (
|
|
24
|
-
<div className="flex min-w-0 items-center gap-2 text-left">
|
|
25
|
-
<Sparkles className="h-3.5 w-3.5 shrink-0 text-primary" />
|
|
26
|
-
<span className="truncate text-xs font-semibold text-gray-700">
|
|
27
|
-
{props.selectedModelOption.providerLabel}/{props.selectedModelOption.modelLabel}
|
|
28
|
-
</span>
|
|
29
|
-
</div>
|
|
30
|
-
) : props.isModelOptionsLoading ? (
|
|
31
|
-
<div className="h-3 w-24 animate-pulse rounded bg-gray-200" />
|
|
32
|
-
) : (
|
|
33
|
-
<SelectValue placeholder={t('chatSelectModel')} />
|
|
34
|
-
)}
|
|
35
|
-
</SelectTrigger>
|
|
36
|
-
<SelectContent className="w-[320px]">
|
|
37
|
-
{props.modelOptions.length === 0 &&
|
|
38
|
-
(props.isModelOptionsLoading ? (
|
|
39
|
-
<div className="space-y-2 px-3 py-2">
|
|
40
|
-
<div className="h-3 w-36 animate-pulse rounded bg-gray-200" />
|
|
41
|
-
<div className="h-3 w-28 animate-pulse rounded bg-gray-200" />
|
|
42
|
-
<div className="h-3 w-32 animate-pulse rounded bg-gray-200" />
|
|
43
|
-
</div>
|
|
44
|
-
) : (
|
|
45
|
-
<div className="px-3 py-2 text-xs text-gray-500">{t('chatModelNoOptions')}</div>
|
|
46
|
-
))}
|
|
47
|
-
{props.modelOptions.map((option) => (
|
|
48
|
-
<SelectItem key={option.value} value={option.value} className="py-2">
|
|
49
|
-
<div className="flex min-w-0 flex-col gap-0.5">
|
|
50
|
-
<span className="truncate text-xs font-semibold text-gray-800">{option.modelLabel}</span>
|
|
51
|
-
<span className="truncate text-[11px] text-gray-500">{option.providerLabel}</span>
|
|
52
|
-
</div>
|
|
53
|
-
</SelectItem>
|
|
54
|
-
))}
|
|
55
|
-
</SelectContent>
|
|
56
|
-
</Select>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { Button } from '@/components/ui/button';
|
|
2
|
-
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
3
|
-
import { ArrowUp, Square } from 'lucide-react';
|
|
4
|
-
|
|
5
|
-
type ChatInputSendControlsProps = {
|
|
6
|
-
sendError?: string | null;
|
|
7
|
-
draft: string;
|
|
8
|
-
hasModelOptions: boolean;
|
|
9
|
-
sessionTypeUnavailable: boolean;
|
|
10
|
-
isSending: boolean;
|
|
11
|
-
canStopGeneration: boolean;
|
|
12
|
-
resolvedStopHint: string;
|
|
13
|
-
onSend: () => Promise<void> | void;
|
|
14
|
-
onStop: () => Promise<void> | void;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export function ChatInputSendControls(props: ChatInputSendControlsProps) {
|
|
18
|
-
return (
|
|
19
|
-
<div className="flex flex-col items-end gap-1">
|
|
20
|
-
{props.sendError?.trim() && <div className="max-w-[420px] text-right text-[11px] text-red-600">{props.sendError}</div>}
|
|
21
|
-
<div className="flex items-center gap-2">
|
|
22
|
-
{props.isSending ? (
|
|
23
|
-
props.canStopGeneration ? (
|
|
24
|
-
<Button size="icon" variant="outline" className="h-8 w-8 rounded-full" onClick={() => void props.onStop()}>
|
|
25
|
-
<Square className="h-3 w-3 fill-current" />
|
|
26
|
-
</Button>
|
|
27
|
-
) : (
|
|
28
|
-
<TooltipProvider>
|
|
29
|
-
<Tooltip>
|
|
30
|
-
<TooltipTrigger asChild>
|
|
31
|
-
<span>
|
|
32
|
-
<Button size="icon" variant="outline" className="h-8 w-8 rounded-full" disabled>
|
|
33
|
-
<Square className="h-3 w-3 fill-current" />
|
|
34
|
-
</Button>
|
|
35
|
-
</span>
|
|
36
|
-
</TooltipTrigger>
|
|
37
|
-
<TooltipContent side="top">
|
|
38
|
-
<p className="text-xs">{props.resolvedStopHint}</p>
|
|
39
|
-
</TooltipContent>
|
|
40
|
-
</Tooltip>
|
|
41
|
-
</TooltipProvider>
|
|
42
|
-
)
|
|
43
|
-
) : (
|
|
44
|
-
<Button
|
|
45
|
-
size="icon"
|
|
46
|
-
className="h-8 w-8 rounded-full"
|
|
47
|
-
onClick={() => void props.onSend()}
|
|
48
|
-
disabled={props.draft.trim().length === 0 || !props.hasModelOptions || props.sessionTypeUnavailable}
|
|
49
|
-
>
|
|
50
|
-
<ArrowUp className="h-5 w-5" />
|
|
51
|
-
</Button>
|
|
52
|
-
)}
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
);
|
|
56
|
-
}
|
package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputSessionTypeSelector.tsx
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
|
2
|
-
import { t } from '@/lib/i18n';
|
|
3
|
-
|
|
4
|
-
type ChatInputSessionTypeSelectorProps = {
|
|
5
|
-
shouldShowSessionTypeSelector: boolean;
|
|
6
|
-
selectedSessionType?: string;
|
|
7
|
-
selectedSessionTypeOption: { value: string; label: string } | null;
|
|
8
|
-
sessionTypeOptions: Array<{ value: string; label: string }>;
|
|
9
|
-
onSelectedSessionTypeChange: (value: string) => void;
|
|
10
|
-
canEditSessionType: boolean;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export function ChatInputSessionTypeSelector(props: ChatInputSessionTypeSelectorProps) {
|
|
14
|
-
if (!props.shouldShowSessionTypeSelector) {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<Select
|
|
20
|
-
value={props.selectedSessionType}
|
|
21
|
-
onValueChange={props.onSelectedSessionTypeChange}
|
|
22
|
-
disabled={!props.canEditSessionType}
|
|
23
|
-
>
|
|
24
|
-
<SelectTrigger className="h-8 w-auto min-w-[140px] rounded-lg border-0 bg-transparent shadow-none text-xs font-medium text-gray-600 hover:bg-gray-100 focus:ring-0 px-3">
|
|
25
|
-
{props.selectedSessionTypeOption ? (
|
|
26
|
-
<span className="truncate text-xs font-semibold text-gray-700">{props.selectedSessionTypeOption.label}</span>
|
|
27
|
-
) : (
|
|
28
|
-
<SelectValue placeholder={t('chatSessionTypeLabel')} />
|
|
29
|
-
)}
|
|
30
|
-
</SelectTrigger>
|
|
31
|
-
<SelectContent className="w-[220px]">
|
|
32
|
-
{props.sessionTypeOptions.map((option) => (
|
|
33
|
-
<SelectItem key={option.value} value={option.value} className="py-2">
|
|
34
|
-
<span className="truncate text-xs font-semibold text-gray-800">{option.label}</span>
|
|
35
|
-
</SelectItem>
|
|
36
|
-
))}
|
|
37
|
-
</SelectContent>
|
|
38
|
-
</Select>
|
|
39
|
-
);
|
|
40
|
-
}
|
package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputThinkingSelector.tsx
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import type { ThinkingLevel } from '@/api/types';
|
|
2
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
|
3
|
-
import { t } from '@/lib/i18n';
|
|
4
|
-
import { Brain } from 'lucide-react';
|
|
5
|
-
|
|
6
|
-
type ChatInputThinkingSelectorProps = {
|
|
7
|
-
supportedLevels: ThinkingLevel[];
|
|
8
|
-
selectedThinkingLevel: ThinkingLevel | null;
|
|
9
|
-
defaultThinkingLevel?: ThinkingLevel | null;
|
|
10
|
-
onSelectedThinkingLevelChange: (value: ThinkingLevel) => void;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
function thinkingLabel(level: ThinkingLevel): string {
|
|
14
|
-
if (level === 'off') {
|
|
15
|
-
return t('chatThinkingLevelOff');
|
|
16
|
-
}
|
|
17
|
-
if (level === 'minimal') {
|
|
18
|
-
return t('chatThinkingLevelMinimal');
|
|
19
|
-
}
|
|
20
|
-
if (level === 'low') {
|
|
21
|
-
return t('chatThinkingLevelLow');
|
|
22
|
-
}
|
|
23
|
-
if (level === 'medium') {
|
|
24
|
-
return t('chatThinkingLevelMedium');
|
|
25
|
-
}
|
|
26
|
-
if (level === 'high') {
|
|
27
|
-
return t('chatThinkingLevelHigh');
|
|
28
|
-
}
|
|
29
|
-
if (level === 'adaptive') {
|
|
30
|
-
return t('chatThinkingLevelAdaptive');
|
|
31
|
-
}
|
|
32
|
-
return t('chatThinkingLevelXhigh');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function normalizeLevels(levels: ThinkingLevel[]): ThinkingLevel[] {
|
|
36
|
-
const deduped: ThinkingLevel[] = [];
|
|
37
|
-
for (const level of ['off', ...levels] as ThinkingLevel[]) {
|
|
38
|
-
if (!deduped.includes(level)) {
|
|
39
|
-
deduped.push(level);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return deduped;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function ChatInputThinkingSelector(props: ChatInputThinkingSelectorProps) {
|
|
46
|
-
if (props.supportedLevels.length === 0) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const options = normalizeLevels(props.supportedLevels);
|
|
51
|
-
const fallback = options.includes('off') ? 'off' : options[0];
|
|
52
|
-
const resolvedValue =
|
|
53
|
-
(props.selectedThinkingLevel && options.includes(props.selectedThinkingLevel) && props.selectedThinkingLevel) ||
|
|
54
|
-
(props.defaultThinkingLevel && options.includes(props.defaultThinkingLevel) && props.defaultThinkingLevel) ||
|
|
55
|
-
fallback;
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<Select value={resolvedValue} onValueChange={(value) => props.onSelectedThinkingLevelChange(value as ThinkingLevel)}>
|
|
59
|
-
<SelectTrigger className="h-8 w-auto min-w-[150px] rounded-lg border-0 bg-transparent px-3 text-xs font-medium text-gray-600 shadow-none hover:bg-gray-100 focus:ring-0">
|
|
60
|
-
<div className="flex min-w-0 items-center gap-2 text-left">
|
|
61
|
-
<Brain className="h-3.5 w-3.5 shrink-0 text-gray-500" />
|
|
62
|
-
<span className="truncate text-xs font-semibold text-gray-700">{thinkingLabel(resolvedValue)}</span>
|
|
63
|
-
</div>
|
|
64
|
-
</SelectTrigger>
|
|
65
|
-
<SelectContent className="w-[180px]">
|
|
66
|
-
{options.map((level) => (
|
|
67
|
-
<SelectItem key={level} value={level}>
|
|
68
|
-
{thinkingLabel(level)}
|
|
69
|
-
</SelectItem>
|
|
70
|
-
))}
|
|
71
|
-
</SelectContent>
|
|
72
|
-
</Select>
|
|
73
|
-
);
|
|
74
|
-
}
|