Skip to content

Commit d15621e

Browse files
committed
fix(ui): remove duplicate back button in info-view logs header
1 parent 982c21b commit d15621e

26 files changed

Lines changed: 1678 additions & 130 deletions

packages/electron-app/electron/main/process-manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ export class CliProcessManager extends EventEmitter {
539539
}
540540

541541
private buildCliArgs(options: StartOptions, host: string): string[] {
542-
const args = ["serve", "--host", host, "--generate-token", "--auth-cookie-name", this.authCookieName, "--unrestricted-root"]
542+
const args = ["serve", "--host", host, "--generate-token", "--auth-cookie-name", this.authCookieName]
543543

544544
if (options.dev) {
545545
// Dev: run plain HTTP + Vite dev server proxy.

packages/tauri-app/src-tauri/src/cli_manager.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -963,7 +963,6 @@ impl CliEntry {
963963
"--auth-cookie-name".to_string(),
964964
auth_cookie_name.to_string(),
965965
"--generate-token".to_string(),
966-
"--unrestricted-root".to_string(),
967966
];
968967

969968
if dev {

packages/ui/src/components/environment-variables-editor.tsx

Lines changed: 11 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,8 @@
1-
import { Component, createSignal, For, Show, mergeProps } from "solid-js"
2-
import { Plus, Trash2, Key, Globe, Eye, EyeOff } from "lucide-solid"
1+
import { Component, createSignal, For, Show } from "solid-js"
2+
import { Plus, Trash2, Key, Globe } from "lucide-solid"
33
import { useConfig } from "../stores/preferences"
44
import { useI18n } from "../lib/i18n"
55

6-
/**
7-
* Password input with visibility toggle
8-
*/
9-
interface IPasswordInputProps {
10-
value: string
11-
placeholder?: string
12-
disabled?: boolean
13-
class?: string
14-
onInput?: (value: string) => void
15-
}
16-
17-
const PasswordInput: Component<IPasswordInputProps> = (props) => {
18-
const merged = mergeProps({ disabled: false } as IPasswordInputProps, props)
19-
const [show, setShow] = createSignal(false)
20-
21-
return (
22-
<div class="relative flex-1">
23-
<input
24-
type={show() ? "text" : "password"}
25-
value={merged.value}
26-
disabled={merged.disabled}
27-
onInput={(e) => merged.onInput?.(e.currentTarget.value)}
28-
class={merged.class}
29-
placeholder={merged.placeholder}
30-
/>
31-
<button
32-
type="button"
33-
onClick={() => setShow(!show())}
34-
disabled={merged.disabled}
35-
class="absolute right-2 top-1/2 -translate-y-1/2 p-0.5 icon-muted icon-accent-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
36-
title={show() ? "Hide value" : "Show value"}
37-
>
38-
{show() ? <EyeOff class="w-3.5 h-3.5" /> : <Eye class="w-3.5 h-3.5" />}
39-
</button>
40-
</div>
41-
)
42-
}
43-
446
interface EnvironmentVariablesEditorProps {
457
disabled?: boolean
468
}
@@ -118,11 +80,12 @@ const EnvironmentVariablesEditor: Component<EnvironmentVariablesEditorProps> = (
11880
placeholder={t("envEditor.fields.name.placeholder")}
11981
title={t("envEditor.fields.name.readOnlyTitle")}
12082
/>
121-
<PasswordInput
83+
<input
84+
type="text"
12285
value={value}
12386
disabled={props.disabled}
124-
onInput={(v) => handleUpdateVariable(key, v)}
125-
class="w-full px-2.5 py-1.5 text-sm bg-surface-base border border-base rounded text-primary focus-ring-accent disabled:opacity-50 disabled:cursor-not-allowed pr-8"
87+
onInput={(e) => handleUpdateVariable(key, e.currentTarget.value)}
88+
class="flex-1 px-2.5 py-1.5 text-sm bg-surface-base border border-base rounded text-primary focus-ring-accent disabled:opacity-50 disabled:cursor-not-allowed"
12689
placeholder={t("envEditor.fields.value.placeholder")}
12790
/>
12891
</div>
@@ -153,11 +116,13 @@ const EnvironmentVariablesEditor: Component<EnvironmentVariablesEditorProps> = (
153116
class="flex-1 px-2.5 py-1.5 text-sm bg-surface-base border border-base rounded text-primary focus-ring-accent disabled:opacity-50 disabled:cursor-not-allowed"
154117
placeholder={t("envEditor.fields.name.placeholder")}
155118
/>
156-
<PasswordInput
119+
<input
120+
type="text"
157121
value={newValue()}
122+
onInput={(e) => setNewValue(e.currentTarget.value)}
123+
onKeyPress={handleKeyPress}
158124
disabled={props.disabled}
159-
onInput={setNewValue}
160-
class="w-full px-2.5 py-1.5 text-sm bg-surface-base border border-base rounded text-primary focus-ring-accent disabled:opacity-50 disabled:cursor-not-allowed pr-8"
125+
class="flex-1 px-2.5 py-1.5 text-sm bg-surface-base border border-base rounded text-primary focus-ring-accent disabled:opacity-50 disabled:cursor-not-allowed"
161126
placeholder={t("envEditor.fields.value.placeholder")}
162127
/>
163128
</div>

packages/ui/src/components/info-view.tsx

Lines changed: 112 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { Component, For, createSignal, createEffect, Show, onMount, onCleanup, createMemo } from "solid-js"
2-
import { getInstanceLogs, instances, isInstanceLogStreaming, setInstanceLogStreaming } from "../stores/instances"
3-
import { ChevronDown } from "lucide-solid"
2+
import { getInstanceLogs, instances, isInstanceLogStreaming, setInstanceLogStreaming, clearLogs } from "../stores/instances"
3+
import { ArrowLeft, Trash2 } from "lucide-solid"
44
import InstanceInfo from "./instance-info"
55
import { useI18n } from "../lib/i18n"
66

77
interface InfoViewProps {
88
instanceId: string
9+
onBackToConversation?: () => void
910
}
1011

1112
const logsScrollState = new Map<string, { scrollTop: number; autoScroll: boolean }>()
@@ -15,19 +16,27 @@ const InfoView: Component<InfoViewProps> = (props) => {
1516
let scrollRef: HTMLDivElement | undefined
1617
const savedState = logsScrollState.get(props.instanceId)
1718
const [autoScroll, setAutoScroll] = createSignal(savedState?.autoScroll ?? false)
19+
const [showScrollTopButton, setShowScrollTopButton] = createSignal(false)
20+
const [showScrollBottomButton, setShowScrollBottomButton] = createSignal(false)
1821

1922
const instance = () => instances().get(props.instanceId)
2023
const logs = createMemo(() => getInstanceLogs(props.instanceId))
2124
const streamingEnabled = createMemo(() => isInstanceLogStreaming(props.instanceId))
2225

2326
const handleEnableLogs = () => setInstanceLogStreaming(props.instanceId, true)
2427
const handleDisableLogs = () => setInstanceLogStreaming(props.instanceId, false)
28+
const handleClearLogs = () => {
29+
clearLogs(props.instanceId)
30+
updateScrollButtons()
31+
}
2532

2633
onMount(() => {
2734

2835
if (scrollRef && savedState) {
2936
scrollRef.scrollTop = savedState.scrollTop
3037
}
38+
// 初始化滾動按鈕可見性 / Initialize scroll button visibility
39+
updateScrollButtons()
3140
})
3241

3342
onCleanup(() => {
@@ -45,18 +54,51 @@ const InfoView: Component<InfoViewProps> = (props) => {
4554
}
4655
})
4756

57+
// 監聽日誌變化並更新滾動按鈕 / Listen for log changes and update scroll buttons
58+
createEffect(() => {
59+
logs() // 追蹤 logs 變化
60+
updateScrollButtons()
61+
})
62+
63+
/** 更新滾動按鈕顯示狀態 / Update scroll button visibility */
64+
const updateScrollButtons = () => {
65+
if (!scrollRef) return
66+
67+
const scrollTop = scrollRef.scrollTop
68+
const scrollHeight = scrollRef.scrollHeight
69+
const clientHeight = scrollRef.clientHeight
70+
const hasItems = logs().length > 0
71+
72+
const atBottom = scrollHeight - (scrollTop + clientHeight) <= 50
73+
const atTop = scrollTop <= 50
74+
75+
setShowScrollBottomButton(hasItems && !atBottom)
76+
setShowScrollTopButton(hasItems && !atTop)
77+
}
78+
79+
/** 滾動至頂部 / Scroll to top */
80+
const scrollToTop = () => {
81+
if (scrollRef) {
82+
scrollRef.scrollTop = 0
83+
setAutoScroll(false)
84+
updateScrollButtons()
85+
}
86+
}
87+
4888
const handleScroll = () => {
4989
if (!scrollRef) return
5090

5191
const isAtBottom = scrollRef.scrollHeight - scrollRef.scrollTop <= scrollRef.clientHeight + 50
5292

5393
setAutoScroll(isAtBottom)
94+
updateScrollButtons()
5495
}
5596

5697
const scrollToBottom = () => {
5798
if (scrollRef) {
5899
scrollRef.scrollTop = scrollRef.scrollHeight
59100
setAutoScroll(true)
101+
updateScrollButtons()
60102
}
61103
}
62104

@@ -83,6 +125,11 @@ const InfoView: Component<InfoViewProps> = (props) => {
83125
}
84126
}
85127

128+
/** 是否顯示浮動滾動按鈕 / Whether to show floating scroll buttons */
129+
const showScrollButtons = createMemo(() => {
130+
return streamingEnabled() && (showScrollTopButton() || showScrollBottomButton())
131+
})
132+
86133
return (
87134
<div class="log-container">
88135
<div class="flex-1 flex flex-col lg:flex-row gap-4 p-4 overflow-hidden">
@@ -94,6 +141,38 @@ const InfoView: Component<InfoViewProps> = (props) => {
94141
<div class="log-header">
95142
<h2 class="panel-title">{t("infoView.logs.title")}</h2>
96143
<div class="flex items-center gap-2">
144+
<Show when={props.onBackToConversation}>
145+
{(onBack) => (
146+
<button
147+
type="button"
148+
class="button-tertiary"
149+
onClick={onBack}
150+
title={t("infoView.logs.actions.back")}
151+
>
152+
<ArrowLeft class="w-4 h-4" />
153+
</button>
154+
)}
155+
</Show>
156+
<Show when={logs().length > 0}>
157+
<button
158+
type="button"
159+
class="button-tertiary"
160+
onClick={handleClearLogs}
161+
title={t("infoView.logs.actions.clear")}
162+
>
163+
<Trash2 class="w-4 h-4" />
164+
</button>
165+
</Show>
166+
<Show when={logs().length > 0}>
167+
<button
168+
type="button"
169+
class="button-tertiary"
170+
onClick={handleClearLogs}
171+
title={t("infoView.logs.actions.clear")}
172+
>
173+
<Trash2 class="w-4 h-4" />
174+
</button>
175+
</Show>
97176
<Show
98177
when={streamingEnabled()}
99178
fallback={
@@ -143,15 +222,37 @@ const InfoView: Component<InfoViewProps> = (props) => {
143222
</Show>
144223
</Show>
145224
</div>
146-
147-
<Show when={!autoScroll() && streamingEnabled()}>
148-
<button
149-
onClick={scrollToBottom}
150-
class="scroll-to-bottom"
151-
>
152-
<ChevronDown class="w-4 h-4" />
153-
{t("infoView.logs.scrollToBottom")}
154-
</button>
225+
226+
{/* 浮動滾動按鈕 / Floating scroll buttons */}
227+
<Show when={showScrollButtons()}>
228+
<div class="message-scroll-button-wrapper">
229+
<Show when={showScrollTopButton()}>
230+
<button
231+
type="button"
232+
class="message-scroll-button"
233+
onClick={scrollToTop}
234+
aria-label={t("infoView.logs.scrollToTop")}
235+
title={t("infoView.logs.scrollToTop")}
236+
>
237+
<span class="message-scroll-icon" aria-hidden="true">
238+
239+
</span>
240+
</button>
241+
</Show>
242+
<Show when={showScrollBottomButton()}>
243+
<button
244+
type="button"
245+
class="message-scroll-button"
246+
onClick={scrollToBottom}
247+
aria-label={t("infoView.logs.scrollToBottom")}
248+
title={t("infoView.logs.scrollToBottom")}
249+
>
250+
<span class="message-scroll-icon" aria-hidden="true">
251+
252+
</span>
253+
</button>
254+
</Show>
255+
</div>
155256
</Show>
156257
</div>
157258
</div>

packages/ui/src/components/instance-info.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,13 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
151151
</div>
152152
<div class="space-y-1">
153153
<For each={environmentEntries()}>
154-
{([key]) => (
154+
{([key, value]) => (
155155
<div dir="ltr" class="flex items-center gap-2 px-2 py-1.5 rounded border bg-surface-secondary border-base">
156156
<span class="text-xs font-mono font-medium flex-1 text-primary" title={key}>
157157
{key}
158158
</span>
159-
<span class="text-xs font-mono flex-1 text-secondary">
160-
***
159+
<span class="text-xs font-mono flex-1 text-secondary" title={value}>
160+
{value}
161161
</span>
162162
</div>
163163
)}

0 commit comments

Comments
 (0)