matched design

Hee Sung Son committed Nov 25, 2025 at 09:33 UTC 9f5ae5df9d0187cf3a43c6d2ea1e950d6e086686
2 files changed +16 -12
cmd/relay-server/frontend/src/components/TagCombobox.tsx
+9 -9
@@ -33,9 +33,7 @@ export function TagCombobox({
33 const query = inputValue.trim().toLowerCase();
34 const pool = availableTags.filter((tag) => !selectedTags.includes(tag));
35 if (!query) return pool.slice(0, 8);
36 - return pool
37 - .filter((tag) => tag.toLowerCase().includes(query))
38 - .slice(0, 8);
36 + return pool.filter((tag) => tag.toLowerCase().includes(query)).slice(0, 8);
37 }, [availableTags, selectedTags, inputValue]);
38
39 useEffect(() => {
@@ -106,18 +104,18 @@ export function TagCombobox({
104 className="flex w-full sm:w-auto sm:min-w-[320px] flex-1 items-center gap-2 overflow-visible"
105 >
106 <div className="flex min-w-0 flex-1 items-center gap-2 rounded-lg border border-border bg-background px-2 py-1.5 min-h-10">
109 - <div className="flex flex-wrap items-center gap-2 overflow-hidden">
107 + <div className="flex flex-1 flex-wrap items-center gap-2 overflow-hidden">
108 {selectedTags.map((tag) => (
109 <Button
110 key={tag}
111 variant="secondary"
112 size="sm"
115 - className="h-7 rounded-full px-3 text-xs"
113 + className="h-7 rounded px-3 bg-secondary text-primary text-xs"
114 onClick={() => onRemove(tag)}
115 aria-label={`Remove tag ${tag}`}
116 >
117 {tag}
120 - <span className="ml-2 text-foreground/70">×</span>
118 + <span className="ml-2 bg-secondary text-primary">×</span>
119 </Button>
120 ))}
121 <input
@@ -140,7 +138,7 @@ export function TagCombobox({
138 </div>
139 </div>
140
143 - <div className="flex items-center rounded-md bg-border text-xs font-semibold text-foreground/80 overflow-hidden h-10 flex-shrink-0">
141 + <div className="flex items-center rounded-md bg-border text-xs font-semibold text-foreground/80 overflow-hidden h-10 shrink-0">
142 <button
143 type="button"
144 className={cn(
@@ -165,11 +163,13 @@ export function TagCombobox({
163 </button>
164 </div>
165
168 - {open && filtered.length > 0 && panelStyle &&
166 + {open &&
167 + filtered.length > 0 &&
168 + panelStyle &&
169 createPortal(
170 <div
171 style={panelStyle}
172 - className="rounded-lg border border-border bg-background shadow-lg"
172 + className="rounded-lg border border-border bg-background shadow-lg overflow-hidden"
173 >
174 <ul
175 id={listId}
cmd/relay-server/frontend/src/pages/ServerList.tsx
+7 -3
@@ -98,7 +98,9 @@ export function ServerList() {
98 counts.set(tag, (counts.get(tag) || 0) + 1);
99 });
100 });
101 - return Array.from(counts.entries()).sort((a, b) => b[1] - a[1]).map(([tag]) => tag);
101 + return Array.from(counts.entries())
102 + .sort((a, b) => b[1] - a[1])
103 + .map(([tag]) => tag);
104 }, [servers]);
105
106 // Filter and sort servers
@@ -109,7 +111,9 @@ export function ServerList() {
111 if (selectedTags.length === 0) return true;
112 const tagsLower = server.tags.map((t) => t.toLowerCase());
113 if (tagMode === "AND") {
112 - return selectedTags.every((tag) => tagsLower.includes(tag.toLowerCase()));
114 + return selectedTags.every((tag) =>
115 + tagsLower.includes(tag.toLowerCase())
116 + );
117 }
118 return selectedTags.some((tag) => tagsLower.includes(tag.toLowerCase()));
119 };
@@ -254,7 +258,7 @@ export function ServerList() {
258 online={server.online}
259 dns={server.dns}
260 serverUrl={server.link}
257 - navigationPath={`/server/${server.id}`}
261 + navigationPath={server.link}
262 navigationState={{
263 id: server.id,
264 name: server.name,