Skip to content

Feat/chat attach - #88

Open
dailysergey wants to merge 4 commits into
0717376:mainfrom
dailysergey:feat/chat-attach
Open

Feat/chat attach#88
dailysergey wants to merge 4 commits into
0717376:mainfrom
dailysergey:feat/chat-attach

Conversation

@dailysergey

Copy link
Copy Markdown

Что делает

Добавляет кнопку-скрепку в чат ассистента во всех трёх фронтах (вики, задачи, книги). По клику — системный file-picker (multiple, любые расширения); файл уходит в общий /storage/Входящие с parse=1, бэкенд возвращает готовый parse_prompt, фронт отправляет эту строку в чат текущей surface. Дальше — как обычная реплика: SKILL_NUDGE направит агента в нужный навык (wiki / tasks / books).

Зависит от

#87 (feat/upload-parse) — без parse_prompt в ответе /storage/upload и без FILES_DIR в add_dirs Claude-движка эта фича не работает. Ждать мержа #87 (либо мержить эту PR сразу после, чтобы rebase был чистым).

Что где

Frontend-wiki (React):

  • components/InputArea.tsx — hidden <input type=file> + кнопка Paperclip слева от textarea, состояние uploading со спиннером.
  • components/InputArea.module.css — стиль .attachBtn вровень с .sendBtn (36×36).
  • lib/i18n.tsattachFile (ru/en).

Frontend-tasks (React):

  • src/ChatPane.tsx — то же, что в wiki, но со своим useChat.send. Catch пишет в console.error + alert(), а не в WS — чтобы «ошибка загрузки» не показывалась как «связь потеряна».
  • src/api.ts — новая storageUpload(dir, file, {parse}) → {path, size, parse_prompt?}.
  • src/nginx.conf — добавлен location /storage с client_max_body_size 512m и proxy_request_buffering off; без него /storage/upload уходил на SPA-fallback index.html, res.json() падал, чат ругался «связь потеряна».
  • src/styles.css, src/i18n.ts — стили и локализация attach_file.

Frontend-books (vanilla JS):

  • index.html — иконка i-clip в свг-defs, кнопка #sheetAttach и hidden #sheetFile в .sheet-foot.
  • src/main.js — wire кнопки + upload через новую storageUploadForParse.
  • src/library.jsstorageUploadForParse(file, dir) рядом с существующим uploadBook.
  • src/style.css, src/i18n.js — стили и attachFile.

Общий inbox

Скрепка кладёт файл в Входящие — ту же папку, куда падают файлы, пришедшие боту в Telegram (TG_MEDIA_DIR там отдельный, но пользовательский inbox один — так у человека один поток «входящих», где бы он ни прикрепил).

Как проверить

  1. Слить feat(storage): upload + auto-parse — «загрузил файл, агент разобрал» #87 (или дождаться, что мейнтейнер сольёт вместе).
  2. ./bender update — пересобрать.
  3. Открыть любую surface (wiki / tasks / books), в правой панели чата — 📎 слева от textarea. Выбрать любой файл (PDF/DOCX/TXT/картинка).
  4. В чате возникает сообщение [Пользователь загрузил файл в «Файлы»: /app/files/Входящие/…], агент открывает через Read и раскладывает по нужному навыку.

Проверки

  • python -m py_compile — n/a (backend не менялся).
  • cd frontend-wiki && npx tsc --noEmit — OK.
  • cd frontend-tasks && npx tsc --noEmit — OK.
  • node --check frontend-books/src/{main,library}.js — OK.
  • Ручной smoke: скрепка в вики и задачнике — файл долетает, prompt появляется в чате, агент отвечает.

dailysergey and others added 4 commits September 1, 2026 14:09
Связывает две части, которые уже жили порознь: файловое хранилище /storage и чат.

Backend:
- POST /storage/upload?parse=1 возвращает готовый `parse_prompt` c абсолютным
  путём файла внутри контейнера; сам агент отсюда не дёргается — разбор идёт
  той же WebSocket-нитью, что и любой запрос пользователя.
- engines/claude.py: FILES_DIR добавлен в add_dirs, чтобы Read/Grep работали
  напрямую (раньше — только через Bash cwd-trick).

Frontend (wiki):
- StorageView: чекбокс «Разобрать агентом после загрузки» (сохраняется в
  localStorage), кнопка «Разобрать» на карточке файла. Оба пути стучатся в чат
  одним CustomEvent `bender:chat-send` — StorageView не знает про ChatPane.
- ChatPane: слушатель этого CustomEvent, отправляет строку через существующий
  useWebSocket.send.
- api.ts: storageUpload теперь возвращает {path, size, parse_prompt?}.
- i18n: три новых строки (ru + en).

Что можно теперь: перетащить PDF/DOCX/картинку в «Файлы» — с включённым
чекбоксом или нажав «Разобрать» — и агент прочитает файл и коротко расскажет,
что там (плюс предложит разложить в вики/задачи).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Кнопка-скрепка появляется в input'е чата всех трёх фронтов (вики, задачи,
книги). По клику — системный file-picker (multiple, любые расширения);
файл уходит в общий /storage/Входящие с parse=1, бэкенд возвращает готовый
parse_prompt со свежим путём внутри контейнера, фронт отправляет эту
строку в чат текущей surface. Дальше — как обычная реплика: SKILL_NUDGE
на бэкенде подсказывает агенту правильный навык (wiki / tasks / books).

Технически:
- зависит от feat/upload-parse (endpoint parse_prompt + FILES_DIR в
  add_dirs Claude);
- вики: InputArea.tsx + CSS + i18n.ts (общий react-компонент);
- задачи: ChatPane.tsx + api.ts (storageUpload) + styles.css + i18n.ts;
- книги: index.html (иконка i-clip, кнопка #sheetAttach, hidden
  #sheetFile) + main.js (wire) + library.js (storageUploadForParse) +
  style.css + i18n.js;
- inbox — общая папка «Входящие», куда уже приземляется всё, что
  приходит боту в Telegram.

Что теперь можно: открыть любую surface, скрепка → выбрать любой файл →
он положится в файловое хранилище и агент сразу его разберёт
(с раскладкой по нужному навыку).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Скрепка была на 4px ниже, чем mic/send: меньшая высота (32/30/34 vs 36/34/38)
+ inputRow с align-items:flex-end. Синхронизирую с размерами send-кнопки
во всех трёх фронтах — скрепка теперь одной высоты с mic.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…вращал HTML

frontend-tasks/nginx.conf: regex `^/(tasks|cron|skills|curator|files)` не
ловил /storage, и запрос /storage/upload уходил на SPA-fallback (index.html).
res.json() падал с SyntaxError, catch звал send() → WS не был готов принять
post-error → в чате возникало «связь с ассистентом потеряна» вместо
поднявшегося prompt'а.

- добавлен `location /storage` с `client_max_body_size 512m` и
  `proxy_request_buffering off` (как у wiki);
- catch в ChatPane больше не пишет в WS: `console.error` + `alert()` —
  ошибку загрузки не путать с обрывом чата.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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