mirror of
https://github.com/RuoJi6/ARTEX.git
synced 2026-11-02 02:43:50 +08:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39658c3a9b | ||
|
|
6d10c7231b | ||
|
|
a8640e25c1 | ||
|
|
fe1172c00d | ||
|
|
1f6028d10a | ||
|
|
cb1a00105a | ||
|
|
31baa6075c |
No files matched your search
@@ -370,6 +370,7 @@ export default function FindingsPage() {
|
||||
query,
|
||||
sort,
|
||||
assetScope: activeAssetScope ?? undefined,
|
||||
...dateRange,
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
@@ -104,7 +104,7 @@ export function FindingDateFilter({
|
||||
<span className="truncate">{label}</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="start" className="w-80 p-4">
|
||||
<PopoverContent align="start" className="w-[26rem] max-w-[calc(100vw-2rem)] p-4">
|
||||
<div className="mb-3">
|
||||
<p className="font-medium">按发现时间筛选</p>
|
||||
<p className="text-muted-foreground text-xs">选择年月日和时分,范围包含起止时刻。</p>
|
||||
@@ -112,14 +112,14 @@ export function FindingDateFilter({
|
||||
<div className="space-y-3">
|
||||
<div className="grid gap-1.5 text-xs font-medium">
|
||||
<span>开始时间</span>
|
||||
<div className="grid grid-cols-[1fr_8.5rem] gap-2">
|
||||
<div className="grid grid-cols-[minmax(0,1fr)_9.25rem] items-center gap-2">
|
||||
<DateField value={fromDate} onChange={(value) => apply({ fromDate: value })} label="开始日期" />
|
||||
<TimeSelect value={fromTime} onValueChange={(value) => apply({ fromTime: value })} aria-label="开始时分" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-1.5 text-xs font-medium">
|
||||
<span>结束时间</span>
|
||||
<div className="grid grid-cols-[1fr_8.5rem] gap-2">
|
||||
<div className="grid grid-cols-[minmax(0,1fr)_9.25rem] items-center gap-2">
|
||||
<DateField value={toDate} onChange={(value) => apply({ toDate: value })} label="结束日期" />
|
||||
<TimeSelect value={toTime} onValueChange={(value) => apply({ toTime: value })} aria-label="结束时分" />
|
||||
</div>
|
||||
|
||||
@@ -185,7 +185,7 @@ export function HttpPacketSyntax({
|
||||
className={cn("grid min-w-0", lineClassName, lineNumbers && "grid-cols-[2.25rem_minmax(0,1fr)]")}
|
||||
>
|
||||
{lineNumbers ? (
|
||||
<span className="sticky left-0 self-stretch border-r bg-muted/20 px-1 text-right text-[11px] text-muted-foreground/50">
|
||||
<span className="sticky left-0 self-stretch select-none border-r bg-muted/20 px-1 text-right text-[11px] text-muted-foreground/50">
|
||||
{index + 1}
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
@@ -10,6 +10,52 @@ import { HttpPacketSyntax } from "@/components/http-code-block";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function EditablePacket({
|
||||
id,
|
||||
value,
|
||||
onChange,
|
||||
onScroll,
|
||||
}: {
|
||||
id: string;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
onScroll: React.UIEventHandler<HTMLDivElement>;
|
||||
}) {
|
||||
const editor = React.useRef<HTMLDivElement>(null);
|
||||
const lastValue = React.useRef(value);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!editor.current || lastValue.current === value) return;
|
||||
if (editor.current.innerText !== value) {
|
||||
editor.current.innerText = value;
|
||||
}
|
||||
lastValue.current = value;
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
ref={editor}
|
||||
role="textbox"
|
||||
aria-label="请求"
|
||||
aria-multiline="true"
|
||||
tabIndex={0}
|
||||
contentEditable
|
||||
suppressContentEditableWarning
|
||||
spellCheck={false}
|
||||
onInput={(event) => {
|
||||
const next = event.currentTarget.innerText.replace(/\r\n?/g, "\n");
|
||||
lastValue.current = next;
|
||||
onChange(next);
|
||||
}}
|
||||
onScroll={onScroll}
|
||||
className="h-full min-h-0 w-0 flex-1 overflow-auto whitespace-pre-wrap break-words rounded-none border-0 px-3 py-2 font-mono text-xs leading-5 outline-none md:text-xs"
|
||||
>
|
||||
<HttpPacketSyntax raw={value} lineNumbers={false} wrapLines />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function RepeaterTextPane({
|
||||
id,
|
||||
label,
|
||||
@@ -64,7 +110,7 @@ export function RepeaterTextPane({
|
||||
{toolbar}
|
||||
<CopyButton text={value} label={`复制${label}`} className={showClock ? "ml-0" : "ml-auto"} />
|
||||
</div>
|
||||
<div className="relative flex min-h-0 flex-1 overflow-hidden rounded border border-input focus-within:border-ring">
|
||||
<div className="group relative flex min-h-0 flex-1 overflow-hidden rounded border border-input focus-within:border-ring">
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="w-12 shrink-0 overflow-hidden border-r bg-muted/30 text-right text-muted-foreground"
|
||||
@@ -73,7 +119,11 @@ export function RepeaterTextPane({
|
||||
{numbers}
|
||||
</pre>
|
||||
</div>
|
||||
<Textarea
|
||||
{syntaxHighlight && onChange ? (
|
||||
<EditablePacket id={id} value={value} onChange={onChange} onScroll={(event) => {
|
||||
if (gutter.current) gutter.current.style.transform = `translateY(-${event.currentTarget.scrollTop}px)`;
|
||||
}} />
|
||||
) : <Textarea
|
||||
id={id}
|
||||
value={value}
|
||||
onChange={(event) => onChange?.(event.target.value)}
|
||||
@@ -94,15 +144,15 @@ export function RepeaterTextPane({
|
||||
className={
|
||||
"field-sizing-fixed relative z-10 h-full max-h-none min-h-0 w-0 flex-1 resize-none rounded-none border-0 px-3 py-2 font-mono text-xs leading-5 focus-visible:ring-0 md:text-xs" +
|
||||
(syntaxHighlight
|
||||
? " bg-transparent text-transparent caret-foreground selection:bg-primary/20 selection:text-transparent placeholder:text-muted-foreground"
|
||||
? " bg-transparent text-transparent caret-foreground selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground group-focus-within:text-foreground"
|
||||
: "")
|
||||
}
|
||||
/>
|
||||
{syntaxHighlight ? (
|
||||
/>}
|
||||
{syntaxHighlight && !onChange ? (
|
||||
<div
|
||||
ref={syntax}
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-y-0 right-0 left-12 overflow-hidden px-3 py-2 font-mono text-xs leading-5"
|
||||
className="pointer-events-none absolute inset-y-0 right-0 left-12 z-20 overflow-hidden px-3 py-2 font-mono text-xs leading-5 group-focus-within:invisible"
|
||||
>
|
||||
<HttpPacketSyntax
|
||||
raw={value}
|
||||
|
||||
@@ -65,7 +65,7 @@ function TimePart({
|
||||
}}
|
||||
>
|
||||
<ComboboxInput
|
||||
className="h-10 min-w-0"
|
||||
className="h-10 w-16 min-w-16 shrink-0"
|
||||
placeholder={placeholder}
|
||||
aria-label={ariaLabel}
|
||||
inputMode="numeric"
|
||||
@@ -107,7 +107,7 @@ export function TimeSelect({
|
||||
const updateMinute = (minute: string) => onValueChange(`${selected.hour ?? "00"}:${minute}`);
|
||||
|
||||
return (
|
||||
<div className="flex min-w-0 items-center gap-1" title={optional && !hasValue ? placeholder : undefined}>
|
||||
<div className="flex w-[9.25rem] min-w-0 items-center gap-1.5" title={optional && !hasValue ? placeholder : undefined}>
|
||||
<TimePart
|
||||
value={selected.hour}
|
||||
items={hours}
|
||||
|
||||
Reference in new issue
Block a user