# File Chooser Pill UI Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Replace native `<input type="file">` browser buttons with a custom pill that shows the selected filename and an × clear button across all 4 file inputs in both frontend apps.

**Architecture:** A single reusable `setupFileChooser(input)` JS function wraps each native file input on DOMContentLoaded — hiding the native control, injecting a "Choose File" button and a hidden filename pill beside it, and toggling between them on `change`. A `resetFileChooser(input)` helper handles the two places in `tauri/src/app.js` that programmatically clear file inputs.

**Tech Stack:** Vanilla JS (no build step, no framework), CSS custom properties already defined in each app's `style.css`.

---

## File Map

| File | Change |
|------|--------|
| `ui/static/style.css` | Add `.file-chooser`, `.file-chooser-btn`, `.file-chooser-pill`, `.file-chooser-name`, `.file-chooser-clear` rules |
| `ui/static/app.js` | Add `setupFileChooser`, `resetFileChooser`, `initFileChoosers`; call `initFileChoosers` on DOMContentLoaded |
| `tauri/src/style.css` | Same CSS additions as `ui/static/style.css` |
| `tauri/src/app.js` | Same JS additions; update 2 existing call sites that reset `reimport-file` programmatically |
| `ui/static/index.html` | No change |
| `tauri/src/index.html` | No change |

---

## Task 1: Add file chooser CSS to both apps

**Files:**
- Modify: `ui/static/style.css` (append after line 399)
- Modify: `tauri/src/style.css` (append after line 641)

- [ ] **Step 1: Append CSS to `ui/static/style.css`**

Add to the very end of the file:

```css
/* File chooser */
.file-chooser {
    display: inline-flex;
    align-items: center;
}

.file-chooser-btn {
    background: var(--border);
    color: var(--text);
    border: none;
    padding: 8px 16px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 13px;
    font-family: inherit;
    transition: background 0.15s;
}

.file-chooser-btn:hover {
    background: #3d444d;
}

.file-chooser-pill {
    display: none;
    align-items: center;
    gap: 8px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 6px 10px;
    font-size: 13px;
}

.file-chooser-pill.visible {
    display: inline-flex;
}

.file-chooser-name {
    font-family: var(--mono);
    color: var(--text);
    max-width: 260px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.file-chooser-clear {
    background: none;
    border: none;
    color: var(--text-dim);
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
    padding: 0 2px;
    flex-shrink: 0;
    font-family: inherit;
}

.file-chooser-clear:hover {
    color: var(--text);
}
```

- [ ] **Step 2: Append the same CSS to `tauri/src/style.css`**

Append the exact same block from Step 1 to the end of `tauri/src/style.css`.

- [ ] **Step 3: Commit**

```bash
git add ui/static/style.css tauri/src/style.css
git commit -m "style: add file-chooser pill CSS to both apps"
```

---

## Task 2: Add JS helpers to the web app (`ui/static/app.js`)

**Files:**
- Modify: `ui/static/app.js` (append before the final closing line, or at top-level)

- [ ] **Step 1: Add the three helper functions to `ui/static/app.js`**

Append the following block to the **end** of `ui/static/app.js`:

```js
// ---------------------------------------------------------------------------
// File chooser — replaces native <input type="file"> with pill UI
// ---------------------------------------------------------------------------

function setupFileChooser(input) {
    const wrapper = document.createElement('div');
    wrapper.className = 'file-chooser';
    input.parentNode.insertBefore(wrapper, input);
    wrapper.appendChild(input);

    const btn = document.createElement('button');
    btn.type = 'button';
    btn.className = 'file-chooser-btn';
    btn.textContent = 'Choose File';
    btn.addEventListener('click', () => input.click());
    wrapper.appendChild(btn);

    const pill = document.createElement('div');
    pill.className = 'file-chooser-pill';

    const nameSpan = document.createElement('span');
    nameSpan.className = 'file-chooser-name';

    const clearBtn = document.createElement('button');
    clearBtn.type = 'button';
    clearBtn.className = 'file-chooser-clear';
    clearBtn.textContent = '×';
    clearBtn.addEventListener('click', () => {
        input.value = '';
        input.dispatchEvent(new Event('change', { bubbles: true }));
    });

    pill.appendChild(nameSpan);
    pill.appendChild(clearBtn);
    wrapper.appendChild(pill);

    input.addEventListener('change', () => {
        if (input.files.length) {
            nameSpan.textContent = input.files[0].name;
            btn.style.display = 'none';
            pill.classList.add('visible');
        } else {
            pill.classList.remove('visible');
            btn.style.display = '';
        }
    });
}

function resetFileChooser(input) {
    const wrapper = input.closest('.file-chooser');
    if (!wrapper) return;
    wrapper.querySelector('.file-chooser-pill').classList.remove('visible');
    wrapper.querySelector('.file-chooser-btn').style.display = '';
}

function initFileChoosers() {
    document.querySelectorAll('input[type="file"]').forEach(setupFileChooser);
}

document.addEventListener('DOMContentLoaded', initFileChoosers);
```

- [ ] **Step 2: Start the web server**

```bash
python ui/server.py
```

Open `http://localhost:5000` (or whichever port the server uses) in a browser.

- [ ] **Step 3: Verify import-file (initial wallet import screen)**

