# Draft Return

> For agents: start with the [agent guide](https://pi-tui.ratstack.sh/llms.txt). Every page here has a Markdown twin, and every pattern has a JSON record.

Lifecycle / Editors and drafts · `draft-return` · [HTML](https://pi-tui.ratstack.sh/patterns/draft-return/) · [JSON](https://pi-tui.ratstack.sh/patterns/draft-return.json) · [all patterns](https://pi-tui.ratstack.sh/patterns.md)

Also known as Draft restoration.

## Intent

Restore the captured editor draft after a temporary submission.

## Motivation

Boomerang temporarily replaces editor text to submit a reload command while an unsent draft exists.

## Applicability

- Use this when a command needs editor-driven submission without losing the current text.

## Structure

```text
capture draft -> temporary submit
finally -> restore captured text
```

## Participants

- [`ctx.ui.getEditorText`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Captures the current main-editor draft.
- [`ctx.ui.setEditorText`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Replaces the main-editor text.
- [`ctx.ui.setEditorComponent`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user): Installs the single custom editor factory.
- [`CustomEditor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus): Preserves application controls for editor replacements.
- `Captured draft`: Survives temporary editor-driven submission.

Pi screen APIs: [`ctx.ui.getEditorText`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setEditorText`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setEditorComponent`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`CustomEditor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), `getEditorText`, `setEditorText`, `setEditorComponent`

## Consequences

- Temporary submission does not discard the captured draft.
- Restoration needs a finally path and the source restores only nonempty text.

## Implementation

- Restore the captured draft in a finally path.
- The source restores only a nonempty captured draft.

## States

Each state was rendered at 40, 60, 80 and 120 columns in Pi's dark and light themes. Each frame links one WebP. An animated frame links its first image, then the animation.

### Existing draft

`draft`: Show a synthetic unsent prompt in the editor.

Checks at every width and theme:

- width: ✓ pass
- style-leak: ✓ pass
- hard-coded-colour: ✓ pass
- height: ✓ pass

Frames:

- 40 columns: ![Draft Return, Existing draft, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/draft-40-dark.bf920a739d3e.webp) ![Draft Return, Existing draft, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/draft-40-light.6b4ac5b4f9f1.webp)
- 60 columns: ![Draft Return, Existing draft, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/draft-60-dark.a7016488bcc8.webp) ![Draft Return, Existing draft, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/draft-60-light.4d036dd77597.webp)
- 80 columns: ![Draft Return, Existing draft, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/draft-80-dark.a1ac69abfecc.webp) ![Draft Return, Existing draft, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/draft-80-light.14dbff27b070.webp)
- 120 columns: ![Draft Return, Existing draft, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/draft-120-dark.4c718c59fbbd.webp) ![Draft Return, Existing draft, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/draft-120-light.3d6d36f86219.webp)

### Temporary submission

`temporary`: Show a temporary command replacing the captured text.

Checks at every width and theme:

- width: ✓ pass
- style-leak: ✓ pass
- hard-coded-colour: ✓ pass
- height: ✓ pass

Frames:

- 40 columns: ![Draft Return, Temporary submission, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/temporary-40-dark.4713d5dbfbfd.webp) ![Draft Return, Temporary submission, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/temporary-40-light.9202a5efd393.webp)
- 60 columns: ![Draft Return, Temporary submission, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/temporary-60-dark.e391ac232894.webp) ![Draft Return, Temporary submission, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/temporary-60-light.fc2886a2f751.webp)
- 80 columns: ![Draft Return, Temporary submission, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/temporary-80-dark.a2438f3fe1cf.webp) ![Draft Return, Temporary submission, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/temporary-80-light.3dc9fe2b3ce1.webp)
- 120 columns: ![Draft Return, Temporary submission, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/temporary-120-dark.15901e6fa62c.webp) ![Draft Return, Temporary submission, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/temporary-120-light.ad58d6f32bb9.webp)

### Draft restored

`restored`: Restore the original nonempty draft after submission or failure.

Checks at every width and theme:

- width: ✓ pass
- style-leak: ✓ pass
- hard-coded-colour: ✓ pass
- height: ✓ pass

Frames:

- 40 columns: ![Draft Return, Draft restored, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/restored-40-dark.904b8fd695c2.webp) ![Draft Return, Draft restored, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/restored-40-light.0e01af6583c8.webp)
- 60 columns: ![Draft Return, Draft restored, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/restored-60-dark.77cbaf60553e.webp) ![Draft Return, Draft restored, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/restored-60-light.f8922f41c42c.webp)
- 80 columns: ![Draft Return, Draft restored, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/restored-80-dark.1c6b0ce9c424.webp) ![Draft Return, Draft restored, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/restored-80-light.1a92fcb7572c.webp)
- 120 columns: ![Draft Return, Draft restored, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/draft-return/restored-120-dark.ede1ffaa7828.webp) ![Draft Return, Draft restored, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/draft-return/restored-120-light.c9b751d32a8d.webp)

## Sample code

`stories/patterns/lifecycle/draft-return.ts`, the story the frames above were rendered from.

```ts
// Draft Return: restore a captured prompt after temporary editor submission.
import { CustomEditor } from "@earendil-works/pi-coding-agent";
import type { PatternStory } from "../../../src/pattern.ts";

export const story: PatternStory = {
  id: "draft-return", title: "Draft Return", kind: "screen",
  apis: ["getEditorText", "setEditorText", "setEditorComponent", "CustomEditor"],
  states: [
    { id: "draft", label: "Existing draft" },
    { id: "temporary", label: "Temporary submission", steps: [{ type: "action", name: "submit" }] },
    { id: "restored", label: "Draft restored", steps: [{ type: "action", name: "finish" }] },
  ],
  setup({ ui, theme, action }) {
    // Lifecycle: draft -> temporary -> restored (success or failure).
    let capturedDraft = "";
    ui.setEditorText("Review the parser changes\nand keep the public API small.");
    ui.setWidget("draft", [theme.fg("accent", "Unsent prompt · editor owns the draft")]);
    action("submit", () => {
      capturedDraft = ui.getEditorText();
      ui.setEditorComponent((tui, editorTheme, keys) => new CustomEditor(tui, editorTheme, keys));
      ui.setEditorText("/reload");
      ui.setWidget("draft", [theme.fg("warning", "Temporary command · draft captured"),
        theme.fg("muted", "Synthetic submission; no reload runs.")]);
    });
    action("finish", () => {
      try {
        // Synthetic failed submission; no live command or agent is invoked.
        ui.setStatus("submit", theme.fg("warning", "reload skipped"));
      } finally {
        ui.setEditorComponent(undefined);
        if (capturedDraft) ui.setEditorText(capturedDraft);
        ui.setWidget("draft", [theme.fg("success", "Original draft restored in finally")]);
      }
    });
  },
};
```

## Known uses: seen in Nico's repos

- [**pi-boomerang**](https://github.com/nicobailon/pi-boomerang): Temporarily install an editor and restore draft after submission
  - [index.ts:1318-1337](https://github.com/nicobailon/pi-boomerang/blob/1a5985b2d92cfa84ce1f470d100d02b368711a91/index.ts#L1318-L1337) @1a5985b2

## For agents: choose and check

Choose Draft Return when your job matches its intent and applicability above. Its neighbours in Editors and drafts are listed below. Read the one whose intent fits your job more closely before you commit.

- [Editor Steward](https://pi-tui.ratstack.sh/patterns/editor-steward.md): Compose editor enhancements behind one replacement factory.
- [Retry Buffer](https://pi-tui.ratstack.sh/patterns/retry-buffer.md): Keep the local draft editable after a failed send.
- [Draft Fence](https://pi-tui.ratstack.sh/patterns/draft-fence.md): Keep inline draft edits separate from the committed queue snapshot.
- [Prompt Trail](https://pi-tui.ratstack.sh/patterns/prompt-trail.md): Browse submitted prompts while retaining the current editor draft.

Check your version:

- Render your version at 40, 60, 80 and 120 columns in the dark and light themes.
- Check width: no rendered line is wider than the terminal.
- Check style-leak: no line ends with colour, bold or a link still switched on.
- Check hard-coded-colour: every colour on screen comes from the active theme.
- Check height: the output fits in the rows the terminal has.
- Read the Pi 1.0.3 docs for [`ctx.ui.getEditorText`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setEditorText`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`ctx.ui.setEditorComponent`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user), [`CustomEditor`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#handle-keyboard-input-and-focus), `getEditorText`, `setEditorText`, `setEditorComponent` before you use them.
- Every reference frame gets the verdict its state expects.

Next actions: `related({ id: "draft-return" })` lists what to read next, and `states({ id: "draft-return", width: 40 })` returns every frame and verdict at one width. The [build-pi-tui-component skill](https://pi-tui.ratstack.sh/skills/build-pi-tui-component.md) walks through the whole loop.

## Related

- [Draft Fence](https://pi-tui.ratstack.sh/patterns/draft-fence.md): separates draft from committed state

## Linked from

- [Retry Buffer](https://pi-tui.ratstack.sh/patterns/retry-buffer.md): restores text after temporary submission
