mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 20:17:20 +00:00
feat: add MiniMax-M2.7 model, custom model input, combobox UX fix, mobile font-size audit (#282)
- Add MiniMax-M2.7 to minimax_native provider model list - Fix combobox dropdown UX: show all options on re-focus instead of filtering by current selection - Add allowCustom prop to combobox for typing custom model names not in the list - Add i18n key useCustomModel for EN/VI/ZH - Audit all raw input/select/textarea elements across web UI to use text-base (16px) on mobile, preventing iOS Safari auto-zoom on focus
This commit is contained in:
21 files changed
+84
-29
No files matched your search
@@ -195,6 +195,7 @@ func minimaxModels() []ModelInfo {
|
||||
// Chat / text
|
||||
{ID: "MiniMax-Text-01", Name: "MiniMax Text 01"},
|
||||
{ID: "MiniMax-M1", Name: "MiniMax M1"},
|
||||
{ID: "MiniMax-M2.7", Name: "MiniMax M2.7"},
|
||||
{ID: "MiniMax-M2.5", Name: "MiniMax M2.5"},
|
||||
// Image generation
|
||||
{ID: "image-01", Name: "Image 01"},
|
||||
|
||||
@@ -148,6 +148,8 @@ export function ProviderModelSelect({
|
||||
onChange={onModelChange}
|
||||
options={models.map((m) => ({ value: m.id, label: m.name }))}
|
||||
placeholder={modelsLoading ? t("loadingModels") : (modelPlaceholder ?? t("enterOrSelectModel"))}
|
||||
allowCustom
|
||||
customLabel={t("useCustomModel")}
|
||||
/>
|
||||
</div>
|
||||
{shouldShowVerify && (
|
||||
|
||||
@@ -122,7 +122,7 @@ export function SkillNameSelect({
|
||||
onFocus={() => setOpen(true)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={value.length === 0 ? (placeholder ?? t("selectOrTypeSkills")) : ""}
|
||||
className="placeholder:text-muted-foreground min-w-[80px] flex-1 bg-transparent py-0.5 text-sm outline-none"
|
||||
className="placeholder:text-muted-foreground min-w-[80px] flex-1 bg-transparent py-0.5 text-base md:text-sm outline-none"
|
||||
/>
|
||||
<ChevronDownIcon
|
||||
className="text-muted-foreground size-4 shrink-0 cursor-pointer opacity-50"
|
||||
|
||||
@@ -73,7 +73,7 @@ export function TagInput({
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={() => { if (input.trim()) addTag(input); }}
|
||||
placeholder={value.length === 0 ? (placeholder ?? t("typeAndPressEnter")) : ""}
|
||||
className="placeholder:text-muted-foreground min-w-[80px] flex-1 bg-transparent py-0.5 text-sm outline-none"
|
||||
className="placeholder:text-muted-foreground min-w-[80px] flex-1 bg-transparent py-0.5 text-base md:text-sm outline-none"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -150,7 +150,7 @@ export function ToolNameSelect({
|
||||
onFocus={() => setOpen(true)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={value.length === 0 ? (placeholder ?? t("selectOrTypeTools")) : ""}
|
||||
className="placeholder:text-muted-foreground min-w-[80px] flex-1 bg-transparent py-0.5 text-sm outline-none"
|
||||
className="placeholder:text-muted-foreground min-w-[80px] flex-1 bg-transparent py-0.5 text-base md:text-sm outline-none"
|
||||
/>
|
||||
<ChevronDownIcon
|
||||
className="text-muted-foreground size-4 shrink-0 cursor-pointer opacity-50"
|
||||
|
||||
@@ -16,6 +16,10 @@ interface ComboboxProps {
|
||||
className?: string;
|
||||
/** Render dropdown into a portal container (useful inside dialogs with overflow clipping). */
|
||||
portalContainer?: React.RefObject<HTMLElement | null>;
|
||||
/** Allow typing custom values not in the options list. Shows a hint in the dropdown. */
|
||||
allowCustom?: boolean;
|
||||
/** Label for the custom value hint (default: "Use custom:"). */
|
||||
customLabel?: string;
|
||||
}
|
||||
|
||||
export function Combobox({
|
||||
@@ -25,9 +29,15 @@ export function Combobox({
|
||||
placeholder,
|
||||
className,
|
||||
portalContainer,
|
||||
allowCustom,
|
||||
customLabel = "Use custom:",
|
||||
}: ComboboxProps) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [search, setSearch] = React.useState("");
|
||||
// Track whether user actively typed since last focus — when false, show all options
|
||||
const inputDirtyRef = React.useRef(false);
|
||||
const [inputDirty, setInputDirty] = React.useState(false);
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||
const dropdownRef = React.useRef<HTMLDivElement>(null);
|
||||
const [dropdownStyle, setDropdownStyle] = React.useState<React.CSSProperties>({});
|
||||
@@ -48,6 +58,8 @@ export function Combobox({
|
||||
(!dropdownRef.current || !dropdownRef.current.contains(target))
|
||||
) {
|
||||
setOpen(false);
|
||||
setInputDirty(false);
|
||||
inputDirtyRef.current = false;
|
||||
}
|
||||
};
|
||||
document.addEventListener("mousedown", handler);
|
||||
@@ -92,14 +104,23 @@ export function Combobox({
|
||||
}
|
||||
}, [open, search, resolvedPortal]);
|
||||
|
||||
// When not dirty (just focused), show all options. When dirty, filter by search.
|
||||
const filtered = React.useMemo(() => {
|
||||
if (!search) return options;
|
||||
if (!inputDirty || !search) return options;
|
||||
const q = search.toLowerCase();
|
||||
return options.filter(
|
||||
(o) =>
|
||||
o.value.toLowerCase().includes(q) ||
|
||||
(o.label && o.label.toLowerCase().includes(q)),
|
||||
);
|
||||
}, [options, search, inputDirty]);
|
||||
|
||||
// Check if typed value is a custom value (not matching any option exactly)
|
||||
const isCustomValue = React.useMemo(() => {
|
||||
if (!search.trim()) return false;
|
||||
return !options.some(
|
||||
(o) => o.value === search || o.label === search,
|
||||
);
|
||||
}, [options, search]);
|
||||
|
||||
const handleSelect = (val: string) => {
|
||||
@@ -107,16 +128,33 @@ export function Combobox({
|
||||
const match = options.find((o) => o.value === val);
|
||||
setSearch(match?.label || val);
|
||||
setOpen(false);
|
||||
setInputDirty(false);
|
||||
inputDirtyRef.current = false;
|
||||
};
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const val = e.target.value;
|
||||
setSearch(val);
|
||||
onChange(val);
|
||||
if (!inputDirtyRef.current) {
|
||||
inputDirtyRef.current = true;
|
||||
setInputDirty(true);
|
||||
}
|
||||
if (!open && options.length > 0) setOpen(true);
|
||||
};
|
||||
|
||||
const dropdownContent = open && filtered.length > 0 && (
|
||||
const handleFocus = () => {
|
||||
// Reset dirty state on focus — shows all options initially
|
||||
inputDirtyRef.current = false;
|
||||
setInputDirty(false);
|
||||
if (options.length > 0) setOpen(true);
|
||||
// Select all text so user can start typing to replace
|
||||
requestAnimationFrame(() => inputRef.current?.select());
|
||||
};
|
||||
|
||||
const showCustomHint = allowCustom && inputDirty && isCustomValue && search.trim();
|
||||
|
||||
const dropdownContent = open && (filtered.length > 0 || showCustomHint) && (
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
style={dropdownStyle}
|
||||
@@ -136,15 +174,26 @@ export function Combobox({
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
{showCustomHint && (
|
||||
<button
|
||||
type="button"
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onClick={() => handleSelect(search.trim())}
|
||||
className="hover:bg-accent hover:text-accent-foreground text-muted-foreground flex w-full cursor-pointer items-center rounded-sm py-1.5 pl-2 text-sm italic outline-hidden select-none"
|
||||
>
|
||||
{customLabel} <span className="text-foreground ml-1 font-medium not-italic">{search.trim()}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className={cn("relative", className)}>
|
||||
<input
|
||||
ref={inputRef}
|
||||
value={search}
|
||||
onChange={handleInputChange}
|
||||
onFocus={() => options.length > 0 && setOpen(true)}
|
||||
onFocus={handleFocus}
|
||||
placeholder={placeholder}
|
||||
className={cn(
|
||||
"border-input placeholder:text-muted-foreground dark:bg-input/30 h-9 w-full rounded-md border bg-transparent px-3 py-1 pr-8 text-base md:text-sm shadow-xs outline-none transition-[color,box-shadow]",
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"model": "Model",
|
||||
"selectProvider": "Select provider",
|
||||
"enterOrSelectModel": "Enter or select model",
|
||||
"useCustomModel": "Use custom:",
|
||||
"providerTip": "LLM provider name. Must match a configured provider.",
|
||||
"modelTip": "Model ID to use.",
|
||||
"check": "Check",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"manage": "Quản lý",
|
||||
"messages": "tin nhắn",
|
||||
"model": "Model",
|
||||
"useCustomModel": "Dùng tuỳ chỉnh:",
|
||||
"modelTip": "ID model để sử dụng.",
|
||||
"modelVerified": "Model đã được xác minh",
|
||||
"name": "Tên",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"manage": "管理",
|
||||
"messages": "条消息",
|
||||
"model": "模型",
|
||||
"useCustomModel": "使用自定义:",
|
||||
"modelTip": "要使用的模型 ID。",
|
||||
"modelVerified": "模型已验证",
|
||||
"name": "名称",
|
||||
|
||||
@@ -202,7 +202,7 @@ function ContactSearchBox({ existingIDs, onSelect }: { existingIDs: Set<string>;
|
||||
onChange={(e) => { setSearch(e.target.value); setOpen(true); }}
|
||||
onFocus={() => search.length >= 2 && setOpen(true)}
|
||||
placeholder={t("instances.searchContacts")}
|
||||
className="h-8 w-full rounded-md border bg-transparent pl-7 pr-2 text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
|
||||
className="h-8 w-full rounded-md border bg-transparent pl-7 pr-2 text-base md:text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
|
||||
/>
|
||||
</div>
|
||||
{open && search.length >= 2 && filtered.length > 0 && createPortal(
|
||||
|
||||
@@ -72,7 +72,7 @@ export function ContactInsertSearch({ onInsert }: ContactInsertSearchProps) {
|
||||
onChange={(e) => { setSearch(e.target.value); setOpen(true); }}
|
||||
onFocus={() => search.length >= 2 && setOpen(true)}
|
||||
placeholder={t("files.insertContact")}
|
||||
className="h-8 w-full max-w-sm rounded-md border bg-transparent pl-7 pr-2 text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
|
||||
className="h-8 w-full max-w-sm rounded-md border bg-transparent pl-7 pr-2 text-base md:text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
|
||||
/>
|
||||
</div>
|
||||
{open && search.length >= 2 && contacts.length > 0 && createPortal(
|
||||
|
||||
@@ -200,7 +200,7 @@ export function EventsPage() {
|
||||
<select
|
||||
value={teamFilter}
|
||||
onChange={(e) => setTeamFilter(e.target.value)}
|
||||
className="h-7 rounded-md border bg-background px-2 text-xs"
|
||||
className="h-7 rounded-md border bg-background px-2 text-base md:text-xs"
|
||||
>
|
||||
<option value="all">{t("filters.allTeams")}</option>
|
||||
{uniqueTeams.map((id) => (
|
||||
@@ -215,7 +215,7 @@ export function EventsPage() {
|
||||
<select
|
||||
value={userFilter}
|
||||
onChange={(e) => setUserFilter(e.target.value)}
|
||||
className="h-7 rounded-md border bg-background px-2 text-xs"
|
||||
className="h-7 rounded-md border bg-background px-2 text-base md:text-xs"
|
||||
>
|
||||
<option value="all">{t("filters.allUsers")}</option>
|
||||
{uniqueUsers.map((uid) => (
|
||||
@@ -229,7 +229,7 @@ export function EventsPage() {
|
||||
<select
|
||||
value={chatFilter}
|
||||
onChange={(e) => setChatFilter(e.target.value)}
|
||||
className="h-7 rounded-md border bg-background px-2 text-xs"
|
||||
className="h-7 rounded-md border bg-background px-2 text-base md:text-xs"
|
||||
>
|
||||
<option value="all">{t("filters.allChats")}</option>
|
||||
{uniqueChats.map((cid) => (
|
||||
|
||||
@@ -49,7 +49,7 @@ export function KnowledgeGraphPage() {
|
||||
id="kg-agent"
|
||||
value={agentId}
|
||||
onChange={(e) => { setAgentId(e.target.value); setUserIdFilter(""); }}
|
||||
className="h-8 rounded-md border bg-background px-2 text-sm"
|
||||
className="h-8 rounded-md border bg-background px-2 text-base md:text-sm"
|
||||
>
|
||||
<option value="">{t("filters.selectAgent")}</option>
|
||||
{agents.map((a) => (
|
||||
@@ -63,7 +63,7 @@ export function KnowledgeGraphPage() {
|
||||
id="kg-scope"
|
||||
value={userIdFilter}
|
||||
onChange={(e) => setUserIdFilter(e.target.value)}
|
||||
className="h-8 rounded-md border bg-background px-2 text-sm max-w-[240px]"
|
||||
className="h-8 rounded-md border bg-background px-2 text-base md:text-sm max-w-[240px]"
|
||||
>
|
||||
<option value="">{t("filters.allScope")}</option>
|
||||
{scopeOptions.map((o) => (
|
||||
@@ -84,7 +84,7 @@ export function KnowledgeGraphPage() {
|
||||
<select
|
||||
value={agentId}
|
||||
onChange={(e) => { setAgentId(e.target.value); setUserIdFilter(""); }}
|
||||
className="mt-2 h-9 rounded-md border bg-background px-3 text-sm"
|
||||
className="mt-2 h-9 rounded-md border bg-background px-3 text-base md:text-sm"
|
||||
>
|
||||
<option value="">{t("filters.selectAgent")}</option>
|
||||
{agents.map((a) => (
|
||||
|
||||
@@ -169,7 +169,7 @@ export function PairingForm({ onApproved }: PairingFormProps) {
|
||||
value={userId}
|
||||
onChange={(e) => setUserId(e.target.value)}
|
||||
placeholder={t("pairing.userIdPlaceholder")}
|
||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base md:text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -59,7 +59,7 @@ export function TokenForm({ onSubmit }: TokenFormProps) {
|
||||
value={userId}
|
||||
onChange={(e) => { setUserId(e.target.value); setError(null); }}
|
||||
placeholder={t("token.userIdPlaceholder")}
|
||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base md:text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
autoFocus
|
||||
disabled={connecting}
|
||||
/>
|
||||
@@ -75,7 +75,7 @@ export function TokenForm({ onSubmit }: TokenFormProps) {
|
||||
value={token}
|
||||
onChange={(e) => { setToken(e.target.value); setError(null); }}
|
||||
placeholder={t("token.tokenPlaceholder")}
|
||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base md:text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
disabled={connecting}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -70,7 +70,7 @@ export function LogsPage() {
|
||||
<select
|
||||
value={level}
|
||||
onChange={(e) => startTail(e.target.value as LogLevel)}
|
||||
className="h-8 cursor-pointer rounded-md border bg-background px-2 text-xs"
|
||||
className="h-8 cursor-pointer rounded-md border bg-background px-2 text-base md:text-xs"
|
||||
title={t("logLevelTitle")}
|
||||
>
|
||||
{levels.map((l) => (
|
||||
@@ -110,7 +110,7 @@ export function LogsPage() {
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
placeholder={t("filterPlaceholder")}
|
||||
className="h-7 w-full rounded-md border bg-background pl-8 pr-3 text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
|
||||
className="h-7 w-full rounded-md border bg-background pl-8 pr-3 text-base md:text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-0.5">
|
||||
|
||||
@@ -194,18 +194,18 @@ export function MCPFormDialog({ open, onOpenChange, server, onSubmit, onTest }:
|
||||
<>
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="mcp-cmd">{t("form.command")}</Label>
|
||||
<Input id="mcp-cmd" value={command} onChange={(e) => setCommand(e.target.value)} placeholder="npx -y @modelcontextprotocol/server-everything" className="font-mono text-sm" />
|
||||
<Input id="mcp-cmd" value={command} onChange={(e) => setCommand(e.target.value)} placeholder="npx -y @modelcontextprotocol/server-everything" className="font-mono" />
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="mcp-args">{t("form.args")}</Label>
|
||||
<Input id="mcp-args" value={args} onChange={(e) => setArgs(e.target.value)} placeholder={t("form.argsPlaceholder")} className="font-mono text-sm" />
|
||||
<Input id="mcp-args" value={args} onChange={(e) => setArgs(e.target.value)} placeholder={t("form.argsPlaceholder")} className="font-mono" />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="mcp-url">{t("form.url")}</Label>
|
||||
<Input id="mcp-url" value={url} onChange={(e) => setUrl(e.target.value)} placeholder="http://localhost:3001/sse" className="font-mono text-sm" />
|
||||
<Input id="mcp-url" value={url} onChange={(e) => setUrl(e.target.value)} placeholder="http://localhost:3001/sse" className="font-mono" />
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<Label>{t("form.headers")}</Label>
|
||||
@@ -237,7 +237,7 @@ export function MCPFormDialog({ open, onOpenChange, server, onSubmit, onTest }:
|
||||
<Label htmlFor="mcp-prefix">{t("form.toolPrefix")}</Label>
|
||||
<div className="flex">
|
||||
<span className="inline-flex items-center px-2.5 rounded-l-md border border-r-0 border-input bg-muted text-muted-foreground text-sm font-mono">mcp_</span>
|
||||
<Input id="mcp-prefix" value={toolPrefix} onChange={(e) => setToolPrefix(e.target.value.replace(/[^a-z0-9_]/g, ""))} placeholder={name.replace(/-/g, "_") || "auto"} className="rounded-l-none font-mono text-sm" />
|
||||
<Input id="mcp-prefix" value={toolPrefix} onChange={(e) => setToolPrefix(e.target.value.replace(/[^a-z0-9_]/g, ""))} placeholder={name.replace(/-/g, "_") || "auto"} className="rounded-l-none font-mono" />
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">{t("form.toolPrefixHint")} Tools: <code className="text-[10px]">mcp_{prefix}__{tool}</code></p>
|
||||
</div>
|
||||
|
||||
@@ -408,7 +408,7 @@ function ToolMultiSelect({
|
||||
onFocus={() => setOpen(true)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={value.length === 0 ? placeholder : ""}
|
||||
className="placeholder:text-muted-foreground min-w-[80px] flex-1 bg-transparent py-0.5 text-sm outline-none"
|
||||
className="placeholder:text-muted-foreground min-w-[80px] flex-1 bg-transparent py-0.5 text-base md:text-sm outline-none"
|
||||
/>
|
||||
<ChevronDownIcon
|
||||
className="text-muted-foreground size-4 shrink-0 cursor-pointer opacity-50"
|
||||
|
||||
@@ -109,7 +109,7 @@ export function MemoryCreateDialog({ open, onOpenChange, agentId: parentAgentId,
|
||||
id="mc-agent"
|
||||
value={selectedAgentId || parentAgentId || ""}
|
||||
onChange={(e) => setSelectedAgentId(e.target.value)}
|
||||
className="h-9 rounded-md border bg-background px-3 text-sm"
|
||||
className="h-9 rounded-md border bg-background px-3 text-base md:text-sm"
|
||||
>
|
||||
<option value="">{t("createDialog.agentIdPlaceholder")}</option>
|
||||
{agents.map((a) => (
|
||||
@@ -158,7 +158,7 @@ export function MemoryCreateDialog({ open, onOpenChange, agentId: parentAgentId,
|
||||
<select
|
||||
value={selectedUserId}
|
||||
onChange={(e) => setSelectedUserId(e.target.value)}
|
||||
className="h-9 rounded-md border bg-background px-3 text-sm"
|
||||
className="h-9 rounded-md border bg-background px-3 text-base md:text-sm"
|
||||
>
|
||||
<option value="">{t("createDialog.selectGroupUser")}</option>
|
||||
{knownUserIds.map((uid) => (
|
||||
|
||||
@@ -134,7 +134,7 @@ export function MemoryPage() {
|
||||
id="mem-agent"
|
||||
value={agentId}
|
||||
onChange={(e) => { setAgentId(e.target.value); setUserIdFilter(""); setPage(1); }}
|
||||
className="h-9 rounded-md border bg-background px-3 text-sm"
|
||||
className="h-9 rounded-md border bg-background px-3 text-base md:text-sm"
|
||||
>
|
||||
<option value="">{t("filters.allAgents")}</option>
|
||||
{agents.map((a) => (
|
||||
@@ -150,7 +150,7 @@ export function MemoryPage() {
|
||||
id="mem-scope"
|
||||
value={userIdFilter}
|
||||
onChange={(e) => { setUserIdFilter(e.target.value); setPage(1); }}
|
||||
className="h-9 rounded-md border bg-background px-3 text-sm min-w-[180px]"
|
||||
className="h-9 rounded-md border bg-background px-3 text-base md:text-sm min-w-[180px]"
|
||||
>
|
||||
<option value="">{t("filters.allScope")}</option>
|
||||
{userIds.map((uid) => (
|
||||
|
||||
@@ -137,7 +137,7 @@ export function SessionDetailPage({
|
||||
<div className="flex items-center gap-1">
|
||||
<input
|
||||
autoFocus
|
||||
className="h-7 rounded border bg-background px-2 text-sm font-medium outline-none focus:ring-1 focus:ring-ring"
|
||||
className="h-7 rounded border bg-background px-2 text-base md:text-sm font-medium outline-none focus:ring-1 focus:ring-ring"
|
||||
value={titleDraft}
|
||||
onChange={(e) => setTitleDraft(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
|
||||
Reference in new issue
Block a user