Skip to content

feat(frontend): classic skin default + frontend UI rework - #4914

Open
LittleChenLiya wants to merge 37 commits into
bytedance:mainfrom
LittleChenLiya:feat/frontend-ui-rework
Open

feat(frontend): classic skin default + frontend UI rework#4914
LittleChenLiya wants to merge 37 commits into
bytedance:mainfrom
LittleChenLiya:feat/frontend-ui-rework

Conversation

@LittleChenLiya

@LittleChenLiya LittleChenLiya commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

概述

前端 UI 重构(含经典皮肤外观)。

改动内容

侧边栏精简

设置面板重构

  • 记忆:精简为搜索 + 工具栏 + 事实列表(移除表格摘要块)

定时任务页重设计

  • 删去推荐创建模板
  • 任务以卡片网格展示,详情在弹窗中查看
  • 空状态:「还没有定时任务」+ 日历图标 + 新建按钮;筛选无匹配时显示轻量空状态
  • 新建定时任务:独立创建页面,年/月/日 日期选择
  • 时区下拉:使用系统支持的完整时区列表

其他

  • 设置子页面错误状态统一(loadError i18n + text-destructive 样式)

截图

image image image image

验证

  • ✅ pnpm test 通过
  • ✅ pnpm typecheck 通过
  • ✅ pnpm lint 通过
  • ✅ pnpm format 通过

相关 PR

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the skin framework, scheduled-task redesign, and settings rework against head 10fa3f8. The skin module (types/storage/context) is clean and well-tested, but I found one functional bug in the zh-CN skill localization (the enable toggle sends the display name to the API), a status-dot lookup bug in the new task cards, a memory-import validation regression that can crash the settings page, and the scheduled-tasks e2e specs were left stale after the UI rework. Smaller notes: core/scheduled-tasks/recipes.ts, the scheduledTasks.recipes/unused filters.* i18n keys, and describeSchedule (now only referenced by its own unit test) survive as dead code after the recipe UI was dropped; about-settings-page.tsx uses locale === "zh-CN" where the rest of the codebase uses locale.startsWith("zh"); and the no-op PageEnter wrapper plus the unused prefersReducedMotion export could wait for PR #4915. Per the repo's test policy, please update the e2e specs and add coverage for the new /workspace/scheduled-tasks/new create flow.

Comment thread frontend/src/components/workspace/settings/skill-settings-page.tsx Outdated
Comment thread frontend/src/app/workspace/scheduled-tasks/page.tsx Outdated
Comment thread frontend/src/components/workspace/settings/memory-settings-page.tsx Outdated
Comment thread frontend/src/app/workspace/scheduled-tasks/page.tsx
Comment thread frontend/src/app/workspace/scheduled-tasks/page.tsx

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at the new head d46dcb7 (previous review was against 10fa3f8). Since then the PR was stripped down — core/skins/ (SkinProvider/storage/applySkinToDocument/SKIN_IDS), chats-settings-page.tsx, and the skill/about localizations are all gone — but the PR description still advertises every one of those (skin framework with persistence and data-skin switching, chats embedded in the settings menu, skills/about translated to Chinese). The description and screenshots no longer match the diff: what remains of the skin feature is a static, non-selectable SkinCard plus the skins.classic i18n keys, so PR #4915 will have nothing to build on here. Please update the description before merge. Status of the earlier findings at this head: the localizeSkill enable-toggle bug is resolved by reverting the skill translation, while the status-dot lookup in StatusBadge, the weakened isImportedMemory validation, the stale frontend/tests/e2e/scheduled-tasks.spec.ts, and the filtered-vs-empty state on the tasks page are all still present (existing comments cover them). One new inline finding below on the timezone dropdown, which was not in the previously reviewed scope.

