feat: add project-aware agent workspace
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { ChevronDown, LocateFixed, MapPin, Search, Trash2 } from "lucide-react";
|
||||
import { ChevronDown, Crosshair, LocateFixed, MapPin, Search, Trash2 } from "lucide-react";
|
||||
import { useId, useLayoutEffect, useRef } from "react";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { Input } from "@/shared/ui/input";
|
||||
@@ -31,8 +31,9 @@ export function GeocodingPanel({ geocoding }: { geocoding: WorkbenchGeocoding })
|
||||
const candidateListRef = useRef<HTMLUListElement | null>(null);
|
||||
const changeSelectionRef = useRef<HTMLButtonElement | null>(null);
|
||||
const { address, result, results, pending, error, enabled } = geocoding;
|
||||
const mapResult = geocoding.origin === "map";
|
||||
const selectionCollapsed = Boolean(result && !geocoding.choosing);
|
||||
const hideSelectionHint = Boolean(results?.items.length && enabled && !error);
|
||||
const hideSelectionHint = Boolean(results?.items.length && enabled && !error && !geocoding.picking);
|
||||
useLayoutEffect(() => {
|
||||
if (selectionCollapsed || !result) return;
|
||||
const list = candidateListRef.current;
|
||||
@@ -42,12 +43,13 @@ export function GeocodingPanel({ geocoding }: { geocoding: WorkbenchGeocoding })
|
||||
const row = selected.getBoundingClientRect();
|
||||
list.scrollTop += row.top - viewport.top - (list.clientHeight - row.height) / 2;
|
||||
}, [selectionCollapsed, result, results]);
|
||||
const status = error
|
||||
?? (pending ? "正在搜索地名…"
|
||||
const status = !geocoding.configured ? "当前项目暂未配置地名搜索" : error
|
||||
?? (pending ? (mapResult ? "正在查询取点位置…" : "正在搜索地名…")
|
||||
: !enabled ? "等待地图加载后即可定位"
|
||||
: geocoding.picking ? "点击地图查询地名,按 Esc 结束取点"
|
||||
: result ? "已在地图标出所选位置"
|
||||
: results ? results.items.length ? "选择一个候选位置进行定位" : "奉贤区内未找到匹配位置,请细化关键字"
|
||||
: "搜索奉贤区内地址或地名,支持回车查询");
|
||||
: results ? results.items.length ? "选择一个候选位置进行定位" : `${geocoding.region}内未找到匹配位置,请细化关键字`
|
||||
: `搜索${geocoding.region}内地址或地名,支持回车查询`);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-3">
|
||||
@@ -79,12 +81,17 @@ export function GeocodingPanel({ geocoding }: { geocoding: WorkbenchGeocoding })
|
||||
aria-label="搜索地名"
|
||||
className="shrink-0 gap-1.5 px-3 text-xs"
|
||||
disabled={!enabled || !address.trim()}
|
||||
loading={pending}
|
||||
loading={pending && !mapResult}
|
||||
loadingLabel="正在搜索地名"
|
||||
>
|
||||
<Search aria-hidden="true" />查询
|
||||
</Button>
|
||||
</div>
|
||||
<Button type="button" variant="secondary" className="w-full text-xs"
|
||||
aria-label="从地图取点查找地名" aria-pressed={geocoding.picking} disabled={!enabled}
|
||||
onClick={geocoding.togglePicking}>
|
||||
<Crosshair aria-hidden="true" />{geocoding.picking ? "结束取点" : "地图取点"}
|
||||
</Button>
|
||||
<div
|
||||
id={statusId}
|
||||
role={error ? "alert" : "status"}
|
||||
@@ -97,9 +104,9 @@ export function GeocodingPanel({ geocoding }: { geocoding: WorkbenchGeocoding })
|
||||
</form>
|
||||
{results && results.items.length > 0 ? (
|
||||
<section aria-label="搜索结果" className="flex min-h-0 flex-1 flex-col gap-2 border-t border-[var(--surface-divider)] pt-3">
|
||||
<div className="flex h-10 shrink-0 items-center justify-between gap-2 px-1 text-[11px] text-[var(--ink-muted)]">
|
||||
<span className="text-xs font-semibold">{selectionCollapsed ? "已选位置" : "选择位置"}</span>
|
||||
{selectionCollapsed ? (
|
||||
<div className={cn("flex shrink-0 items-center justify-between gap-2 px-1 text-[11px] text-[var(--ink-muted)]", mapResult ? "h-6" : "h-10")}>
|
||||
<span className="text-xs font-semibold">{selectionCollapsed ? mapResult ? "取点结果" : "已选位置" : "选择位置"}</span>
|
||||
{selectionCollapsed && !mapResult ? (
|
||||
<Button ref={changeSelectionRef} type="button" variant="secondary" className="gap-1.5 px-3 text-xs" aria-expanded={false}
|
||||
onClick={(event) => {
|
||||
geocoding.changeSelection();
|
||||
@@ -111,7 +118,7 @@ export function GeocodingPanel({ geocoding }: { geocoding: WorkbenchGeocoding })
|
||||
}}>
|
||||
更换位置<ChevronDown aria-hidden="true" />
|
||||
</Button>
|
||||
) : <span className="tabular-nums">{results.truncated ? `前 ${results.total} 个结果` : `共 ${results.total} 个结果`}</span>}
|
||||
) : mapResult ? <span>地图位置</span> : <span className="tabular-nums">{results.truncated ? `前 ${results.total} 个结果` : `共 ${results.total} 个结果`}</span>}
|
||||
</div>
|
||||
{!selectionCollapsed ? <ul ref={candidateListRef} aria-busy={pending} className={cn(RESULT_VIEWPORT_CLASS_NAME, "bg-transparent", pending && "opacity-60")}>
|
||||
{results.items.map((item, index) => (
|
||||
@@ -132,8 +139,8 @@ export function GeocodingPanel({ geocoding }: { geocoding: WorkbenchGeocoding })
|
||||
{result && selectionCollapsed ? (
|
||||
<section aria-label="定位结果" className={cn(RESULT_VIEWPORT_CLASS_NAME, "flex flex-col gap-2 overflow-hidden")}>
|
||||
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain [scrollbar-width:thin]">
|
||||
<div className="space-y-3">
|
||||
<div className="flex min-h-16 items-center gap-3 border-b border-[var(--surface-divider)] px-1 py-3 text-xs">
|
||||
<div className={mapResult ? "space-y-2" : "space-y-3"}>
|
||||
<div className={cn("flex items-center gap-3 border-b border-[var(--surface-divider)] px-1 text-xs", mapResult ? "min-h-12 py-1" : "min-h-16 py-3")}>
|
||||
<PlaceSummary item={result} annotation={result.category ?? result.district} />
|
||||
</div>
|
||||
<dl aria-label="经纬度坐标" className="space-y-1 px-1">
|
||||
|
||||
Reference in New Issue
Block a user