aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs/2026-10-16-file-drop-design.md
blob: d3c8a9d8332706278b0461c2f2a0e706c5f82b0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File drop and the entry line: design

Date: 2026-10-16. Status: approved in brainstorming, not implemented.

## Goal

SUPER+Home opens a single line, "How can I help you?", which grows into the
chat once a question is asked. A file dragged onto the island opens a middle
state offering what can be done with it, and asking about it is one of
those choices, not the only one. This follows Modesty's island drop (read
for ideas only; Modesty is GPLv3).

The daemon side (files in `ask`, extraction, history) is desktop-assistant's
`docs/superpowers/specs/2026-10-16-attachments-design.md`.

This is piece 1 of three. Piece 2 fills the file state's **Ask** row with
per-type model prompts, piece 3 its **Do** row with local file operations
(PDF to markdown, merge, video to audio, format or GIF, trackcrop). Each
gets its own spec.

## Three states

| state | when | the card shows |
| --- | --- | --- |
| entry | no turns, nothing staged | one line: "How can I help you?" |
| file | files staged, no turns | the file chips, the action rows, the input ("Ask about report.pdf…"), **Chat about it** |
| chat | the conversation has turns | the chat as today |

The card stays centred, with the dim backdrop in every state. Its height
animates (about 180 ms) between states, growing from the centre.

- **Opening.** SUPER+Home shows the chat if the current conversation was
  active (a `user` or `done` event) in the last 10 minutes. Otherwise the
  canvas sends `new`, and the empty conversation is the entry state. The
  old conversation stays in History. The 10 minutes are a constant, not a
  preference.
- **Entry to chat.** Enter on a typed question sends `ask`; its `user`
  event adds a turn, which is the chat state.
- **A drop** always starts a new task: the canvas sends `new`, stages the
  files and shows the file state, even over a recent conversation.
- **File state.** Each chip has a ×; removing the last returns to entry.
  Enter on a typed question sends `ask {text, files}` and clears the chips.
  **Chat about it** moves to the chat layout with the files still staged
  and focus in the input, sending nothing. In piece 1 the action rows hold
  only Chat about it. A file of a kind chat does not take (video, archive)
  shows "chat supports text, images and PDF" on its chip; while any such
  chip is staged, Chat about it and the input are disabled (removing it
  enables them).
- **Results of a local operation** (piece 3) report in the file state and
  never turn it into a chat.
- Escape closes the card in every state, after the history panel.

Sent files show as chips under the question in the transcript, with the
`truncated` mark from the daemon's `files` list.

## File kinds

`desktop/assistant/Files.js` maps a path to `text`, `image`, `pdf`,
`video`, `archive` or `other` by extension, with the same text, code and
image lists as the daemon. It also turns a dropped URL into a path:
`file://` URLs are decoded, anything else is ignored.

## The island receptacle

In `desktop/island/Island.qml`:

- one `DropArea` spans the sliver and the space for a bubble below it;
- a drag entering with URLs and a copy action sets `dropHold`, which shows
  a bubble, "Drop for the assistant", and adds the bubble to the input
  mask so the drop can land on it;
- leaving clears `dropHold`;
- a drop converts the URLs, calls `asst.attach(paths)` and accepts the
  copy. With no file URLs it is refused.

`shell.qml` passes the canvas as `asst: assistant`, never under the
canvas's own id name (AGENTS.md: a property named like the enclosing id
binds to itself).

Wayland reports a drag only once it enters one of the island's input
regions, which is why the drop begins on the sliver.

## Testing

- `node desktop/assistant/test_files.js`: kinds per extension, case
  folding, files with no extension, URL decoding with spaces and non-ASCII
  names, non-file URLs dropped.
- The scratch harness load check, as for the canvas.
- By hand: drag a file over the sliver and see the bubble; drop it and get
  the file state; remove the chip and get entry; drop again and use Chat
  about it; ask about a PDF and an image; see chips in the transcript;
  drop a video and see the refusal; SUPER+Home after more than 10 minutes
  gives entry, within 10 minutes the chat.