Comment thread frontend/src/components/workspace/scheduled-task-schedule-input.tsx Outdated
@LittleChenLiya
LittleChenLiya marked this pull request as ready for review August 21, 2026 05:29
@github-actions github-actions Bot added area:frontend Next.js frontend under frontend/ risk:medium Medium risk: regular code changes size/XL PR changes 700+ lines labels Aug 21, 2026

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review at head 40bcb8e (third pass; earlier reviews were against 10fa3f8 and d46dcb7). All six prior findings are resolved at this head: the zh-CN skill localization moved out to #4915, StatusBadge now receives the raw status, the memory import re-validates all six sections via isImportedMemory, the scheduled-tasks e2e suite was rewritten for the card-grid/detail-dialog UI, the filtered empty state now distinguishes no-matches from no-tasks, and the timezone dropdown restores the full Intl.supportedValuesOf list behind a common/all toggle. The remaining items are coordination/coverage notes, not blockers: the sidebar Chats removal makes the conversation-list page unreachable until #4922 lands, and the new one-time date validation has no unit coverage.

Comment thread frontend/src/components/workspace/workspace-nav-chat-list.tsx
Comment thread frontend/src/components/workspace/scheduled-task-schedule-input.tsx Outdated

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fourth pass at head 40bcb8e (unchanged since the last review yesterday). Two new inline findings: the split-out create page drops the ?thread_id= context that the old inline form used to pre-fill reuse_thread + the target thread id, and the new year/month/day inputs lost the accessible name the old datetime-local input had. Everything else from the earlier passes still holds at this head — the six prior findings remain fixed, and the open coordination/coverage notes (conversation-list entry point pending #4922, unit coverage for the one-time date validation) are unchanged.

Comment thread frontend/src/app/workspace/scheduled-tasks/page.tsx Outdated
Comment thread frontend/src/components/workspace/scheduled-task-schedule-input.tsx

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fifth pass, re-reviewed at head 8687d5e. The three open items from the last review are all resolved here: both create entry points now forward ?thread_id= and seed reuse_thread + the target thread id on the new create page, the year/month/day inputs carry aria-label, and buildOnceRunAtLocal was extracted into cron.ts with unit coverage for Feb 29/30, month bounds, empty time, pre-1970, and the zoned round-trip. Also verified the input-box.tsx change is a pure wrapper (<div className="relative">) plus prettier reformatting with no behavioral delta, and the new sidebar e2e regexes match the actual i18n strings at this head. Two small new notes below: the edit dialog can save an empty once-spec (the create page guards this, the edit path does not), and the new FilterChips convey their toggle state only through color.

timezone: editSchedule.timezone || "UTC",
})
}
disabled={updateTask.isPending}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: the create page disables submit while the schedule is incomplete (!createSchedule.schedule_spec.cron && !createSchedule.schedule_spec.run_at), but this edit submit only checks updateTask.isPending. With the one-time date now split into four inputs, clearing any one of them makes the schedule input emit schedule_spec: {} (see the runAt ? { run_at: runAt } : {} branch in scheduled-task-schedule-input.tsx), and Save then PUTs an empty spec — the user just gets the generic update-failure toast with no pointer at which field is wrong. Consider mirroring the create guard here, e.g. disabled={updateTask.isPending || (!editSchedule.schedule_spec.cron && !editSchedule.schedule_spec.run_at)} (title/prompt too, if you want full parity).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in cbc1c00: the edit submit now mirrors the create guard — disabled={updateTask.isPending || (!editSchedule.schedule_spec.cron && !editSchedule.schedule_spec.run_at)} — so Save cannot PUT an empty spec when any once-spec field is cleared.

}) {
return (
<button
type="button"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (a11y): the Enabled/Paused chips behave as toggle buttons, but their state is conveyed only by the primary-colored fill, so a screen reader announces them as plain buttons with no on/off state. Adding aria-pressed={active} would announce the active filter. (The card corner dots and StatusBadge dots are already correctly aria-hidden.)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in cbc1c00: added aria-pressed={active} to the Enabled/Paused FilterChips so screen readers announce the toggle state.

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sixth pass, re-reviewed at head cbc1c00 (after 10fa3f8 / d46dcb7 / 40bcb8e / 8687d5e). Everything raised in earlier rounds is resolved at this head: the StatusBadge dot lookup, the memory import re-validation, the timezone list with the common/all toggle, the thread_id forwarding into the new create page, the edit-submit guard, and the FilterChip aria-pressed. Three new findings on the final state, all around the scheduled-task page: (1) the new empty state renders during the initial fetch because hasTasks is derived from data before the query resolves, (2) the create page's formError path is unreachable and an invalid one-time date gives no feedback now that the schedule preview is gone, and (3) the detail dialog dropped the lastRunId/lastError rows but their i18n keys were left behind and task.last_error is no longer surfaced anywhere.

timezone: "UTC",
});
const [createNonce, setCreateNonce] = useState(0);
const hasTasks = (data ?? []).length > 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loading flash: hasTasks is derived from data, which is undefined until the react-query fetch resolves (useScheduledTasks/useThreadScheduledTasks have no placeholderData), so on every initial load hasTasks is false, queryError is null, and the filteredData.length === 0 branch renders the full "No scheduled tasks yet" empty state with the create CTA — only for it to be replaced by the card grid a moment later. The earlier fix in this thread distinguished no-tasks from no-matches, but not pending from empty. Gating on the query state (e.g. const isLoading = threadId ? threadTasksQuery.isPending : allTasksQuery.isPending and skipping the empty state while it's true) would keep the flash out.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1b08bc7 — the empty states are now gated on the query state: while the react-query fetch is pending, isLoading renders a loading placeholder instead of the "No scheduled tasks yet" block, so the empty state (and its create CTA) can no longer flash before the first data resolves.

!hasSchedule ||
(contextMode === "reuse_thread" && !targetThreadId)
) {
setFormError(st.create.fillRequired);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This validation and the formError render below are unreachable: the submit button is disabled under exactly the same conditions (!title || !prompt || (!cron && !run_at) || (reuse_thread && !targetThreadId)), so handleCreate can never hit the setFormError branch. That matters more now that the schedule preview (data-testid="schedule-preview") is gone: with the once schedule split into year/month/day inputs, an invalid combination (Feb 30, month 13, or an untouched time field) makes buildOnceRunAtLocal return "" and the button silently greys out with no explanation. Consider dropping the dead branch and instead showing a hint when createSchedule.schedule_type === "once" && !createSchedule.schedule_spec.run_at (e.g. "Enter a valid date and time"), which the old preview line used to communicate.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1b08bc7 — the dead validation branch and formError render were dropped from the create page, and the once-type hint now renders under the schedule input (schedule_type === "once" && !schedule_spec.run_at) showing st.create.invalidOnce ("Enter a valid date and time"), which covers the invalid-date cases the old preview line used to communicate.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1b08bc7: dropped the unreachable formError branch. The create page now shows the "Enter a valid date and time" hint under the schedule input whenever the once spec is incomplete (once with no run_at), restoring the discoverability the removed schedule-preview line provided.

@LittleChenLiya LittleChenLiya Aug 30, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed - the dead formError branch is gone (the submit guard already covered those cases); the once-type missing-date case now renders a reachable invalidOnce hint under the schedule input instead (acca5a2).

value: formatTimestamp(task.next_run_at, locale),
},
{
label: stDetail.lastRun,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the reworked detail rows stop after lastRun, but the old dialog also showed lastRunId and lastError — the i18n keys scheduledTasks.detail.lastRunId/lastError are still present in en-US.ts (lines 387-388), zh-CN.ts, and types.ts, and are now unreferenced. More substantively, task.last_error is no longer surfaced anywhere on the page (only the per-run run.error inside the run list), so a failed task whose runs have rolled out of the list has no visible error. Either remove the two dead keys, or keep a lastError row (it's the most useful field when a task shows the red status dot).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1b08bc7 + 532a891 — TaskDetail now keeps a lastError row (task.last_error ?? NONE), so a failed task's error stays visible even after its runs roll out of the list; the orphan lastRunId key was dropped from en-US/zh-CN/types.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1b08bc7 + 532a891: TaskDetail now keeps a lastError row (task.last_error ?? em-dash fallback) so a failed task still surfaces its error even after its runs roll out of the list, and the now-unreferenced scheduledTasks.detail.lastRunId key is dropped from en-US/zh-CN/types.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed - the detail dialog now also renders a lastError row (surfaced from task.last_error), and the orphaned lastRunId i18n key was dropped (acca5a2 / 4c22a5e).

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seventh pass, re-reviewed at head 1b08bc7 (one commit since cbc1c00: the empty-state / form-error / lastError fixes). All three findings from the last round verified fixed: the empty state is now gated on the correct per-branch isPending (the disabled thread query is never consulted for the flag, and cached data keeps the 15s refetch interval from re-flashing the loading state), the unreachable formError branch is gone and replaced by an invalidOnce hint with keys added to all three locale files, and the detail dialog surfaces task.last_error again. Two small notes below on the final state.

prompt: "Prompt",
submit: "Create",
fillRequired: "Fill all required fields",
invalidOnce: "Enter a valid date and time",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: now that the unreachable formError branch is gone, scheduledTasks.create.fillRequired has no remaining references (en-US.ts:369, zh-CN.ts:353, types.ts:291) — the old inline create form on main was its last consumer. It can be dropped the same way t.sidebar.chats was in 8687d5e (scheduledTasks.detail.lastRunId is the other orphan, already noted in the detail-rows thread).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b8d2a6f — the orphan fillRequired key was dropped from en-US.ts/zh-CN.ts/types.ts (same sweep that removed t.sidebar.chats in 8687d5e). lastRunId was cleaned up in 532a891.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b8d2a6f: scheduledTasks.create.fillRequired is removed from en-US/zh-CN/types, same orphan sweep as t.sidebar.chats in 8687d5e.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed - scheduledTasks.create.fillRequired and the last remaining references were removed (49b5ad7).

}
disabled={
updateTask.isPending ||
(!editSchedule.schedule_spec.cron &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: the new invalidOnce hint was added on the create page only, but the edit dialog has the same silent state. For a once-type task, clearing any of the year/month/day/time fields makes the schedule input emit schedule_spec: {}, and Save greys out via this guard with no explanation — the discoverability gap the create-page hint just fixed. Rendering the same hint under the edit ScheduledTaskScheduleInput (e.g. when editSchedule.schedule_type === "once" && !editSchedule.schedule_spec.run_at) would give edit-mode parity with /workspace/scheduled-tasks/new.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b8d2a6f — the edit dialog now renders the same hint under the ScheduledTaskScheduleInput when editSchedule.schedule_type === "once" && !editSchedule.schedule_spec.run_at (using st.edit.invalidOnce), giving edit mode parity with the create page.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b8d2a6f: the edit dialog now renders the same hint under the schedule input when the once spec is incomplete (editSchedule.schedule_type once with no run_at), giving edit-mode parity with /workspace/scheduled-tasks/new.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed - the edit dialog now mirrors the create guard: it renders the invalidOnce hint when a once schedule lacks run_at and keeps submit disabled until the schedule is complete (49b5ad7).

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eighth pass, re-reviewed at head 532a891 (39fa5c9 / b8d2a6f / 532a891 since the last review at 1b08bc7). Everything raised across the seven earlier rounds is verified resolved at this head: the TaskDetail lastError row with the ?? fallback, the invalidOnce hint mirrored in the edit dialog, the orphan fillRequired / lastRunId / sidebar.chats keys dropped from all three locale files, the timezone common/all toggle whose COMMON_TIMEZONES list matches the new i18n timezone.common map, and the empty states gated on the per-branch isPending (create errors still surface via the useCreateScheduledTask onError toast, confirmed in hooks.ts). The input-box.tsx change is a pure re-indent inside a new relative wrapper with no behavioral diff, and the globals.css classic palette / message-bubble / settings-dialog restyling look internally consistent. Two new minor suggestions below.

schedule_spec: createSchedule.schedule_spec,
timezone: createSchedule.timezone || "UTC",
},
{ onSuccess: () => router.push("/workspace/scheduled-tasks") },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: the inbound half of the thread_id round-trip is handled (createHref on the list page forwards ?thread_id=, and this page seeds reuse_thread + the target id), but every exit from this page hardcodes the unfiltered URL — this success callback, plus the Back button (line 58) and Cancel (line 127). A user arriving from a thread's "Scheduled tasks" link who creates the task, or just navigates back, lands on the global list and loses the thread context this page deliberately preserved. Since initialThreadId is already in scope, router.push(initialThreadId ? /workspace/scheduled-tasks?thread_id=${encodeURIComponent(initialThreadId)} : "/workspace/scheduled-tasks") (or plain router.back() for Back/Cancel) would keep the filter symmetrical.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d8da426. Create success, Back, and Cancel now all return to listHref, which keeps ?thread_id= when the page was seeded from inbound query params.


await page.getByPlaceholder("Task title").fill("Created from UI");
await page.getByPlaceholder("Prompt").fill("Summarize thread");
await page.getByRole("button", { name: "Create", exact: true }).click();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage: this test creates with the default recurring schedule only — nothing in the reworked spec clicks the One-time toggle or fills the new year/month/day/time inputs, so the once branch of the redesigned create flow (the split date inputs, the invalidOnce hint, and the run_at submission path on /workspace/scheduled-tasks/new) has no e2e coverage. Worth noting because an earlier fix reply on this PR states the year/month/day create flow "has its own e2e coverage", which the spec at this head does not actually provide — buildOnceRunAtLocal's unit tests cover the date math, but not the UI wiring. Extending this test to switch to One-time, clear one field (assert the hint + disabled Create), then fill a valid date would lock in the most logic-dense part of the redesign.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d8da426. Added an e2e that switches to One-time, asserts the invalidOnce hint + disabled Create, fills year/month/day/time, then submits. Also added a filtered-list round-trip test for inbound thread_id.

@WillemJiang

Copy link
Copy Markdown
Collaborator

@LittleChenLiya please fix the conflicts with the main branch.

Make the new warm-cream workspace appearance the default DeerFlow look, including refreshed message, welcome, and settings chrome.
Stack Observatory on the new classic workspace look as an opt-in skin, keeping classic as the default.
…error state, memory empty state, once-schedule validation
- Scheduled task timezone select defaults to a short localized common list
  and expands to the full IANA set via a More timezones toggle; labels go
  through i18n (en-US/zh-CN) instead of hardcoded zh text.
- Raise the chat header z-index so the mobile sidebar trigger stays
  clickable above the welcome layout.
- Gate the scheduled-tasks empty states on !queryError so a failed load
  doesn't also advertise an empty list.
- Adapt e2e specs to the slimmed sidebar (Scheduled Tasks instead of
  Chats, updated welcome copy) and disambiguate the header link via its
  aria-label.
- Mock token-usage and workspace-changes endpoints as 404 to prevent the
  fetcher's 401 login redirect from firing when the backend is absent.
…ation, a11y label

- Forward the ?thread_id= context into /workspace/scheduled-tasks/new and seed reuse_thread + target thread id. - Extract one-time run-at validation into buildOnceRunAtLocal in cron.ts with rstest coverage. - Add aria-label to once year/month/day inputs. - Drop unused t.sidebar.chats i18n key.
…main

Re-apply the bytedance#4918 ReuseThreadNotice (alert) in the redesigned scheduled-tasks UI: detail dialog shows it for reuse_thread tasks, the create page shows it when reuse_thread is selected. Adds scheduled-task-detail/scheduled-task-create-form testids and adapts the e2e spec to the new layout.
@LittleChenLiya
LittleChenLiya force-pushed the feat/frontend-ui-rework branch from 532a891 to 78f3d19 Compare August 24, 2026 11:08
…in reuse-thread e2e

The dialog overlay intercepts the create-toggle click; press Escape and await dialog dismissal first.
<header className="flex shrink-0 items-center justify-between gap-3 border-b px-4 py-3">
<div className="flex items-center gap-3">
<Button
variant="ghost"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Give the back button an accessible name

This icon-only navigation button has no aria-label or other accessible name, so assistive technology announces it only as an unnamed button. Please add a localized label such as “Back to scheduled tasks” (and optionally a matching title).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d8da426. The icon-only back button now has a localized aria-label/title (st.create.back: "Back to scheduled tasks" / "返回定时任务").

… cover one-time e2e

Round-trip inbound thread_id on Back/Cancel/success from /workspace/scheduled-tasks/new. Give the icon-only back button a localized aria-label. Add e2e for the once schedule year/month/day/time path and for returning to the filtered list.

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed the latest fixes at d8da426; one new edge-case issue found.

schedule_spec: createSchedule.schedule_spec,
timezone: createSchedule.timezone || "UTC",
},
{ onSuccess: () => router.push(listHref) },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Return to a list that can contain the created task

listHref is always derived from the inbound initialThreadId, but this form lets the user switch to fresh_thread_per_run or edit targetThreadId. In those cases the POST creates a task with thread_id: null or a different thread ID, then success navigates back to the original thread's filtered list, where the new task cannot appear. Keeping listHref for Back/Cancel makes sense, but the success destination should follow the submitted context (global for fresh tasks, or the chosen target for reuse tasks). The new round-trip test covers only the unchanged pre-seeded thread ID, so it misses both branches.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bd73174. Back/Cancel still use inbound listHref. Create success now follows the submitted context: global list for fresh_thread_per_run, or ?thread_id= of the chosen targetThreadId for reuse_thread. Added e2e for switching to Fresh from a seeded filter, and for editing the reuse thread ID before submit.

…ain them

Keep inbound thread_id for Back/Cancel, but send create success to the submitted context: global list for fresh_thread_per_run, or the chosen target thread for reuse_thread. Cover both branches in e2e.
Keep the redesigned memory facts list from this PR. Main's Streamdown sanitization on memory summaries does not apply because this branch no longer renders summaries via SafeStreamdown. Auto-merged chat-page (ThreadSubagentBatches), i18n, and mock-api branch coverage from bytedance#4983/bytedance#4998/bytedance#4987.
@LittleChenLiya

Copy link
Copy Markdown
Collaborator Author

Merged origin/main in bbd2aae to clear the DIRTY conflict. The only conflict was memory-settings-page.tsx (this PR's redesigned facts list vs main's Streamdown sanitization on summaries). Kept this PR's facts-list UI; sanitizer does not apply because this branch no longer renders summaries via SafeStreamdown. Chat page (ThreadSubagentBatches), i18n, and mock-api branch coverage from main auto-merged. #4915 and #4922 were rebased onto the new head.

Keep the card+dialog scheduled-tasks UI from this PR. Port bytedance#5064 duplication into the create page via query params (title/prompt/context/schedule), restore schedule-preview via describeSchedule, and adapt the duplicate e2e to open the detail dialog then navigate to /new.
@LittleChenLiya

Copy link
Copy Markdown
Collaborator Author

Merged origin/main in eb7ee01. Conflict was scheduled-tasks page (this PR's card+dialog UI vs #5064 duplicate-into-inline-form). Kept the redesigned UI and ported duplication: Duplicate in the detail dialog navigates to /workspace/scheduled-tasks/new with draft query params. Restored schedule-preview via describeSchedule so the duplicate e2e still checks the filled create form. #4915 and #4922 rebased onto the new head.

@WillemJiang

Copy link
Copy Markdown
Collaborator

@LittleChenLiya, please resolve the conflict with the main branch.

# Conflicts:
#	frontend/src/components/workspace/input-box.tsx
@LittleChenLiya

Copy link
Copy Markdown
Collaborator Author

Merged origin/main in b5f19c0. The remaining conflict was input-box.tsx (this PR's relative PromptInput wrapper vs #5050 model-name truncation). Kept the wrapper for skin/z-index and applied main's truncation classes (max-w-40 min-w-0 sm:max-w-56 plus flex min-w-0 flex-col text-left). Conversation outline (#5025) and i18n keys auto-merged.

@willem-bd willem-bd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found two blocking regressions in the new scheduled-task duplicate flow. The focused cron tests pass and the current CI suite is green, but the existing duplicate E2E case covers only a short prompt with reuse_thread, so it misses both cases below.

const [contextMode, setContextMode] = useState<
"fresh_thread_per_run" | "reuse_thread"
>(
initialContextMode === "reuse_thread" || initialThreadId

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Preserve explicit fresh-thread mode

The duplicate route can provide context_mode=fresh_thread_per_run together with a stored thread_id, but this condition converts that combination to reuse_thread. Submitting the duplicate then changes it from isolated runs to reusing an existing conversation. Give an explicit context_mode precedence and infer reuse from thread_id only when the mode parameter is absent.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed both P1s:

  • Duplicate no longer serializes the prompt (or other task contents) into the query string. It now navigates with ?from=<taskId> and hydrates the create form from sessionStorage, falling back to the scheduled-tasks list cache/API.
  • Explicit context_mode takes precedence over thread_id. Duplicating a fresh_thread_per_run task that still has a stored thread_id stays on isolated runs instead of being coerced to reuse_thread.

Also merged origin/main (copy-data cache + sandbox timeout) so this PR is no longer DIRTY.

"title",
`${selectedTask.title}${st.actions.duplicateTitleSuffix}`,
);
params.set("prompt", selectedTask.prompt);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Keep task prompts out of URLs

Serializing the complete prompt into the query string exposes task contents in browser history and Nginx access logs. Prompts have no length limit, so a valid long prompt can also exceed request-line limits and make Duplicate fail. Navigate with the task ID and fetch the source task, or use navigation/session state that does not put user content in the URL.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed both P1s:

  • Duplicate no longer serializes the prompt (or other task contents) into the query string. It now navigates with ?from=<taskId> and hydrates the create form from sessionStorage, falling back to the scheduled-tasks list cache/API.
  • Explicit context_mode takes precedence over thread_id. Duplicating a fresh_thread_per_run task that still has a stored thread_id stays on isolated runs instead of being coerced to reuse_thread.

Also merged origin/main (copy-data cache + sandbox timeout) so this PR is no longer DIRTY.

Honor an explicit context_mode when duplicating, and pass the source
task id plus sessionStorage instead of serializing the prompt into the
query string.
@LittleChenLiya

Copy link
Copy Markdown
Collaborator Author

Merged origin/main again in 05a1fe0 to pick up #5006 (sandbox authz recheck). No frontend content conflicts. #4915 and #4922 were rebased onto the new head.

…llout

The describe("describeSchedule") block (once/daily en+zh, weekly en+zh,
weekly-no-weekdays fallback, hourly, monthly, custom) was accidentally
deleted during a rebase onto main while buildOnceRunAtLocal was extracted.
Restore it so the locale branches of describeSchedule stay covered.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:frontend Next.js frontend under frontend/ risk:medium Medium risk: regular code changes size/XL PR changes 700+ lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants