Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,56 @@
All notable changes to TigerRoundtable are documented here. Format loosely follows Keep a Changelog;
this module is **experimental** and the surface may shift between beta releases.

## [0.3.0-beta] — 2026-07-21

The table can now **read** shared links, files, and images, and **write** downloadable files and diagrams.

### Added — reading
- **URL reading.** When someone shares a link, the server fetches the page, strips it to text, and folds
it into the AIs' context so they review the real content — not just the URL. SSRF-guarded (blocks
private/reserved ranges, the cloud metadata IP, non-`http(s)`, odd ports; pins the validated IP so DNS
can't rebind), no redirects, bounded (count/size/time), file-cached, config-gated
(`roundtable.read.urls.enabled`).
- **File attachments.** Humans attach files (📎 or drag-drop) the AIs read: text/markdown/CSV/JSON/code
verbatim, **PDF** (operator scan) and **DOCX** (ZipArchive, else PharData). Stored **privately** on the
configured media disk under the module's own `roundtable/<org_id>/<room_id>/` prefix — a separate
`roundtable_attachment` table, never `media`, so nothing appears in the Media Manager — and served only
through an ACL + participation-gated streamer. 10 MB cap; extracted text cached on the row.
- **Images (vision).** Attached images are read **natively** when every active AI seat is vision-capable;
if any seat is text-only, one **transcription+description (caption/OCR) pass** runs up front and everyone
reads that text instead — decided per room via core `Tiger_Agent_Provider_Factory::supportsVision`.
5 MB cap; caption cached on the row.

### Added — writing
- **File output.** Any message downloads as **.md / .txt** (client-side) or **.docx** (generated
server-side as real OOXML). An AI can hand back a named deliverable by emitting a `file:<name.ext>`
fenced block, rendered as a download card (native extension + "as .docx").
- **Diagrams & drawings.** An `svg` fenced block (or raw `<svg>`) renders **inline** after sanitizing
(script / `on*` / `foreignObject` / external refs stripped) with a **.svg** download; a `mermaid` block
renders as a **live diagram** via a self-hosted, **lazy-loaded** Mermaid (fetched only when a diagram
appears, served through an immutable-cached asset route) with a **.mmd** download.

### Migrations
- `20260721000003` — `roundtable_attachment` (private file/image attachments + cached extract/caption).

### Requires
- tiger-core **≥ 0.36.0-beta** (multimodal provider input + `supportsVision`).

## [0.2.0-beta] — 2026-07-21

### Added
- **People, not just bots.** Participants are now AI seats *and* invited org users. The add-participant
picker chooses **AI participant** (persona) or **Invite a person** (from the discoverable pool).
- **Membership / opt-in.** A `roundtable_member` table gates discoverability: users **self-join** or are
**invited** (accept → active); guests never qualify. A non-member sees a Join/Accept gate.
- **Invited people chat live.** Any human's message opens a round (turn engine keys on role, not the
moderator); AIs react to anyone. A ~4s poll keeps multiple viewers in sync — plain HTTP, no sockets.
- **Per-room role floor.** The creator caps participants to a role-or-higher (`roundtable.min_role`,
checked against the live ACL graph). Managers create/manage + invite; members read/post in their rooms.
- **Members modal** (invite org users) + a manager/member-aware UI (management controls hidden for members).

### Migrations
- `20260721000002` — `roundtable_member`, `roundtable.min_role`, `roundtable_participant.user_id`.
## [0.1.3-beta] — 2026-07-21

