1import { stripDisplayedPlanMarkdown, proposedPlanTitle } from "@t3tools/shared/proposedPlanText";
2
3export function buildCollapsedProposedPlanPreviewMarkdown(
4 planMarkdown: string,
5 options?: {
6 maxLines?: number;
7 },
8): string {
9 const maxLines = options?.maxLines ?? 8;
10 const lines = stripDisplayedPlanMarkdown(planMarkdown)
11 .trimEnd()
12 .split(/\r?\n/)
13 .map((line) => line.trimEnd());
14 const previewLines: string[] = [];
15 let visibleLineCount = 0;
16 let hasMoreContent = false;
17
18 for (const line of lines) {
19 const isVisibleLine = line.trim().length > 0;
20 if (isVisibleLine && visibleLineCount >= maxLines) {
21 hasMoreContent = true;
22 break;
23 }
24 previewLines.push(line);
25 if (isVisibleLine) {
26 visibleLineCount += 1;
27 }
28 }
29
30 while (previewLines.length > 0 && previewLines.at(-1)?.trim().length === 0) {
31 previewLines.pop();
32 }
33
34 if (previewLines.length === 0) {
35 return proposedPlanTitle(planMarkdown) ?? "Plan preview unavailable.";
36 }
37
38 if (hasMoreContent) {
39 previewLines.push("", "...");
40 }
41
42 return previewLines.join("\n");
43}
44
45function sanitizePlanFileSegment(input: string): string {
46 const sanitized = input
47 .toLowerCase()
48 .replace(/[`'".,!?()[\]{}]+/g, "")
49 .replace(/[^a-z0-9]+/g, "-")
50 .replace(/^-+|-+$/g, "");
51 return sanitized.length > 0 ? sanitized : "plan";
52}
53
54/** Prefix of the message the app sends when the user approves a plan. */
55export const PLAN_IMPLEMENTATION_PROMPT_PREFIX = "PLEASE IMPLEMENT THIS PLAN:\n";
56
57export function buildPlanImplementationPrompt(planMarkdown: string): string {
58 return `${PLAN_IMPLEMENTATION_PROMPT_PREFIX}${planMarkdown.trim()}`;
59}
60
61export function resolvePlanFollowUpSubmission(input: { draftText: string; planMarkdown: string }): {
62 text: string;
63 interactionMode: "default" | "plan";
64} {
65 const trimmedDraftText = input.draftText.trim();
66 if (trimmedDraftText.length > 0) {
67 return {
68 text: trimmedDraftText,
69 interactionMode: "plan",
70 };
71 }
72
73 return {
74 text: buildPlanImplementationPrompt(input.planMarkdown),
75 interactionMode: "default",
76 };
77}
78
79export function buildPlanImplementationThreadTitle(planMarkdown: string): string {
80 const title = proposedPlanTitle(planMarkdown);
81 if (!title) {
82 return "Implement plan";
83 }
84 return `Implement ${title}`;
85}
86
87export function buildProposedPlanMarkdownFilename(planMarkdown: string): string {
88 const title = proposedPlanTitle(planMarkdown);
89 return `${sanitizePlanFileSegment(title ?? "plan")}.md`;
90}
91
92export function normalizePlanMarkdownForExport(planMarkdown: string): string {
93 return `${planMarkdown.trimEnd()}\n`;
94}
95
96export function downloadPlanAsTextFile(filename: string, contents: string): void {
97 const blob = new Blob([contents], { type: "text/markdown;charset=utf-8" });
98 const url = URL.createObjectURL(blob);
99 const anchor = document.createElement("a");
100 anchor.href = url;
101 anchor.download = filename;
102 anchor.click();
103 window.setTimeout(() => {
104 URL.revokeObjectURL(url);
105 }, 0);
106}