mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-07 05:45:39 +08:00
Compare commits
7 Commits
docs/add-r
...
fixes/mino
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58f7247a2d | ||
|
|
e2ff79bb82 | ||
|
|
c667b6a179 | ||
|
|
fa9eaf5573 | ||
|
|
2edfef2e3f | ||
|
|
96b16b42e4 | ||
|
|
f082cdc63b |
771
package-lock.json
generated
771
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -67,7 +67,7 @@
|
|||||||
"author": "CloudCLI UI Contributors",
|
"author": "CloudCLI UI Contributors",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@anthropic-ai/claude-agent-sdk": "^0.2.116",
|
"@anthropic-ai/claude-agent-sdk": "^0.3.165",
|
||||||
"@codemirror/lang-css": "^6.3.1",
|
"@codemirror/lang-css": "^6.3.1",
|
||||||
"@codemirror/lang-html": "^6.4.9",
|
"@codemirror/lang-html": "^6.4.9",
|
||||||
"@codemirror/lang-javascript": "^6.2.4",
|
"@codemirror/lang-javascript": "^6.2.4",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const CLAUDE_MODELS = {
|
|||||||
{
|
{
|
||||||
value: "default",
|
value: "default",
|
||||||
label: "Default (recommended)",
|
label: "Default (recommended)",
|
||||||
description: "Use the default model (currently Opus 4.7 (1M context)) · $5/$25 per Mtok",
|
description: "Use the default model (currently Opus 4.8 (1M context)) · $5/$25 per Mtok",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "sonnet",
|
value: "sonnet",
|
||||||
|
|||||||
@@ -31,6 +31,24 @@ export function createWebSocketServer(
|
|||||||
});
|
});
|
||||||
|
|
||||||
wss.on('connection', (ws, request) => {
|
wss.on('connection', (ws, request) => {
|
||||||
|
// Keep WebSocket alive across reverse-proxy idle timeouts (Cloudflare ~100s,
|
||||||
|
// AWS ALB 60s, nginx 60s, etc.). Without app-level pings these connections
|
||||||
|
// are silently torn down even when the UI is active, causing repeated
|
||||||
|
// reconnect cycles. ws library heartbeat is opt-in.
|
||||||
|
const HEARTBEAT_INTERVAL_MS = 30_000;
|
||||||
|
const heartbeat = setInterval(() => {
|
||||||
|
if (ws.readyState === ws.OPEN) {
|
||||||
|
try {
|
||||||
|
ws.ping();
|
||||||
|
} catch {
|
||||||
|
// socket may have been closed concurrently — interval will be cleared below
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, HEARTBEAT_INTERVAL_MS);
|
||||||
|
const stopHeartbeat = () => clearInterval(heartbeat);
|
||||||
|
ws.on('close', stopHeartbeat);
|
||||||
|
ws.on('error', stopHeartbeat);
|
||||||
|
|
||||||
const incomingRequest = request as AuthenticatedWebSocketRequest;
|
const incomingRequest = request as AuthenticatedWebSocketRequest;
|
||||||
const url = incomingRequest.url ?? '/';
|
const url = incomingRequest.url ?? '/';
|
||||||
const pathname = new URL(url, 'http://localhost').pathname;
|
const pathname = new URL(url, 'http://localhost').pathname;
|
||||||
|
|||||||
@@ -295,6 +295,7 @@ export default function ChatComposer({
|
|||||||
|
|
||||||
<PromptInputTextarea
|
<PromptInputTextarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
|
dir="auto"
|
||||||
value={input}
|
value={input}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
onClick={onTextareaClick}
|
onClick={onTextareaClick}
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ const MessageComponent = memo(({ message, prevMessage, createDiff, onFileOpen, o
|
|||||||
/* User message bubble on the right */
|
/* User message bubble on the right */
|
||||||
<div className="flex w-full items-end space-x-0 sm:w-auto sm:max-w-[85%] sm:space-x-3 md:max-w-md lg:max-w-lg xl:max-w-xl">
|
<div className="flex w-full items-end space-x-0 sm:w-auto sm:max-w-[85%] sm:space-x-3 md:max-w-md lg:max-w-lg xl:max-w-xl">
|
||||||
<div className="group flex-1 rounded-2xl rounded-br-md bg-blue-600 px-3 py-2 text-white shadow-sm sm:flex-initial sm:px-4">
|
<div className="group flex-1 rounded-2xl rounded-br-md bg-blue-600 px-3 py-2 text-white shadow-sm sm:flex-initial sm:px-4">
|
||||||
<div className="whitespace-pre-wrap break-words text-sm">
|
<div dir="auto" className="whitespace-pre-wrap break-words text-sm">
|
||||||
{message.content}
|
{message.content}
|
||||||
</div>
|
</div>
|
||||||
{message.images && message.images.length > 0 && (
|
{message.images && message.images.length > 0 && (
|
||||||
@@ -405,7 +405,7 @@ const MessageComponent = memo(({ message, prevMessage, createDiff, onFileOpen, o
|
|||||||
</ReasoningContent>
|
</ReasoningContent>
|
||||||
</Reasoning>
|
</Reasoning>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm text-gray-700 dark:text-gray-300">
|
<div dir="auto" className="text-sm text-gray-700 dark:text-gray-300">
|
||||||
{/* Reasoning accordion */}
|
{/* Reasoning accordion */}
|
||||||
{showThinking && message.reasoning && (
|
{showThinking && message.reasoning && (
|
||||||
<Reasoning className="mb-3" defaultOpen={false}>
|
<Reasoning className="mb-3" defaultOpen={false}>
|
||||||
|
|||||||
@@ -321,6 +321,7 @@ export default function ProviderSelectionEmptyState({
|
|||||||
|
|
||||||
<p className="mt-3 flex items-center justify-center gap-1.5 text-center text-xs text-muted-foreground/60">
|
<p className="mt-3 flex items-center justify-center gap-1.5 text-center text-xs text-muted-foreground/60">
|
||||||
<Trans
|
<Trans
|
||||||
|
ns="chat"
|
||||||
i18nKey="providerSelection.pressToSearch"
|
i18nKey="providerSelection.pressToSearch"
|
||||||
values={{ shortcut: MOD_KEY === "⌘" ? "⌘K" : "Ctrl+K" }}
|
values={{ shortcut: MOD_KEY === "⌘" ? "⌘K" : "Ctrl+K" }}
|
||||||
components={{
|
components={{
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ const useWebSocketProviderState = (): WebSocketContextType => {
|
|||||||
const { token } = useAuth();
|
const { token } = useAuth();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// The cleanup below sets unmountedRef = true. Without this reset, every
|
||||||
|
// re-run of the effect (e.g. on token refresh) would short-circuit connect()
|
||||||
|
// at its unmounted guard and leave the socket permanently disconnected.
|
||||||
|
unmountedRef.current = false;
|
||||||
connect();
|
connect();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ export default defineConfig(({ mode }) => {
|
|||||||
'/shell': {
|
'/shell': {
|
||||||
target: `ws://${proxyHost}:${serverPort}`,
|
target: `ws://${proxyHost}:${serverPort}`,
|
||||||
ws: true
|
ws: true
|
||||||
|
},
|
||||||
|
'/plugin-ws': {
|
||||||
|
target: `ws://${proxyHost}:${serverPort}`,
|
||||||
|
ws: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user