### Fixed
Expand Down
45 changes: 45 additions & 0 deletions assets/roundtable.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ html, body { height: 100%; }
.rt-msg-text { font-size: .95rem; line-height: 1.5; white-space: pre-wrap; word-wrap: break-word; }
.rt-msg-you .rt-msg-name { color: var(--bs-primary, #1971c2); }
.rt-thinking { display: flex; gap: 11px; padding: 8px 6px; align-items: center; color: var(--bs-secondary-color, #6c757d); font-size: .85rem; font-style: italic; }
.rt-turn-error { margin: 6px 6px; padding: 8px 12px; border-radius: 8px; font-size: .85rem; color: var(--bs-danger-text-emphasis, #b02a37); background: var(--bs-danger-bg-subtle, #f8d7da); border: 1px solid var(--bs-danger-border-subtle, #f1aeb5); }
.rt-dots span { animation: rt-blink 1.2s infinite both; }
.rt-dots span:nth-child(2){animation-delay:.2s} .rt-dots span:nth-child(3){animation-delay:.4s}
@keyframes rt-blink { 0%,80%,100%{opacity:.2} 40%{opacity:1} }
Expand All @@ -69,6 +70,50 @@ html, body { height: 100%; }
.rt-composer .rt-actions { display: flex; align-items: center; gap: 8px; }
.rt-hint { font-size: .72rem; color: var(--bs-secondary-color, #adb5bd); margin-top: 6px; display: flex; justify-content: space-between; }

/* ---- Attachments: composer chips + message file cards -------------------- */
.rt-composer.rt-drop .rt-input-wrap { border-color: var(--bs-primary, #0d6efd); box-shadow: 0 0 0 3px rgba(13,110,253,.15); }
.rt-attach-btn { background: transparent; border: 0; color: var(--bs-secondary-color, #adb5bd); font-size: 1.05rem; line-height: 1; padding: 2px 4px; align-self: flex-end; }
.rt-attach-btn:hover { color: var(--bs-primary, #0d6efd); }
.rt-pending { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.rt-chip { display: inline-flex; align-items: center; gap: 6px; max-width: 240px; font-size: .8rem; padding: 4px 8px; border-radius: 999px; background: var(--bs-secondary-bg, #e9ecef); color: var(--bs-body-color, #212529); }
.rt-chip-load { opacity: .7; }
.rt-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-chip-x { background: transparent; border: 0; color: inherit; font-size: 1rem; line-height: 1; padding: 0 2px; opacity: .6; }
.rt-chip-x:hover { opacity: 1; }
.rt-attachments { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.rt-attach { display: inline-flex; align-items: center; gap: 8px; max-width: 260px; text-decoration: none; padding: 7px 12px; border-radius: 9px; border: 1px solid var(--bs-border-color, #dee2e6); background: var(--bs-tertiary-bg, #f8f9fa); color: var(--bs-body-color, #212529); }
.rt-attach:hover { border-color: var(--bs-primary, #0d6efd); }
.rt-attach i { font-size: 1.1rem; color: var(--bs-secondary-color, #6c757d); flex: 0 0 auto; }
.rt-attach-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .85rem; }
.rt-attach-meta { font-size: .72rem; color: var(--bs-secondary-color, #adb5bd); flex: 0 0 auto; }
.rt-attach-img { display: inline-block; border-radius: 9px; overflow: hidden; border: 1px solid var(--bs-border-color, #dee2e6); line-height: 0; }
.rt-attach-img img { max-width: 280px; max-height: 220px; width: auto; height: auto; display: block; }
.rt-attach-img:hover { border-color: var(--bs-primary, #0d6efd); }

/* ---- AI output: file blocks, code, SVG/Mermaid, per-message download ------ */
.rt-msg-dl { margin-left: auto; }
.rt-msg-dl-btn { background: transparent; border: 0; color: var(--bs-secondary-color, #adb5bd); font-size: .8rem; opacity: 0; padding: 0 4px; line-height: 1; }
.rt-msg:hover .rt-msg-dl-btn, .rt-msg-dl.show .rt-msg-dl-btn { opacity: .65; }
.rt-msg-dl-btn:hover { opacity: 1; color: var(--bs-primary, #0d6efd); }
.rt-code { background: var(--bs-tertiary-bg, #f8f9fa); border: 1px solid var(--bs-border-color, #dee2e6); border-radius: 8px; padding: 10px 12px; margin: 8px 0; overflow-x: auto; font-size: .82rem; }
.rt-code code { white-space: pre; }
.rt-card-actions { display: flex; gap: 6px; margin-top: 8px; }
.rt-filecard { border: 1px solid var(--bs-border-color, #dee2e6); border-radius: 10px; margin: 8px 0; overflow: hidden; }
.rt-filecard-head { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: var(--bs-tertiary-bg, #f8f9fa); border-bottom: 1px solid var(--bs-border-color, #dee2e6); }
.rt-filecard-head > i { color: var(--bs-secondary-color, #6c757d); }
.rt-filecard-name { font-weight: 600; font-size: .85rem; flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-filecard .rt-card-actions { margin: 0; flex: 0 0 auto; }
.rt-filecard-body { margin: 0; padding: 10px 12px; font-size: .8rem; white-space: pre-wrap; max-height: 260px; overflow: auto; background: transparent; }
.rt-svgcard, .rt-mermaidcard { margin: 8px 0; padding: 12px; border: 1px solid var(--bs-border-color, #dee2e6); border-radius: 10px; background: var(--bs-body-bg, #fff); }
.rt-svg-render, .rt-mermaid-render { text-align: center; overflow-x: auto; }
.rt-svg-render svg, .rt-mermaid-render svg { max-width: 100%; height: auto; }
.rt-mermaid-render .rt-code { text-align: left; }

/* ---- Membership gate + manager-only hiding ------------------------------- */
.rt-gate { position: fixed; inset: 0; display: grid; place-items: center; background: var(--bs-body-bg, #fff); z-index: 1090; }
.rt-gate-card { text-align: center; max-width: 400px; padding: 2rem 1.5rem; }
.rt-hidden { display: none !important; }

@media (max-width: 720px) {
.rt-side { width: 76px; flex-basis: 76px; }
.rt-side .rt-hide-narrow, .rt-part-body, .rt-topic-btn .rt-topic-name { display: none; }
Expand Down
Loading