FN-7945: route List-view task opens through the movable popup when Open tasks as popups is on
Extends the existing board/right-dock "Open tasks as popups" routing so ordinary List row/card and keyboard opens use the same shared movable/resizable FloatingWindow instead of the docked split-pane/mobile detail. - Add openMobileTasksInPopup prop to ListView, threaded through App -> MainContent -> ListView (dashboard/types.ts) - handleRowClick routes to onPopOut (popOutTaskDetail) when the setting is on, on both desktop split-pane and mobile/tablet single-pane; docked behavior is preserved when the setting is off - Restore Enter/Space keyboard activation on list rows to invoke the same handleRowClick path, alongside existing context-menu key handling - Update docs/dashboard-guide.md and docs/settings-reference.md to describe List row/card opens as part of the popup routing surface, and refresh the Appearance settings help copy/FNXC comment accordingly - Add changeset (.changeset/fn-7945-list-view-task-popup.md, minor) describing the user-facing behavior - Extend ListView.test.tsx coverage for the new popup routing and restored keyboard activation Files changed: .changeset/fn-7945-list-view-task-popup.md | 7 ++ docs/dashboard-guide.md | 4 +- docs/settings-reference.md | 2 +- packages/dashboard/app/App.tsx | 1 + packages/dashboard/app/components/ListView.tsx | 48 +++++++++---- .../app/components/__tests__/ListView.test.tsx | 80 +++++++++++++++++++++- .../app/components/dashboard/MainContent.tsx | 2 + .../dashboard/app/components/dashboard/types.ts | 1 + .../settings/sections/AppearanceSection.tsx | 4 +- 9 files changed, 127 insertions(+), 22 deletions(-) Fusion-Task-Id: FN-7945 Fusion-Task-Lineage: 784cb4ee-c493-4ace-bf8b-0e3dbaaef9a3 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-7945-list-view-task-popup.md
Normal file
7
.changeset/fn-7945-list-view-task-popup.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
summary: Open tasks as popups now applies to List clicks with the same movable task window as the Board.
|
||||
category: feature
|
||||
dev: Threads openMobileTasksInPopup App -> MainContent -> ListView; ListView.handleRowClick routes to onPopOut/popOutTaskDetail (floating-window--task-detail) when enabled, on both desktop split-pane and mobile/tablet single-pane, preserving docked behavior when off.
|
||||
@@ -108,12 +108,12 @@ When enabled on desktop or tablet project screens, the right dock is a persisten
|
||||
|
||||
If **Settings → Appearance → Open tasks in the right sidebar** is enabled, board task-card clicks open task detail inside this right dock and keep the board visible. The setting is default off; mobile or hidden/inactive dock states automatically fall back to the existing full-panel task detail unless the task-popup setting below is enabled, and non-board task-open paths keep their existing behavior.
|
||||
|
||||
<!-- FNXC:MobileTaskPopups 2026-07-01-12:00: Board-card task opens have a separate default-off popup setting so operators on desktop, tablet, and mobile can opt into the existing FloatingWindow task popup when they want the board visible; ordinary popup routing takes precedence over the right dock, while deep-tab and non-board opens keep their existing paths.
|
||||
<!-- FNXC:MobileTaskPopups 2026-07-13-00:00 (FN-7945): Board-card and List row/card task opens have a separate default-off popup setting so operators on desktop, tablet, and mobile can opt into the existing FloatingWindow task popup when they want the board or List view visible; ordinary popup routing takes precedence over the right dock, while deep-tab, context-menu, and non-board/List opens keep their existing paths.
|
||||
FNXC:TaskPopupGeometry 2026-07-03-00:00: Desktop/tablet task popups share a persisted geometry key across task IDs so operators can size and place the popup once, then open other tasks without repeating that setup. Mobile remains a full-screen sheet regardless of saved desktop geometry.
|
||||
FNXC:RightDockTaskPopup 2026-07-03-00:00: The same task-popup preference also applies to ordinary clicks in the right-dock Tasks list so that opted-in operators never lose the list to embedded detail by clicking a dock task row.
|
||||
FNXC:TaskPopupLayer 2026-07-04-18:36: Ordinary task popups sit on the board/task-detail layer instead of the global floating-utility stack so the board/list context remains visually primary while Terminal, Quick Chat, file browser, workflow editor, and other utility windows keep their top utility stacking.
|
||||
FNXC:TaskPopupViewGating 2026-07-13-00:00: The default-off popup view attachment setting must be documented as render-only hiding, not closing, because popup snapshots and shared geometry survive view switches. -->
|
||||
**Settings → Appearance → Open tasks as popups** changes ordinary board task-card clicks and right-dock Tasks-list clicks across desktop, tablet, and mobile viewports. When enabled, those clicks use the existing task popup/FloatingWindow surface on the board/task-detail layer instead of the full-panel task detail or right-dock task detail, keeping the board or dock list visible in the background while utility windows keep their higher global stacking. On desktop and tablet, task popups restore the last saved popup size and position between tasks; on mobile, task popups stay full-screen sheets. Deep `changes`/`retries`/`workflow` opens, list/task-detail links outside the right-dock Tasks list, plugin/graph opens, and explicit pop-out actions keep their existing paths.
|
||||
**Settings → Appearance → Open tasks as popups** changes ordinary board task-card clicks, List row/card opens, and right-dock Tasks-list clicks across desktop, tablet, and mobile viewports. When enabled, those clicks use the existing task popup/FloatingWindow surface on the board/task-detail layer instead of the full-panel task detail, List split-detail/docked detail, or right-dock task detail, keeping the board, List view, or dock list visible in the background while utility windows keep their higher global stacking. On desktop and tablet, task popups restore the last saved popup size and position between tasks; on mobile, task popups stay full-screen sheets. Deep `changes`/`retries`/`workflow` opens, List context-menu/refine actions, task-detail links, plugin/graph opens, and explicit pop-out actions keep their existing paths.
|
||||
|
||||
**Settings → Appearance → Keep task popups on their Board/List view** is default off. When enabled, each open task-detail popup stays attached to the Board or List view where it was opened: navigating to Command Center, Agents, Settings, or a different task view hides the popup without closing it, and returning to the originating Board/List view re-shows the same popup in the same saved position.
|
||||
|
||||
|
||||
@@ -507,7 +507,7 @@ When `pushAfterMerge` is enabled, a completed direct merge first runs `git pull
|
||||
| `recycleWorktrees` | `boolean` | `false` | Default: off (opt-in). Reuse worktrees from a pool for faster startup. |
|
||||
| `showWorktreeGrouping` | `boolean` | `false` | Default: off. When off, WIP/processing columns render plain task cards without worktree group shells or worktree-name labels in both legacy and workflow-mode boards. When on, every WIP/processing column groups tasks by worktree and shows worktree names, including workflow-mode columns flagged as counting toward WIP. |
|
||||
| `openTasksInRightSidebar` | `boolean` | `false` | Default: off. When off, board task-card clicks keep the existing full-panel task detail that replaces the board. When on and the right dock is active on desktop/tablet, board task-card clicks open the task detail in the right sidebar so the board stays visible; mobile or hidden/inactive right-dock states automatically fall back to the full-panel behavior. Non-board task-open paths, including list split detail, floating pop-outs, graph/plugin opens, and deep `changes`/`retries`/`workflow` opens, keep their existing behavior; ordinary right-dock Tasks-list cards are governed by `openMobileTasksInPopup` first and otherwise use embedded dock detail. |
|
||||
| `openMobileTasksInPopup` | `boolean` | `false` | Default: off. When off, ordinary board task-card clicks keep the existing fallback behavior: the full-panel task detail, or the right dock when `openTasksInRightSidebar` is on and the dock is active; ordinary right-dock Tasks-list clicks open embedded dock detail with the normal back-to-list controls. When on, ordinary board task-card clicks and right-dock Tasks-list clicks open the task in the existing task popup/FloatingWindow surface on desktop, tablet, and mobile so the board or dock list remains visible; this popup route takes precedence over right-dock routing for those ordinary clicks. Desktop/tablet task popups restore the last saved popup size and position across task IDs and use the board/task-detail layer rather than the global utility layer, while their Activity dropdown stays above and attached during popup drag/resize; mobile task popups remain full-screen sheets. Deep `changes`/`retries`/`workflow` opens, list/detail opens outside the right-dock Tasks list, graph/plugin opens, nested task-detail opens, and explicit pop-out actions keep their existing behavior. |
|
||||
| `openMobileTasksInPopup` | `boolean` | `false` | Default: off. When off, ordinary board task-card clicks keep the existing fallback behavior: the full-panel task detail, or the right dock when `openTasksInRightSidebar` is on and the dock is active; List row/card opens keep the desktop split-detail pane or the mobile/tablet docked detail; ordinary right-dock Tasks-list clicks open embedded dock detail with the normal back-to-list controls. When on, ordinary board task-card clicks, List row/card opens, and right-dock Tasks-list clicks open the task in the existing task popup/FloatingWindow surface on desktop, tablet, and mobile so the board, List view, or dock list remains visible; this popup route takes precedence over right-dock routing for those ordinary clicks. Desktop/tablet task popups restore the last saved popup size and position across task IDs and use the board/task-detail layer rather than the global utility layer, while their Activity dropdown stays above and attached during popup drag/resize; mobile task popups remain full-screen sheets. Deep `changes`/`retries`/`workflow` opens, context-menu/refine/detail links, graph/plugin opens, nested task-detail opens, and explicit pop-out actions keep their existing behavior. |
|
||||
| `taskPopupsBoardListOnly` | `boolean` | `false` | Project-scoped Appearance setting. Default: off, so open task popups remain visible over every main-content view. When on, each open task-detail popup is attached to the Board or List view where it was opened: switching to Command Center, Agents, Settings, another task view, or the other Board/List view hides it without closing or clearing popup state; returning to the originating Board/List view re-renders the same popup with its shared persisted size/position. |
|
||||
| `showCostBadgeOnCards` | `boolean` | `false` | Default: off. When enabled from Settings → Appearance, board cards with positive recorded token usage show a read-time derived model-cost badge beside the execution-time badge. Unpriced models display `—`, and tasks with no token usage render no badge shell. |
|
||||
| `executorAllowSiblingBranchRename` | `boolean` | `false` | Opt back into the legacy executor behavior that silently allocates sibling branches (`fusion/<task-id>-2`, `-2-2`, …) when the canonical task branch is already checked out elsewhere. When disabled (default), branch conflicts fail loudly and leave the task in `todo` with `status: "failed"` so operators can resolve conflicting branches/worktrees with git tooling before retrying. See [Task Management → Branch conflict handling](./task-management.md#branch-conflict-handling). The dashboard Settings modal exposes the same toggle with warning copy because this legacy mode is discouraged. |
|
||||
|
||||
@@ -1369,6 +1369,7 @@ function AppInner() {
|
||||
mergeStrategy,
|
||||
planAutoApproveEnabled,
|
||||
settingsLoaded,
|
||||
openMobileTasksInPopup,
|
||||
taskDetailChatFirst,
|
||||
skillsEnabled,
|
||||
experimentalFeatures,
|
||||
|
||||
@@ -232,6 +232,8 @@ interface ListViewProps {
|
||||
onPopOut pops the split-pane task detail into a movable, resizable, non-blocking FloatingWindow managed at App level. Wired to the Maximize2 "Pop out" button in TaskDetailContent's header.
|
||||
*/
|
||||
onPopOut?: (task: Task | TaskDetail) => void;
|
||||
/** Mirrors the Board/right-dock "Open tasks as popups" routing for ordinary List row/card opens. */
|
||||
openMobileTasksInPopup?: boolean;
|
||||
addToast: (message: string, type?: ToastType) => void;
|
||||
globalPaused?: boolean;
|
||||
onNewTask?: () => void;
|
||||
@@ -320,6 +322,7 @@ export function ListView({
|
||||
onResetTask,
|
||||
onDuplicateTask,
|
||||
onPopOut,
|
||||
openMobileTasksInPopup = false,
|
||||
onOpenDetail,
|
||||
addToast,
|
||||
globalPaused,
|
||||
@@ -1850,20 +1853,6 @@ export function ListView({
|
||||
}, LIST_TOUCH_CONTEXT_MENU_DELAY_MS);
|
||||
}, [clearLongPressTimer, isMobile, openContextMenuAt]);
|
||||
|
||||
const handleListKeyDown = useCallback((event: React.KeyboardEvent, task: Task) => {
|
||||
if (event.key !== "ContextMenu" && !(event.shiftKey && event.key === "F10")) return;
|
||||
if (isListContextInteractiveTarget(event.target)) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
|
||||
suppressNextRowClickRef.current = true;
|
||||
openContextMenuAt(
|
||||
task,
|
||||
rect.left + Math.min(rect.width - LIST_CONTEXT_MENU_VIEWPORT_MARGIN, LIST_KEYBOARD_CONTEXT_MENU_OFFSET),
|
||||
rect.top + Math.min(rect.height - LIST_CONTEXT_MENU_VIEWPORT_MARGIN, LIST_KEYBOARD_CONTEXT_MENU_OFFSET),
|
||||
);
|
||||
}, [openContextMenuAt]);
|
||||
|
||||
const handleListPointerMove = useCallback((event: React.PointerEvent) => {
|
||||
const start = longPressStartRef.current;
|
||||
if (!start || start.pointerId !== event.pointerId) return;
|
||||
@@ -1936,6 +1925,14 @@ export function ListView({
|
||||
return;
|
||||
}
|
||||
closeContextMenu();
|
||||
/*
|
||||
FNXC:ListView 2026-07-13-00:00 (FN-7945):
|
||||
When "Open tasks as popups" is on, ordinary List row/card and keyboard opens route to the shared movable/resizable popped-out FloatingWindow (`onPopOut` → `popOutTaskDetail`) for Board parity and navigate-while-open behavior. When off, preserve the existing docked split-pane on desktop and docked modal on mobile/tablet.
|
||||
*/
|
||||
if (openMobileTasksInPopup && onPopOut) {
|
||||
onPopOut(task);
|
||||
return;
|
||||
}
|
||||
if (useSinglePaneList) {
|
||||
onOpenDetail(task, { origin: "list-mobile" });
|
||||
return;
|
||||
@@ -1944,9 +1941,30 @@ export function ListView({
|
||||
setSelectedTaskId(task.id);
|
||||
setSelectedTaskSnapshot(task);
|
||||
},
|
||||
[closeContextMenu, onOpenDetail, useSinglePaneList]
|
||||
[closeContextMenu, onOpenDetail, onPopOut, openMobileTasksInPopup, useSinglePaneList]
|
||||
);
|
||||
|
||||
const handleListKeyDown = useCallback((event: React.KeyboardEvent, task: Task) => {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
if (isListContextInteractiveTarget(event.target)) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
handleRowClick(task);
|
||||
return;
|
||||
}
|
||||
if (event.key !== "ContextMenu" && !(event.shiftKey && event.key === "F10")) return;
|
||||
if (isListContextInteractiveTarget(event.target)) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
|
||||
suppressNextRowClickRef.current = true;
|
||||
openContextMenuAt(
|
||||
task,
|
||||
rect.left + Math.min(rect.width - LIST_CONTEXT_MENU_VIEWPORT_MARGIN, LIST_KEYBOARD_CONTEXT_MENU_OFFSET),
|
||||
rect.top + Math.min(rect.height - LIST_CONTEXT_MENU_VIEWPORT_MARGIN, LIST_KEYBOARD_CONTEXT_MENU_OFFSET),
|
||||
);
|
||||
}, [handleRowClick, openContextMenuAt]);
|
||||
|
||||
// Debounce detail fetches so rapid keyboard/mouse navigation through a
|
||||
// long task list doesn't issue a heavy /tasks/:id request (with log +
|
||||
// comments) per row. Only the task the user lands on triggers a fetch.
|
||||
|
||||
@@ -623,13 +623,15 @@ describe("ListView", () => {
|
||||
const viewportSpy = mockDesktopViewport();
|
||||
const tasks = [createMockTask({ id: "FN-001", title: "Test Task" })];
|
||||
const mockOnOpenDetail = vi.fn();
|
||||
const onPopOut = vi.fn();
|
||||
|
||||
renderListView({ tasks, onOpenDetail: mockOnOpenDetail });
|
||||
renderListView({ tasks, onOpenDetail: mockOnOpenDetail, onPopOut });
|
||||
|
||||
const row = screen.getByText("FN-001").closest("tr");
|
||||
fireEvent.click(row!);
|
||||
|
||||
expect(mockOnOpenDetail).not.toHaveBeenCalled();
|
||||
expect(onPopOut).not.toHaveBeenCalled();
|
||||
expect(localStorage.getItem(scopedStorageKey("kb-dashboard-list-selected-task"))).toBe("FN-001");
|
||||
expect(row?.className).toContain("list-row--selected");
|
||||
await waitFor(() => {
|
||||
@@ -640,22 +642,96 @@ describe("ListView", () => {
|
||||
viewportSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("routes desktop List row clicks and keyboard opens to the task popup when enabled", () => {
|
||||
const viewportSpy = mockDesktopViewport();
|
||||
const tasks = [createMockTask({ id: "FN-001", title: "Test Task" })];
|
||||
const onOpenDetail = vi.fn();
|
||||
const onPopOut = vi.fn();
|
||||
|
||||
renderListView({ tasks, onOpenDetail, onPopOut, openMobileTasksInPopup: true });
|
||||
|
||||
const row = screen.getByText("FN-001").closest("tr") as HTMLElement;
|
||||
fireEvent.click(row);
|
||||
|
||||
expect(onPopOut).toHaveBeenCalledWith(tasks[0]);
|
||||
expect(onPopOut).toHaveBeenCalledTimes(1);
|
||||
expect(onOpenDetail).not.toHaveBeenCalled();
|
||||
expect(screen.queryByTestId("list-split-detail-content")).toBeNull();
|
||||
expect(localStorage.getItem(scopedStorageKey("kb-dashboard-list-selected-task"))).toBeNull();
|
||||
|
||||
fireEvent.keyDown(row, { key: "Enter" });
|
||||
expect(onPopOut).toHaveBeenCalledTimes(2);
|
||||
expect(onPopOut).toHaveBeenLastCalledWith(tasks[0]);
|
||||
expect(onOpenDetail).not.toHaveBeenCalled();
|
||||
expect(screen.queryByTestId("list-split-detail-content")).toBeNull();
|
||||
viewportSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("falls back to desktop docked detail when popup routing is enabled without onPopOut", async () => {
|
||||
const viewportSpy = mockDesktopViewport();
|
||||
const tasks = [createMockTask({ id: "FN-001", title: "Test Task" })];
|
||||
const onOpenDetail = vi.fn();
|
||||
|
||||
renderListView({ tasks, onOpenDetail, openMobileTasksInPopup: true });
|
||||
|
||||
const row = screen.getByText("FN-001").closest("tr") as HTMLElement;
|
||||
fireEvent.click(row);
|
||||
|
||||
expect(onOpenDetail).not.toHaveBeenCalled();
|
||||
expect(localStorage.getItem(scopedStorageKey("kb-dashboard-list-selected-task"))).toBe("FN-001");
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("list-split-detail-content")).toBeInTheDocument();
|
||||
});
|
||||
viewportSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("calls onOpenDetail on mobile row click", () => {
|
||||
const viewportSpy = mockMobileViewport();
|
||||
const tasks = [createMockTask({ id: "FN-001", title: "Test Task" })];
|
||||
const mockOnOpenDetail = vi.fn();
|
||||
const onPopOut = vi.fn();
|
||||
|
||||
renderListView({ tasks, onOpenDetail: mockOnOpenDetail });
|
||||
renderListView({ tasks, onOpenDetail: mockOnOpenDetail, onPopOut });
|
||||
|
||||
const card = document.querySelector('.list-card[data-id="FN-001"]');
|
||||
fireEvent.click(card!);
|
||||
|
||||
expect(mockOnOpenDetail).toHaveBeenCalledWith(tasks[0], { origin: "list-mobile" });
|
||||
expect(mockOnOpenDetail).toHaveBeenCalledTimes(1);
|
||||
expect(onPopOut).not.toHaveBeenCalled();
|
||||
expect(fetchTaskDetail).not.toHaveBeenCalled();
|
||||
viewportSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("routes mobile and tablet List cards to the task popup when enabled", () => {
|
||||
const mobileViewportSpy = mockMobileViewport();
|
||||
const mobileTasks = [createMockTask({ id: "FN-001", title: "Mobile popup" })];
|
||||
const mobileOnOpenDetail = vi.fn();
|
||||
const mobileOnPopOut = vi.fn();
|
||||
|
||||
const mobileRender = renderListView({ tasks: mobileTasks, onOpenDetail: mobileOnOpenDetail, onPopOut: mobileOnPopOut, openMobileTasksInPopup: true });
|
||||
fireEvent.click(document.querySelector('.list-card[data-id="FN-001"]') as HTMLElement);
|
||||
|
||||
expect(mobileOnPopOut).toHaveBeenCalledWith(mobileTasks[0]);
|
||||
expect(mobileOnOpenDetail).not.toHaveBeenCalled();
|
||||
expect(screen.queryByTestId("list-split-detail-content")).toBeNull();
|
||||
mobileRender.unmount();
|
||||
mobileViewportSpy.mockRestore();
|
||||
|
||||
const tabletViewportSpy = mockTabletViewport();
|
||||
const tabletTasks = [createMockTask({ id: "FN-002", title: "Tablet popup" })];
|
||||
const tabletOnOpenDetail = vi.fn();
|
||||
const tabletOnPopOut = vi.fn();
|
||||
|
||||
renderListView({ tasks: tabletTasks, onOpenDetail: tabletOnOpenDetail, onPopOut: tabletOnPopOut, openMobileTasksInPopup: true });
|
||||
fireEvent.click(document.querySelector('.list-card[data-id="FN-002"]') as HTMLElement);
|
||||
|
||||
expect(tabletOnPopOut).toHaveBeenCalledWith(tabletTasks[0]);
|
||||
expect(tabletOnOpenDetail).not.toHaveBeenCalled();
|
||||
expect(screen.queryByTestId("list-split-detail-content")).toBeNull();
|
||||
tabletViewportSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("opens the task context menu from desktop row right-click without selecting or opening detail", async () => {
|
||||
const viewportSpy = mockDesktopViewport();
|
||||
const onOpenDetail = vi.fn();
|
||||
|
||||
@@ -74,6 +74,7 @@ export function MainContent({
|
||||
mergeStrategy,
|
||||
planAutoApproveEnabled,
|
||||
settingsLoaded,
|
||||
openMobileTasksInPopup,
|
||||
taskDetailChatFirst,
|
||||
skillsEnabled,
|
||||
experimentalFeatures,
|
||||
@@ -917,6 +918,7 @@ export function MainContent({
|
||||
lastFetchTimeMs={lastFetchTimeMs}
|
||||
prAuthAvailable={prAuthAvailable}
|
||||
autoMerge={autoMerge}
|
||||
openMobileTasksInPopup={openMobileTasksInPopup}
|
||||
taskDetailChatFirst={taskDetailChatFirst}
|
||||
mergeStrategy={mergeStrategy}
|
||||
onOpenWorkflowEditor={openWorkflowEditorWithNav}
|
||||
|
||||
@@ -116,6 +116,7 @@ export interface MainContentProps {
|
||||
mergeStrategy: string;
|
||||
planAutoApproveEnabled: boolean;
|
||||
settingsLoaded: boolean;
|
||||
openMobileTasksInPopup: boolean;
|
||||
taskDetailChatFirst: boolean;
|
||||
skillsEnabled: boolean;
|
||||
experimentalFeatures: Record<string, boolean>;
|
||||
|
||||
@@ -45,12 +45,12 @@ export function AppearanceSection({ scopeBanner, form, setForm, themeMode, color
|
||||
<small className="form-text text-muted">{t("settings.appearance.openTasksInRightSidebarHelp", "When enabled, board task cards open detail in the right sidebar when it is available; mobile and hidden-sidebar states keep the full task panel. Default: disabled.")}</small>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
{/* FNXC:MobileTaskPopups 2026-07-01-12:00: Keep the stored openMobileTasksInPopup key for compatibility, but present the setting as all-viewport ordinary task popup routing because desktop operators also need the board or right-dock Tasks list visible behind task detail. */}
|
||||
{/* FNXC:MobileTaskPopups 2026-07-13-00:00 (FN-7945): Keep the stored openMobileTasksInPopup key for compatibility, but present the setting as all-viewport ordinary task popup routing because desktop operators also need the board, List view, or right-dock Tasks list visible behind task detail. */}
|
||||
<label className="checkbox-label">
|
||||
<input type="checkbox" checked={form.openMobileTasksInPopup === true} onChange={(e) => setForm((f) => ({ ...f, openMobileTasksInPopup: e.target.checked }))}/>
|
||||
<span>{t("settings.appearance.openMobileTasksInPopup", "Open tasks as popups")}</span>
|
||||
</label>
|
||||
<small className="form-text text-muted">{t("settings.appearance.openMobileTasksInPopupHelp", "When enabled, ordinary board task-card and right-dock Tasks-list clicks open the existing task popup so the board or list remains visible. Deep-tab and other task opens keep their current behavior. Default: disabled.")}</small>
|
||||
<small className="form-text text-muted">{t("settings.appearance.openMobileTasksInPopupHelp", "When enabled, ordinary board task-card, List row/card, and right-dock Tasks-list clicks open the existing movable task popup so the board or list remains visible. Deep-tab and other task opens keep their current behavior. Default: disabled.")}</small>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
{/* FNXC:TaskPopupViewGating 2026-07-13-00:00: This project-scoped setting is opt-in because existing task popups float globally by default. When enabled, each popup stays attached to the Board/List view where it was opened, hiding on other views without closing or clearing geometry. */}
|
||||
|
||||
Reference in New Issue
Block a user