FN-7318: preserve planning summary spaces

Preserve editable Planning Mode summary descriptions so mobile and desktop textareas keep typed spaces.

- Keep non-empty generated descriptions untrimmed while still falling back for whitespace-only payloads.
- Cover mobile and desktop summary description editing with user-event typing assertions.
- Add a patch changeset for the Planning Mode description input fix.

Files changed:
 .changeset/fix-mobile-planning-summary-spaces.md     |  7 +++++++
 .../dashboard/app/components/PlanningModeModal.tsx   |  9 +++++++--
 .../PlanningModeModal.ui-interactions.test.tsx       | 20 ++++++++++++++++++++
 3 files changed, 34 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7318

Fusion-Task-Lineage: e83ab078-e9a5-4bd4-88fa-72aa031af42e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-06-30 15:04:02 -07:00
parent 254e97da74
commit 151800f387
3 changed files with 34 additions and 2 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Fix mobile Planning Mode description editing so spaces can be entered.
category: fix
dev: Guards Planning Mode summary normalization so editable fields keep normal text input.

View File

@@ -142,8 +142,13 @@ function normalizePlanningSummary(summary: PlanningSummary): PlanningSummary {
const title = typeof raw.title === "string" && raw.title.trim().length > 0
? raw.title.trim()
: "Untitled planning task";
const description = typeof raw.description === "string" && raw.description.trim().length > 0
? raw.description.trim()
/*
FNXC:PlanningMode 2026-06-30-12:54:
Final summary description is an editable draft. Preserve internal and trailing whitespace during render so mobile text entry can insert a space before the next word; only whitespace-only server payloads fall back to the normalized title.
*/
const rawDescription = typeof raw.description === "string" ? raw.description : "";
const description = rawDescription.trim().length > 0
? rawDescription
: title;
return {
...summary,

View File

@@ -25,6 +25,7 @@ vi.mock("../../hooks/useNavigationHistory", async (importOriginal) => {
};
});
import { act, render, renderHook, screen, fireEvent, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import * as api from "../../api";
import { PlanningModeModal } from "../PlanningModeModal";
import { TaskDetailModal } from "../TaskDetailModal";
@@ -997,6 +998,25 @@ describe("PlanningModeModal", () => {
});
});
it.each([
{ viewport: "mobile" as const, typedDescription: "Mobile summary accepts spaces" },
{ viewport: "desktop" as const, typedDescription: "Desktop summary accepts spaces" },
])("preserves typed spaces in the $viewport summary description textarea", async ({ viewport, typedDescription }) => {
mockViewport(viewport);
const user = userEvent.setup();
await renderPlanningSummary("Generated summary with existing words");
const textarea = screen.getByLabelText("Description") as HTMLTextAreaElement;
textarea.focus();
textarea.setSelectionRange(0, textarea.value.length);
await user.type(textarea, typedDescription);
expect(textarea.value).toContain(typedDescription);
expect(textarea.value).not.toContain(typedDescription.replaceAll(" ", ""));
expect(screen.getByRole("button", { name: /Create Single Task/i })).toBeDefined();
expect(screen.getByRole("button", { name: /Break into Tasks/i })).toBeDefined();
});
it("expands and collapses the mobile summary description without breaking the adjacent markdown toggle", async () => {
mockViewport("mobile");
const { container } = await renderPlanningSummary(