Files
fusion/packages/dashboard/app/utils/taskTitleDisplay.ts
Fusion Agent ef5ebd21e5 FN-044: show bounded description fallbacks for titleless tasks
Display titleless tasks consistently across cards and list views without persisting generated fallback titles.

- Centralize title, description, and ID display fallback selection.
- Bound long description fallbacks to 200 characters with literal dots and preserve full tooltips.
- Cover desktop, mobile, whitespace, length-boundary, and explicit-title behavior.

Files changed:
 packages/dashboard/app/components/ListView.tsx     |  5 +-
 packages/dashboard/app/components/TaskCard.css     | 12 +++
 packages/dashboard/app/components/TaskCard.tsx     | 18 +++++-
 .../app/components/__tests__/ListView.test.tsx     | 64 ++++++++++++++++++++++
 .../app/components/__tests__/TaskCard.test.tsx     | 43 +++++++++++++++
 packages/dashboard/app/utils/taskTitleDisplay.ts   | 51 +++++++++++++++++
 6 files changed, 188 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-044

Fusion-Task-Lineage: 822f17f3-4839-4f9e-9aa7-cd90037b67de

Co-authored-by: Fusion <noreply@runfusion.ai>
2026-08-20 03:57:23 +00:00

52 lines
1.7 KiB
TypeScript

import type { Task } from "@fusion/core";
export type TaskTitleDisplaySource = "title" | "description" | "id";
export interface TaskTitleDisplay {
source: TaskTitleDisplaySource;
text: string;
fullText: string;
isBoundedDescription: boolean;
}
const MAX_DESCRIPTION_FALLBACK_LENGTH = 200;
const DESCRIPTION_FALLBACK_SUFFIX = "...";
/**
* Selects a display-only card label without changing the authoritative task data.
*
* FNXC:TaskTitleDisplay 2026-08-19-15:22:
* FN-044 renders an ordinary titleless FN-036 task from its description only after a nonblank
* persisted title has been ruled out. This UI seam must not restore an AI length policy or persist
* a fallback title; selected descriptions above 200 characters use a literal `...` within 200 total.
*/
export function getTaskTitleDisplay(task: Pick<Task, "id" | "title" | "description">): TaskTitleDisplay {
if (typeof task.title === "string" && task.title.trim().length > 0) {
return {
source: "title",
text: task.title,
fullText: task.title,
isBoundedDescription: false,
};
}
if (typeof task.description === "string" && task.description.trim().length > 0) {
const isBoundedDescription = task.description.length > MAX_DESCRIPTION_FALLBACK_LENGTH;
return {
source: "description",
text: isBoundedDescription
? task.description.slice(0, MAX_DESCRIPTION_FALLBACK_LENGTH - DESCRIPTION_FALLBACK_SUFFIX.length) + DESCRIPTION_FALLBACK_SUFFIX
: task.description,
fullText: task.description,
isBoundedDescription,
};
}
return {
source: "id",
text: task.id,
fullText: task.id,
isBoundedDescription: false,
};
}