Before any wallet is imported, the app shows the import screen. Verify:
- A "Choose File" button is visible where the file input was.
- Click "Choose File" → the OS file picker opens.
- Pick any `.json` file → the button disappears; a pill appears with the filename.
- Click `×` on the pill → the pill disappears; "Choose File" reappears.
- The native `<input type="file">` is not visible at any point.

- [ ] **Step 4: Verify broadcast-file and reimport-file**

After importing a wallet, navigate to the Broadcast tab and Settings tab. For each:
- The file input area shows a "Choose File" button (not the native browser button).
- Selecting a file shows the pill; clicking × clears it.
- For broadcast-file specifically: after selecting a file, the PSBT textarea should fill with the file contents (existing `loadPsbtFile` logic). After clicking ×, the textarea content should remain (not cleared — this is correct).

- [ ] **Step 5: Commit**

```bash
git add ui/static/app.js
git commit -m "feat: add file chooser pill UI to web app"
```

---

## Task 3: Add JS helpers to the Tauri app (`tauri/src/app.js`) and update call sites

**Files:**
- Modify: `tauri/src/app.js`

The `tauri/src/app.js` already resets `reimport-file` programmatically in two places. After adding the same helper functions, those two reset lines need `resetFileChooser` calls added.

- [ ] **Step 1: Add the three helper functions to `tauri/src/app.js`**

Append the same block from Task 2 Step 1 to the **end** of `tauri/src/app.js`:

```js
// ---------------------------------------------------------------------------
// File chooser — replaces native <input type="file"> with pill UI
// ---------------------------------------------------------------------------

function setupFileChooser(input) {
    const wrapper = document.createElement('div');
    wrapper.className = 'file-chooser';
    input.parentNode.insertBefore(wrapper, input);
    wrapper.appendChild(input);

    const btn = document.createElement('button');
    btn.type = 'button';
    btn.className = 'file-chooser-btn';
    btn.textContent = 'Choose File';
    btn.addEventListener('click', () => input.click());
    wrapper.appendChild(btn);

    const pill = document.createElement('div');
    pill.className = 'file-chooser-pill';

    const nameSpan = document.createElement('span');
    nameSpan.className = 'file-chooser-name';

    const clearBtn = document.createElement('button');
    clearBtn.type = 'button';
    clearBtn.className = 'file-chooser-clear';
    clearBtn.textContent = '×';
    clearBtn.addEventListener('click', () => {
        input.value = '';
        input.dispatchEvent(new Event('change', { bubbles: true }));
    });

    pill.appendChild(nameSpan);
    pill.appendChild(clearBtn);
    wrapper.appendChild(pill);

    input.addEventListener('change', () => {
        if (input.files.length) {
            nameSpan.textContent = input.files[0].name;
            btn.style.display = 'none';
            pill.classList.add('visible');
        } else {
            pill.classList.remove('visible');
            btn.style.display = '';
        }
    });
}

function resetFileChooser(input) {
    const wrapper = input.closest('.file-chooser');
    if (!wrapper) return;
    wrapper.querySelector('.file-chooser-pill').classList.remove('visible');
    wrapper.querySelector('.file-chooser-btn').style.display = '';
}

function initFileChoosers() {
    document.querySelectorAll('input[type="file"]').forEach(setupFileChooser);
}

document.addEventListener('DOMContentLoaded', initFileChoosers);
```

- [ ] **Step 2: Update the `loadSettings` reset at line 972**

Find this code in `tauri/src/app.js` (in the `loadSettings` function):

```js
document.getElementById('reimport-file').value = '';
```

Replace with:

```js
const ri = document.getElementById('reimport-file');
ri.value = '';
resetFileChooser(ri);
```

- [ ] **Step 3: Update the `reimportWallet` reset at line ~1138**

Find this code in `tauri/src/app.js` (inside `reimportWallet`, after a successful `invoke('import_wallet', ...)` call):

```js
        fileInput.value = '';
        alertDiv.innerHTML = '<div class="alert success">Wallet imported successfully!</div>';
```

Replace with:

```js
        fileInput.value = '';
        resetFileChooser(fileInput);
        alertDiv.innerHTML = '<div class="alert success">Wallet imported successfully!</div>';
```

- [ ] **Step 4: Build and run the Tauri app in dev mode**

```bash
cd tauri
cargo tauri dev
```

Wait for the app window to open.

- [ ] **Step 5: Verify all four file inputs in the Tauri app**

Run through each file input:

**import-file** (first screen, before wallet loaded):
- "Choose File" button visible. Click → OS picker opens. Select a `.json` file → pill shows filename, button hidden. Click × → pill gone, button back.

**sign-psbt-file** (Sign tab, offline mode only):
- Same pill behavior. Select a `.psbt` or `.txt` → pill appears. Click × → cleared.

**broadcast-file** (Broadcast tab, online mode):
- Same pill behavior. Selecting a file should populate the PSBT textarea (existing onchange logic). Clicking × should clear the pill but leave the textarea content intact.

**reimport-file** (Settings tab):
- Pill behavior works. After a successful wallet reimport, the pill resets automatically (Step 3 ensures `resetFileChooser` is called). Navigating away from Settings and back also resets the pill (Step 2 via `loadSettings`).

- [ ] **Step 6: Commit**

```bash
git add tauri/src/app.js
git commit -m "feat: add file chooser pill UI to Tauri app"
```
