Skip to content

Mobile-responsive layout, image attachments, session list - #18

Open
LeonmanRolls wants to merge 3 commits into
VVander:mainfrom
LeonmanRolls:mobile-and-features
Open

Mobile-responsive layout, image attachments, session list#18
LeonmanRolls wants to merge 3 commits into
VVander:mainfrom
LeonmanRolls:mobile-and-features

Conversation

@LeonmanRolls

Copy link
Copy Markdown

Three additions tested running on a Mac, accessed from an iPhone via Tailscale.

1. Mobile-responsive layout (38e796b)

The existing layout was desktop-only; on a phone the input bar sat below the visible viewport (iOS Safari 100vh includes the URL-bar area) and message text overflowed off-screen.

  • @media (max-width: 640px) breakpoint tightens header / message / input-bar padding.
  • Hides the wordmark and connection-status text on phones to free header room.
  • Bumps prompt-input font-size to 16px on small screens so iOS Safari doesn't auto-zoom on focus.
  • overflow-wrap: anywhere on message text and bubbles so long unbroken strings (URLs, tokens) can't push the layout wide.
  • min-width: 0 on the prompt-input so flex doesn't keep it stuck at placeholder-text width.
  • overflow-x: hidden on #app as a safety net.
  • #app height: 100vh100dvh (with 100vh fallback) so the input bar stays inside the visible viewport on mobile Safari when the URL bar is showing.

2. Image attachments (d48d3f6)

The pi SDK already accepts an images?: ImageContent[] array on PromptOptions; this wires the existing capability through to the user.

Server (server/index.ts):

  • New images field on the prompt WebSocket command.
  • Validates each entry: shape, mime type (png/jpeg/gif/webp), decoded size (≤10 MB), drops anything that fails.
  • Passes through to session.prompt(text, { images }).
  • Allows image-only prompts (no longer requires text).

Frontend (index.html, src/main.ts, src/style.css):

  • 📎 button next to the textarea triggers a hidden file input.
  • Picked files are FileReader-encoded to base64 and shown as a thumbnail strip above the input bar, each with an × to remove.
  • On send, images are packed into the WebSocket payload alongside the text and cleared from the strip.
  • User-message bubbles render any attached images inline.
  • state_sync rehydration extracts image content from past user messages so attachments survive a page reload.

Note: whether the agent can see the attached image depends on the active model. Vision-capable models render and reason about it; text-only models receive the payload but ignore the image content.

3. Session list + resume (dcd2e37)

Previously there was no way to navigate back to a prior chat — clicking + New silently abandoned the current session (the JSONL file on disk was preserved, but the UI didn't expose it). Wires the SDK's existing capability — AgentSession.switchSession() and SessionManager.getSessionDir() — through to the user.

Server:

  • list_sessions WebSocket command. Reads the SessionManager's session directory, parses each .jsonl header, finds the first user message for a preview snippet, sorts newest-first.
  • switch_session WebSocket command. Validates the requested file is inside the session directory (path-traversal guard), calls session.switchSession(path), then broadcasts state_sync so all connected clients re-render.

Frontend:

  • New "⌛ History" button in the header next to "+ New".
  • Click it → sends list_sessions → dialog (reusing the existing dialog-overlay) lists prior chats with timestamp + first user-message preview. The active session is highlighted.
  • Click an entry → sends switch_session → server broadcasts state_sync → message list re-hydrates.

Test plan

  • npm run build and npm run build:server both clean.
  • Local server still serves HTTP 200 on 127.0.0.1:8080.
  • Tailscale serve proxy delivers HTTPS to a phone, layout fits without horizontal overflow.
  • Image picked on phone shows as thumbnail, sends to agent, persists in session JSONL on disk with correct { type: "image", data, mimeType } shape.
  • ⌛ History lists prior sessions in this cwd; clicking one swaps the chat in place.

- add @media (max-width: 640px) breakpoint tightening header, message,
  and input-bar padding
- hide wordmark and connection-status text on phones to free header room
- bump prompt-input font-size to 16px on small screens so iOS Safari
  doesn't auto-zoom when the textarea is focused
- add overflow-wrap: anywhere on message text and bubbles so long
  unbroken strings (URLs, tokens) can't push the layout wide
- min-width: 0 on the prompt-input so flex doesn't keep it stuck at
  placeholder text width
- overflow-x: hidden on #app as a safety net
- switch #app height from 100vh to 100dvh (with 100vh fallback) so the
  input bar stays inside the visible viewport on mobile Safari when the
  URL bar is showing
Add the ability to attach images to a prompt from the web UI. The pi SDK
already accepts an `images?: ImageContent[]` array on prompt options, so
this wires the existing capability through to the user.

Server (server/index.ts):
- Accept an optional `images` array on the `prompt` WebSocket command.
- Validate each entry: shape, mime type (png/jpeg/gif/webp), decoded size
  (≤10 MB), and drop anything that fails.
- Pass through to session.prompt(text, { images }).
- Allow image-only prompts (no longer require text).

Frontend (index.html, src/main.ts, src/style.css):
- New 📎 button next to the textarea triggers a hidden file input.
- Picked files are FileReader-encoded to base64 and shown as a
  thumbnail strip above the input bar, each with an × to remove.
- On send, images are packed into the WebSocket payload alongside
  the text and cleared from the strip.
- User-message bubbles render any attached images inline.
- state_sync rehydration extracts image content from user messages so
  past attachments survive a page reload.
- Mobile breakpoint shrinks the chips and bounds bubble images to 60vw.
The web UI previously had no way to navigate back to a prior chat —
clicking + New silently abandoned the current session (the JSONL file
on disk is preserved by pi-coding-agent, but the UI didn't expose it).

Wires the SDK's existing capability — AgentSession.switchSession() and
SessionManager.getSessionDir() — through to the user.

Server (server/index.ts):
- Add `list_sessions` WebSocket command. Reads the SessionManager's
  session directory, parses each .jsonl header, finds the first user
  message for a preview, sorts newest-first.
- Add `switch_session` WebSocket command. Validates the requested file
  is inside the session directory (path-traversal guard), calls
  session.switchSession(path), then broadcasts state_sync so all
  connected clients re-render.

Frontend (index.html, src/main.ts):
- New "⌛ History" button in the header next to "+ New".
- Clicking it sends `list_sessions` and shows a dialog (reusing the
  existing dialog-overlay) listing prior chats with timestamp + first
  user-message preview. The active session is highlighted.
- Clicking an entry sends `switch_session` and the server broadcasts
  state_sync, which re-rehydrates the message list.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant