feat: 支持分钟级别定时#251
Conversation
There was a problem hiding this comment.
Hey - 我发现了两个问题,并提供了一些整体性的反馈:
- 触发时段的存储键已经更新为
YYYY-MM-DD-HH-mm格式,但normalizeTriggeredSlotKey和cleanTriggeredSlots不再接受旧的YYYY-MM-DD-HH键;建议保留一条临时的兼容路径,或进行一次性迁移,以避免旧的条目遗留在 localStorage 中。 - 默认时间值
08:00在多个地方被硬编码(timeDraft状态和新策略创建处);建议抽取一个共享常量,以便保持默认排程时间的一致性并更易于调整。
给 AI Agents 的提示词
Please address the comments from this code review:
## Overall Comments
- The storage key handling for triggered slots has been updated to a `YYYY-MM-DD-HH-mm` format, but `normalizeTriggeredSlotKey` and `cleanTriggeredSlots` no longer accept the old `YYYY-MM-DD-HH` keys; consider keeping a temporary compatibility path or a one-shot migration to avoid leaking old entries in localStorage.
- The default time value `08:00` is hardcoded in multiple places (`timeDraft` state and new policy creation); consider extracting a shared constant to keep the default schedule time consistent and easier to adjust.
## Individual Comments
### Comment 1
<location path="src/components/SchedulePanel.tsx" line_range="22-25" />
<code_context>
-// 小时选项 (0-23)
-const HOURS = Array.from({ length: 24 }, (_, i) => i);
+// 校验 "HH:mm" 格式
+const TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
interface SchedulePanelProps {
</code_context>
<issue_to_address>
**suggestion:** Consider normalizing the `<input type="time">` value instead of strictly rejecting values that include seconds.
Some browsers emit `HH:mm:ss` for `type="time"`, which will fail this pattern and block adding a schedule even though the value can be safely reduced to minutes. Consider normalizing `e.target.value` (e.g., `value.slice(0, 5)` or via `Date`/`Intl`) before storing it in `timeDraft`, and validate only the normalized `HH:mm` string to preserve the invariant used elsewhere.
Suggested implementation:
```typescript
// 校验规范化后的 "HH:mm" 格式
const TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
// 规范化 <input type="time"> 的值,将 "HH:mm:ss" 等形式裁剪为 "HH:mm"
const normalizeTimeValue = (value: string): string => {
if (!value) return value;
// 绝大多数浏览器时间字符串形如 "HH:mm" 或 "HH:mm:ss"
// 只要前 5 位是 "HH:mm" 形态,就裁剪前 5 位作为规范格式
if (value.length >= 5 && value[2] === ':') {
return value.slice(0, 5);
}
return value;
};
interface SchedulePanelProps {
```
To fully implement the behavior you described, you should also update the time input handling code (not visible in the snippet). Wherever you currently handle the time input (e.g. `onChange`, `onBlur`, or wherever `e.target.value` is stored into `timeDraft` or validated via `TIME_PATTERN`):
1. Normalize the raw value before using it:
- Change something like:
- `const value = e.target.value;`
- To:
- `const value = normalizeTimeValue(e.target.value);`
2. Use the normalized `value` for:
- `setTimeDraft(value);`
- Any `TIME_PATTERN.test(...)` calls.
- Any other schedule creation/update logic that relies on time strings.
3. Ensure that any initial values coming from props or external sources that represent a time value are either already `HH:mm` or also passed through `normalizeTimeValue` before being stored/validated, to keep the invariant consistent.
</issue_to_address>
### Comment 2
<location path="src/stores/appStore.ts" line_range="69-77" />
<code_context>
import type { AppState, LogEntry, TaskRunStatus } from './types';
+/** 向后兼容:将旧版整点小时 hours 迁移为 times ("HH:mm") */
+function migrateSchedulePolicies(inst: {
+ schedulePolicies?: SchedulePolicy[];
+}): SchedulePolicy[] | undefined {
+ if (!inst.schedulePolicies) return undefined;
+ return inst.schedulePolicies.map((policy) => {
+ if (policy.times) {
+ // 已是新格式,丢弃可能残留的 hours 字段
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Normalize existing `times` arrays during migration to ensure they satisfy the new invariant (sorted & deduped).
In the helper, when `policy.times` already exists you currently return it unchanged (aside from dropping `hours`). If any legacy or externally-produced `times` are unsorted or contain duplicates, they’ll violate the documented “已排序去重” contract that the UI and scheduler now assume. Consider normalizing here too, e.g. `Array.from(new Set(policy.times)).sort((a, b) => a.localeCompare(b))`, so both migrated and pre-existing configs share the same guarantees.
```suggestion
return inst.schedulePolicies.map((policy) => {
if (policy.times) {
// 已是新格式:规范化 times(排序去重),并丢弃可能残留的 hours 字段
const normalizedTimes = Array.from(new Set(policy.times)).sort((a, b) =>
a.localeCompare(b),
);
const { hours: _legacyHours, ...rest } = policy;
return { ...rest, times: normalizedTimes };
}
const times = (policy.hours ?? [])
.map((h) => `${String(h).padStart(2, '0')}:00`)
.sort((a, b) => a.localeCompare(b));
```
</issue_to_address>帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的代码审查。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- The storage key handling for triggered slots has been updated to a
YYYY-MM-DD-HH-mmformat, butnormalizeTriggeredSlotKeyandcleanTriggeredSlotsno longer accept the oldYYYY-MM-DD-HHkeys; consider keeping a temporary compatibility path or a one-shot migration to avoid leaking old entries in localStorage. - The default time value
08:00is hardcoded in multiple places (timeDraftstate and new policy creation); consider extracting a shared constant to keep the default schedule time consistent and easier to adjust.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The storage key handling for triggered slots has been updated to a `YYYY-MM-DD-HH-mm` format, but `normalizeTriggeredSlotKey` and `cleanTriggeredSlots` no longer accept the old `YYYY-MM-DD-HH` keys; consider keeping a temporary compatibility path or a one-shot migration to avoid leaking old entries in localStorage.
- The default time value `08:00` is hardcoded in multiple places (`timeDraft` state and new policy creation); consider extracting a shared constant to keep the default schedule time consistent and easier to adjust.
## Individual Comments
### Comment 1
<location path="src/components/SchedulePanel.tsx" line_range="22-25" />
<code_context>
-// 小时选项 (0-23)
-const HOURS = Array.from({ length: 24 }, (_, i) => i);
+// 校验 "HH:mm" 格式
+const TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
interface SchedulePanelProps {
</code_context>
<issue_to_address>
**suggestion:** Consider normalizing the `<input type="time">` value instead of strictly rejecting values that include seconds.
Some browsers emit `HH:mm:ss` for `type="time"`, which will fail this pattern and block adding a schedule even though the value can be safely reduced to minutes. Consider normalizing `e.target.value` (e.g., `value.slice(0, 5)` or via `Date`/`Intl`) before storing it in `timeDraft`, and validate only the normalized `HH:mm` string to preserve the invariant used elsewhere.
Suggested implementation:
```typescript
// 校验规范化后的 "HH:mm" 格式
const TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
// 规范化 <input type="time"> 的值,将 "HH:mm:ss" 等形式裁剪为 "HH:mm"
const normalizeTimeValue = (value: string): string => {
if (!value) return value;
// 绝大多数浏览器时间字符串形如 "HH:mm" 或 "HH:mm:ss"
// 只要前 5 位是 "HH:mm" 形态,就裁剪前 5 位作为规范格式
if (value.length >= 5 && value[2] === ':') {
return value.slice(0, 5);
}
return value;
};
interface SchedulePanelProps {
```
To fully implement the behavior you described, you should also update the time input handling code (not visible in the snippet). Wherever you currently handle the time input (e.g. `onChange`, `onBlur`, or wherever `e.target.value` is stored into `timeDraft` or validated via `TIME_PATTERN`):
1. Normalize the raw value before using it:
- Change something like:
- `const value = e.target.value;`
- To:
- `const value = normalizeTimeValue(e.target.value);`
2. Use the normalized `value` for:
- `setTimeDraft(value);`
- Any `TIME_PATTERN.test(...)` calls.
- Any other schedule creation/update logic that relies on time strings.
3. Ensure that any initial values coming from props or external sources that represent a time value are either already `HH:mm` or also passed through `normalizeTimeValue` before being stored/validated, to keep the invariant consistent.
</issue_to_address>
### Comment 2
<location path="src/stores/appStore.ts" line_range="69-77" />
<code_context>
import type { AppState, LogEntry, TaskRunStatus } from './types';
+/** 向后兼容:将旧版整点小时 hours 迁移为 times ("HH:mm") */
+function migrateSchedulePolicies(inst: {
+ schedulePolicies?: SchedulePolicy[];
+}): SchedulePolicy[] | undefined {
+ if (!inst.schedulePolicies) return undefined;
+ return inst.schedulePolicies.map((policy) => {
+ if (policy.times) {
+ // 已是新格式,丢弃可能残留的 hours 字段
+ const { hours: _legacyHours, ...rest } = policy;
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Normalize existing `times` arrays during migration to ensure they satisfy the new invariant (sorted & deduped).
In the helper, when `policy.times` already exists you currently return it unchanged (aside from dropping `hours`). If any legacy or externally-produced `times` are unsorted or contain duplicates, they’ll violate the documented “已排序去重” contract that the UI and scheduler now assume. Consider normalizing here too, e.g. `Array.from(new Set(policy.times)).sort((a, b) => a.localeCompare(b))`, so both migrated and pre-existing configs share the same guarantees.
```suggestion
return inst.schedulePolicies.map((policy) => {
if (policy.times) {
// 已是新格式:规范化 times(排序去重),并丢弃可能残留的 hours 字段
const normalizedTimes = Array.from(new Set(policy.times)).sort((a, b) =>
a.localeCompare(b),
);
const { hours: _legacyHours, ...rest } = policy;
return { ...rest, times: normalizedTimes };
}
const times = (policy.hours ?? [])
.map((h) => `${String(h).padStart(2, '0')}:00`)
.sort((a, b) => a.localeCompare(b));
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| const TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/; | ||
|
|
||
| interface SchedulePanelProps { | ||
| instanceId: string; |
There was a problem hiding this comment.
suggestion: 建议对 <input type="time"> 的值做规范化,而不是严格拒绝包含秒的值。
部分浏览器对 type="time" 会输出 HH:mm:ss,这会导致当前的正则匹配失败,从而阻止添加排程,但其实该值是可以安全地缩减到分钟粒度的。建议在将 e.target.value 存入 timeDraft 之前先做规范化处理(例如使用 value.slice(0, 5),或通过 Date/Intl 处理),并只对规范化后的 HH:mm 字符串做校验,以保持其它地方使用的时间格式不变。
建议的实现方式:
// 校验规范化后的 "HH:mm" 格式
const TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
// 规范化 <input type="time"> 的值,将 "HH:mm:ss" 等形式裁剪为 "HH:mm"
const normalizeTimeValue = (value: string): string => {
if (!value) return value;
// 绝大多数浏览器时间字符串形如 "HH:mm" 或 "HH:mm:ss"
// 只要前 5 位是 "HH:mm" 形态,就裁剪前 5 位作为规范格式
if (value.length >= 5 && value[2] === ':') {
return value.slice(0, 5);
}
return value;
};
interface SchedulePanelProps {为了完整实现你在描述中希望的行为,你还需要更新时间输入的处理逻辑(在当前代码片段中不可见)。在所有处理时间输入的地方(例如 onChange、onBlur,或者任何将 e.target.value 写入 timeDraft 或用 TIME_PATTERN 校验的地方):
- 在使用原始值之前先做规范化:
- 将类似下面的代码:
const value = e.target.value;
- 替换为:
const value = normalizeTimeValue(e.target.value);
- 将类似下面的代码:
- 使用规范化后的
value来:- 调用
setTimeDraft(value); - 进行任何
TIME_PATTERN.test(...)调用。 - 以及所有依赖时间字符串的排程创建/更新逻辑。
- 调用
- 确保来自 props 或外部来源的初始时间值要么已经是
HH:mm格式,要么同样先通过normalizeTimeValue再存储/校验,以保持该不变式的一致性。
Original comment in English
suggestion: Consider normalizing the <input type="time"> value instead of strictly rejecting values that include seconds.
Some browsers emit HH:mm:ss for type="time", which will fail this pattern and block adding a schedule even though the value can be safely reduced to minutes. Consider normalizing e.target.value (e.g., value.slice(0, 5) or via Date/Intl) before storing it in timeDraft, and validate only the normalized HH:mm string to preserve the invariant used elsewhere.
Suggested implementation:
// 校验规范化后的 "HH:mm" 格式
const TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
// 规范化 <input type="time"> 的值,将 "HH:mm:ss" 等形式裁剪为 "HH:mm"
const normalizeTimeValue = (value: string): string => {
if (!value) return value;
// 绝大多数浏览器时间字符串形如 "HH:mm" 或 "HH:mm:ss"
// 只要前 5 位是 "HH:mm" 形态,就裁剪前 5 位作为规范格式
if (value.length >= 5 && value[2] === ':') {
return value.slice(0, 5);
}
return value;
};
interface SchedulePanelProps {To fully implement the behavior you described, you should also update the time input handling code (not visible in the snippet). Wherever you currently handle the time input (e.g. onChange, onBlur, or wherever e.target.value is stored into timeDraft or validated via TIME_PATTERN):
- Normalize the raw value before using it:
- Change something like:
const value = e.target.value;
- To:
const value = normalizeTimeValue(e.target.value);
- Change something like:
- Use the normalized
valuefor:setTimeDraft(value);- Any
TIME_PATTERN.test(...)calls. - Any other schedule creation/update logic that relies on time strings.
- Ensure that any initial values coming from props or external sources that represent a time value are either already
HH:mmor also passed throughnormalizeTimeValuebefore being stored/validated, to keep the invariant consistent.
|
旧用户配置忽略,需要用户重新配置 |
close MaaEnd/MaaEnd#1247
opus4.8
Summary by Sourcery
通过将调度策略从按小时时间段切换为显式时间点,支持分钟级调度。
New Features:
Enhancements:
Original summary in English
Summary by Sourcery
Support minute-level scheduling by switching schedule policies from hourly slots to explicit time points.
New Features:
Enhancements: