Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6d9894975 |
No files matched your search
@@ -300,13 +300,131 @@ it("shows the default nightly evolve controls in ultra", async () => {
|
||||
"aria-checked",
|
||||
),
|
||||
).toBe("true");
|
||||
expect((screen.getByLabelText("执行时间") as HTMLInputElement).value).toBe(
|
||||
"03:00",
|
||||
);
|
||||
const timePicker = screen.getByRole("button", {
|
||||
name: "执行时间,03:00",
|
||||
});
|
||||
expect(timePicker.tagName).toBe("BUTTON");
|
||||
expect(timePicker.textContent).toContain("03:00");
|
||||
expect(screen.getByText(/只运行这一次,不改变每天的计划/)).toBeTruthy();
|
||||
expect(screen.getByRole("button", { name: "立即整合" })).toBeTruthy();
|
||||
});
|
||||
|
||||
it("opens the time picker from the whole control and saves a complete time", async () => {
|
||||
prepare("ultra");
|
||||
vi.mocked(setEvolveSchedule).mockResolvedValue({
|
||||
enabled: true,
|
||||
time: "04:30",
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
render(<MemoryModeSettings hasActiveModel onConfigureModel={vi.fn()} />);
|
||||
|
||||
const trigger = await screen.findByRole("button", {
|
||||
name: "执行时间,03:00",
|
||||
});
|
||||
await user.click(trigger);
|
||||
|
||||
const picker = screen.getByRole("dialog", { name: "执行时间" });
|
||||
await user.selectOptions(
|
||||
within(picker).getByRole("listbox", { name: "执行时间 HH" }),
|
||||
"04",
|
||||
);
|
||||
await user.selectOptions(
|
||||
within(picker).getByRole("listbox", { name: "执行时间 MM" }),
|
||||
"30",
|
||||
);
|
||||
expect(setEvolveSchedule).not.toHaveBeenCalled();
|
||||
|
||||
await user.click(within(picker).getByRole("button", { name: "保存修改" }));
|
||||
await waitFor(() =>
|
||||
expect(setEvolveSchedule).toHaveBeenCalledExactlyOnceWith({
|
||||
time: "04:30",
|
||||
}),
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(screen.getByRole("button", { name: "执行时间,04:30" })).toBe(
|
||||
document.activeElement,
|
||||
),
|
||||
);
|
||||
expect(screen.queryByRole("dialog", { name: "执行时间" })).toBeNull();
|
||||
});
|
||||
|
||||
it("keeps Tab inside the time picker and restores focus on Escape", async () => {
|
||||
prepare("ultra");
|
||||
const user = userEvent.setup();
|
||||
render(<MemoryModeSettings hasActiveModel onConfigureModel={vi.fn()} />);
|
||||
|
||||
const trigger = await screen.findByRole("button", {
|
||||
name: "执行时间,03:00",
|
||||
});
|
||||
await user.click(trigger);
|
||||
const picker = screen.getByRole("dialog", { name: "执行时间" });
|
||||
const hours = within(picker).getByRole("listbox", {
|
||||
name: "执行时间 HH",
|
||||
});
|
||||
const save = within(picker).getByRole("button", { name: "保存修改" });
|
||||
expect(document.activeElement).toBe(hours);
|
||||
await user.keyboard("{Shift>}{Tab}{/Shift}");
|
||||
expect(document.activeElement).toBe(save);
|
||||
await user.keyboard("{Tab}");
|
||||
expect(document.activeElement).toBe(hours);
|
||||
|
||||
await user.keyboard("{Escape}");
|
||||
|
||||
expect(screen.queryByRole("dialog", { name: "执行时间" })).toBeNull();
|
||||
expect(document.activeElement).toBe(trigger);
|
||||
});
|
||||
|
||||
it("keeps the selected time open for retry when saving fails", async () => {
|
||||
prepare("ultra");
|
||||
vi.mocked(setEvolveSchedule).mockRejectedValue(new Error("offline"));
|
||||
const user = userEvent.setup();
|
||||
render(<MemoryModeSettings hasActiveModel onConfigureModel={vi.fn()} />);
|
||||
|
||||
await user.click(
|
||||
await screen.findByRole("button", { name: "执行时间,03:00" }),
|
||||
);
|
||||
const picker = screen.getByRole("dialog", { name: "执行时间" });
|
||||
const minutes = within(picker).getByRole("listbox", {
|
||||
name: "执行时间 MM",
|
||||
});
|
||||
await user.selectOptions(minutes, "30");
|
||||
await user.click(within(picker).getByRole("button", { name: "保存修改" }));
|
||||
|
||||
expect(
|
||||
await screen.findByText("自动整合设置未能保存,请重试。"),
|
||||
).toBeTruthy();
|
||||
expect(screen.getByRole("dialog", { name: "执行时间" })).toBeTruthy();
|
||||
expect((minutes as HTMLSelectElement).value).toBe("30");
|
||||
await waitFor(() =>
|
||||
expect(document.activeElement).toBe(
|
||||
within(picker).getByRole("button", { name: "保存修改" }),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps the time dialog and focus stable while saving is pending", async () => {
|
||||
prepare("ultra");
|
||||
vi.mocked(setEvolveSchedule).mockReturnValue(new Promise(() => {}));
|
||||
const user = userEvent.setup();
|
||||
render(<MemoryModeSettings hasActiveModel onConfigureModel={vi.fn()} />);
|
||||
|
||||
await user.click(
|
||||
await screen.findByRole("button", { name: "执行时间,03:00" }),
|
||||
);
|
||||
const picker = screen.getByRole("dialog", { name: "执行时间" });
|
||||
await user.selectOptions(
|
||||
within(picker).getByRole("listbox", { name: "执行时间 MM" }),
|
||||
"30",
|
||||
);
|
||||
await user.click(within(picker).getByRole("button", { name: "保存修改" }));
|
||||
await waitFor(() => expect(setEvolveSchedule).toHaveBeenCalledTimes(1));
|
||||
|
||||
expect(document.activeElement).toBe(picker);
|
||||
await user.keyboard("{Escape}");
|
||||
expect(screen.getByRole("dialog", { name: "执行时间" })).toBe(picker);
|
||||
expect(document.activeElement).toBe(picker);
|
||||
});
|
||||
|
||||
it("hides the time field when automatic evolve is off and keeps the saved time visible", async () => {
|
||||
prepare("ultra");
|
||||
vi.mocked(setEvolveSchedule).mockResolvedValue({
|
||||
@@ -322,7 +440,7 @@ it("hides the time field when automatic evolve is off and keeps the saved time v
|
||||
await user.click(automatic);
|
||||
|
||||
await screen.findByText(/重新开启后仍按 03:00 运行/);
|
||||
expect(screen.queryByLabelText("执行时间")).toBeNull();
|
||||
expect(screen.queryByRole("button", { name: /执行时间/ })).toBeNull();
|
||||
expect(screen.getByRole("button", { name: "立即整合" })).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import { MemoryModeChoiceList } from "./MemoryModeChoiceList";
|
||||
import { useOnboardingTranslation } from "./onboarding-language";
|
||||
import { R6Button, R6Switch } from "./R6Controls";
|
||||
import { R6StateRecovery } from "./R6StateRecovery";
|
||||
import { TimePicker } from "./TimePicker";
|
||||
|
||||
const MODE_COPY: Record<
|
||||
MemoryPipelineMode,
|
||||
@@ -332,8 +333,10 @@ export function MemoryModeSettings({
|
||||
setError("");
|
||||
try {
|
||||
setScheduleState(await setEvolveSchedule(update));
|
||||
return true;
|
||||
} catch {
|
||||
setError("自动整合设置未能保存,请重试。");
|
||||
return false;
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -455,14 +458,12 @@ export function MemoryModeSettings({
|
||||
</label>
|
||||
<p>{text("使用这台设备的本地时间。")}</p>
|
||||
</div>
|
||||
<input
|
||||
<TimePicker
|
||||
id={`${id}-evolve-time`}
|
||||
type="time"
|
||||
label={text("执行时间")}
|
||||
value={schedule.time}
|
||||
disabled={busy || maintenance}
|
||||
onChange={(event) =>
|
||||
void changeSchedule({ time: event.target.value })
|
||||
}
|
||||
onChange={(time) => changeSchedule({ time })}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
import { Check, ChevronDown, Clock3 } from "lucide-react";
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useOnboardingTranslation } from "./onboarding-language";
|
||||
import { R6Button } from "./R6Controls";
|
||||
|
||||
const HOURS = Array.from({ length: 24 }, (_, value) =>
|
||||
value.toString().padStart(2, "0"),
|
||||
);
|
||||
const MINUTES = Array.from({ length: 60 }, (_, value) =>
|
||||
value.toString().padStart(2, "0"),
|
||||
);
|
||||
|
||||
function splitTime(value: string): [string, string] {
|
||||
const match = /^([01]\d|2[0-3]):([0-5]\d)$/.exec(value);
|
||||
return match ? [match[1], match[2]] : ["00", "00"];
|
||||
}
|
||||
|
||||
export function TimePicker({
|
||||
id,
|
||||
label,
|
||||
value,
|
||||
disabled = false,
|
||||
onChange,
|
||||
}: {
|
||||
id: string;
|
||||
label: string;
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
onChange(value: string): Promise<boolean>;
|
||||
}) {
|
||||
const { locale, text } = useOnboardingTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [placement, setPlacement] = useState<"above" | "below">("above");
|
||||
const [hour, setHour] = useState(() => splitTime(value)[0]);
|
||||
const [minute, setMinute] = useState(() => splitTime(value)[1]);
|
||||
const root = useRef<HTMLDivElement>(null);
|
||||
const trigger = useRef<HTMLButtonElement>(null);
|
||||
const panel = useRef<HTMLElement>(null);
|
||||
const hourSelect = useRef<HTMLSelectElement>(null);
|
||||
const minuteSelect = useRef<HTMLSelectElement>(null);
|
||||
const restoreTriggerFocus = useRef(false);
|
||||
const restoreSaveFocus = useRef(false);
|
||||
const savingRef = useRef(false);
|
||||
savingRef.current = saving;
|
||||
const panelId = `${id}-picker`;
|
||||
|
||||
const close = useCallback((restoreFocus = true) => {
|
||||
setOpen(false);
|
||||
restoreTriggerFocus.current = restoreFocus;
|
||||
}, []);
|
||||
|
||||
function show() {
|
||||
const [nextHour, nextMinute] = splitTime(value);
|
||||
setHour(nextHour);
|
||||
setMinute(nextMinute);
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const closeOutside = (event: MouseEvent) => {
|
||||
if (savingRef.current) return;
|
||||
const target = event.target as Element | null;
|
||||
const label = target?.closest("label");
|
||||
if (label instanceof HTMLLabelElement && label.htmlFor === id) return;
|
||||
if (!root.current?.contains(target)) close(false);
|
||||
};
|
||||
const closeOnEscape = (event: KeyboardEvent) => {
|
||||
if (event.key !== "Escape" || savingRef.current) return;
|
||||
event.preventDefault();
|
||||
close();
|
||||
};
|
||||
document.addEventListener("mousedown", closeOutside);
|
||||
document.addEventListener("keydown", closeOnEscape);
|
||||
hourSelect.current?.focus();
|
||||
hourSelect.current?.selectedOptions[0]?.scrollIntoView?.({
|
||||
block: "nearest",
|
||||
});
|
||||
minuteSelect.current?.selectedOptions[0]?.scrollIntoView?.({
|
||||
block: "nearest",
|
||||
});
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", closeOutside);
|
||||
document.removeEventListener("keydown", closeOnEscape);
|
||||
};
|
||||
}, [close, id, open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (disabled && open && !saving) close(false);
|
||||
if (!disabled && !open && restoreTriggerFocus.current) {
|
||||
restoreTriggerFocus.current = false;
|
||||
trigger.current?.focus();
|
||||
}
|
||||
if (!disabled && open && restoreSaveFocus.current) {
|
||||
restoreSaveFocus.current = false;
|
||||
panel.current
|
||||
?.querySelector<HTMLButtonElement>("[data-time-picker-save]")
|
||||
?.focus();
|
||||
}
|
||||
if (saving && open) panel.current?.focus();
|
||||
}, [close, disabled, open, saving]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!open) return;
|
||||
const place = () => {
|
||||
const anchor = trigger.current?.getBoundingClientRect();
|
||||
const picker = panel.current?.getBoundingClientRect();
|
||||
if (!anchor || !picker) return;
|
||||
const scroller = trigger.current?.closest(
|
||||
".app-content, .onboarding-frame",
|
||||
);
|
||||
const bounds = scroller?.getBoundingClientRect();
|
||||
const topEdge = (bounds ? Math.max(0, bounds.top) : 0) + 12;
|
||||
const bottomEdge =
|
||||
(bounds
|
||||
? Math.min(window.innerHeight, bounds.bottom)
|
||||
: window.innerHeight) - 12;
|
||||
const above = anchor.top - topEdge;
|
||||
const below = bottomEdge - anchor.bottom;
|
||||
setPlacement(
|
||||
above >= picker.height || above >= below ? "above" : "below",
|
||||
);
|
||||
};
|
||||
place();
|
||||
window.addEventListener("resize", place);
|
||||
window.addEventListener("scroll", place, true);
|
||||
return () => {
|
||||
window.removeEventListener("resize", place);
|
||||
window.removeEventListener("scroll", place, true);
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
async function save() {
|
||||
const next = `${hour}:${minute}`;
|
||||
if (next === value) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
setSaving(true);
|
||||
const saved = await onChange(next);
|
||||
setSaving(false);
|
||||
if (saved) close();
|
||||
else restoreSaveFocus.current = true;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="r6-time-picker" ref={root}>
|
||||
<button
|
||||
ref={trigger}
|
||||
id={id}
|
||||
type="button"
|
||||
className="r6-time-picker-trigger"
|
||||
aria-label={
|
||||
locale.startsWith("zh") ? `${label},${value}` : `${label}: ${value}`
|
||||
}
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded={open}
|
||||
aria-controls={panelId}
|
||||
disabled={disabled}
|
||||
onClick={() => (open ? close() : show())}
|
||||
>
|
||||
<span>{value}</span>
|
||||
<span className="r6-time-picker-icons" aria-hidden="true">
|
||||
<Clock3 size={15} />
|
||||
<ChevronDown size={14} />
|
||||
</span>
|
||||
</button>
|
||||
{open ? (
|
||||
<section
|
||||
ref={panel}
|
||||
id={panelId}
|
||||
className="r6-time-picker-panel"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label={label}
|
||||
tabIndex={-1}
|
||||
data-placement={placement}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key !== "Tab") return;
|
||||
const controls = Array.from(
|
||||
event.currentTarget.querySelectorAll<HTMLElement>(
|
||||
"select:not(:disabled), button:not(:disabled)",
|
||||
),
|
||||
);
|
||||
const first = controls[0];
|
||||
const last = controls.at(-1);
|
||||
if (!first || !last) {
|
||||
event.preventDefault();
|
||||
event.currentTarget.focus();
|
||||
return;
|
||||
}
|
||||
if (event.shiftKey && document.activeElement === first) {
|
||||
event.preventDefault();
|
||||
last.focus();
|
||||
} else if (!event.shiftKey && document.activeElement === last) {
|
||||
event.preventDefault();
|
||||
first.focus();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="r6-time-picker-columns">
|
||||
<label className="r6-time-picker-column">
|
||||
<span aria-hidden="true">HH</span>
|
||||
<select
|
||||
ref={hourSelect}
|
||||
size={6}
|
||||
aria-label={`${label} HH`}
|
||||
value={hour}
|
||||
disabled={saving}
|
||||
onChange={(event) => setHour(event.target.value)}
|
||||
>
|
||||
{HOURS.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<span className="r6-time-picker-separator" aria-hidden="true">
|
||||
:
|
||||
</span>
|
||||
<label className="r6-time-picker-column">
|
||||
<span aria-hidden="true">MM</span>
|
||||
<select
|
||||
ref={minuteSelect}
|
||||
size={6}
|
||||
aria-label={`${label} MM`}
|
||||
value={minute}
|
||||
disabled={saving}
|
||||
onChange={(event) => setMinute(event.target.value)}
|
||||
>
|
||||
{MINUTES.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<footer>
|
||||
<R6Button disabled={saving} onClick={() => close()}>
|
||||
{text("取消")}
|
||||
</R6Button>
|
||||
<R6Button
|
||||
variant="primary"
|
||||
pending={saving}
|
||||
data-time-picker-save
|
||||
onClick={() => void save()}
|
||||
>
|
||||
<Check size={15} aria-hidden="true" />
|
||||
{text("保存修改")}
|
||||
</R6Button>
|
||||
</footer>
|
||||
</section>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -134,11 +134,6 @@
|
||||
background: transparent;
|
||||
color: #4e6478;
|
||||
}
|
||||
.model-settings .generation-technical-toggle:hover:not(:disabled) {
|
||||
border-color: transparent;
|
||||
background: #e9f0f4;
|
||||
color: #304757;
|
||||
}
|
||||
.model-settings .generation-technical-fields {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
@@ -295,7 +295,7 @@
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #e1e8ed;
|
||||
}
|
||||
.r6-settings .r6-evolve-time label {
|
||||
.r6-settings .r6-evolve-time > div > label {
|
||||
color: #304757;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
@@ -304,20 +304,146 @@
|
||||
.r6-evolve-time p {
|
||||
margin-top: 1px;
|
||||
}
|
||||
.r6-evolve-settings input[type="time"] {
|
||||
.r6-time-picker {
|
||||
position: relative;
|
||||
width: 180px;
|
||||
}
|
||||
.r6-time-picker-trigger {
|
||||
box-sizing: border-box;
|
||||
width: 180px;
|
||||
min-height: 44px;
|
||||
padding: 0 10px;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 0 12px 0 14px;
|
||||
border: 1px solid #c6d2da;
|
||||
border-radius: 8px;
|
||||
color: #304757;
|
||||
background: #fff;
|
||||
font:
|
||||
500 14px / 22px "Noto Sans SC",
|
||||
"PingFang SC",
|
||||
sans-serif;
|
||||
font-variant-numeric: tabular-nums;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 140ms ease-out,
|
||||
background-color 140ms ease-out,
|
||||
box-shadow 140ms ease-out;
|
||||
}
|
||||
.r6-evolve-settings input[type="time"]:focus-visible {
|
||||
.r6-time-picker-trigger:hover:not(:disabled) {
|
||||
border-color: #9eafbb;
|
||||
background: #f8fafb;
|
||||
}
|
||||
.r6-time-picker-trigger[aria-expanded="true"] {
|
||||
border-color: #7892a6;
|
||||
box-shadow: 0 0 0 3px rgb(85 124 147 / 12%);
|
||||
}
|
||||
.r6-time-picker-trigger:focus-visible {
|
||||
outline: 2px solid var(--r6-focus);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.r6-time-picker-trigger:disabled {
|
||||
color: #71808b;
|
||||
background: #edf0f3;
|
||||
cursor: default;
|
||||
}
|
||||
.r6-time-picker-trigger:active:not(:disabled) {
|
||||
transform: none;
|
||||
}
|
||||
.r6-time-picker-icons {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
color: #607888;
|
||||
}
|
||||
.r6-time-picker-trigger[aria-expanded="true"]
|
||||
.r6-time-picker-icons
|
||||
svg:last-child {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.r6-time-picker-panel {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
z-index: 1200;
|
||||
right: 0;
|
||||
bottom: calc(100% + 8px);
|
||||
width: min(280px, calc(100vw - 32px));
|
||||
padding: 14px;
|
||||
border: 1px solid #c6d2da;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
box-shadow: 0 14px 36px rgb(48 71 87 / 16%);
|
||||
}
|
||||
.r6-time-picker-panel[data-placement="below"] {
|
||||
top: calc(100% + 8px);
|
||||
bottom: auto;
|
||||
}
|
||||
.r6-time-picker-columns {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
||||
align-items: end;
|
||||
gap: 8px;
|
||||
}
|
||||
.r6-time-picker-column {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
color: #5e6e7a;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
.r6-time-picker-columns select {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 174px;
|
||||
padding: 4px;
|
||||
border: 1px solid #d5dfe8;
|
||||
border-radius: 7px;
|
||||
color: #304757;
|
||||
background: #f8fafb;
|
||||
font:
|
||||
500 14px / 24px "Noto Sans SC",
|
||||
"PingFang SC",
|
||||
sans-serif;
|
||||
font-variant-numeric: tabular-nums;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #b6c3cc transparent;
|
||||
}
|
||||
.r6-time-picker-columns select:focus-visible {
|
||||
outline: 2px solid var(--r6-focus);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.r6-time-picker-columns option {
|
||||
min-height: 28px;
|
||||
padding: 5px 8px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.r6-time-picker-columns option:checked {
|
||||
color: #304757;
|
||||
background: #dfe9ef;
|
||||
}
|
||||
.r6-time-picker-separator {
|
||||
align-self: center;
|
||||
margin-top: 22px;
|
||||
color: #7892a6;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.r6-time-picker-panel footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #e1e8ed;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.r6-time-picker-panel footer .r6-button {
|
||||
min-width: 68px;
|
||||
}
|
||||
.r6-state-recovery {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -1059,9 +1185,14 @@
|
||||
.r6-evolve-time {
|
||||
gap: 8px;
|
||||
}
|
||||
.r6-evolve-settings input[type="time"] {
|
||||
.r6-time-picker,
|
||||
.r6-time-picker-trigger {
|
||||
width: min(220px, 100%);
|
||||
}
|
||||
.r6-time-picker-panel {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
.r6-state-recovery {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -9,7 +9,6 @@ import hashlib
|
||||
import json
|
||||
import math
|
||||
import random
|
||||
from datetime import datetime
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
@@ -462,8 +461,6 @@ async def _two_spaces(path):
|
||||
"""A live 1024 space and a stray 512 space sharing some ids."""
|
||||
from hy_memory.data.vector_store_chroma import ChromaVectorStore
|
||||
|
||||
stable_time = datetime(2025, 1, 1)
|
||||
|
||||
async def _store(base, dims, model, contents):
|
||||
cfg = MemoryConfig()
|
||||
cfg.vector_store.provider = "chroma"
|
||||
@@ -478,7 +475,6 @@ async def _two_spaces(path):
|
||||
node = MemoryNode(
|
||||
node_id=node_id, user_id=user, agent_id="a", session_id="s",
|
||||
layer=MemoryLayer.L2_FACT, content=content, status=status,
|
||||
gmt_created=stable_time, gmt_modified=stable_time, valid_from=stable_time,
|
||||
)
|
||||
node.embedding = emb.vector(content)
|
||||
nodes.append(node)
|
||||
|
||||
Reference in new issue
Block a user