Conference organiser feature requests (multi-language, interactivity & live polling, 1:1 capture) #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Conference organisers reviewed the app and would like a few additions. Existing functionality below is for confirmation; new requests follow.
Already supported (confirm with organisers)
Requested additions
Multi-language UI — English, Spanish, French (the three languages on the CM50 website).
Translations — translate content (messages / programme / notes) across EN/ES/FR.
Interactivity & live polling — one shared engine for collecting and displaying live audience input, used both in flagged "interactable" sessions and in plenary (Mentimeter-style):
1:1 meeting capture — notes for bilateral meetings (e.g. CEO-to-CEO). Visibility: participants in the meeting only (not organisers).
Live translation during presentations. will need live stt model and translation model. presenters should predefine a dictionary of words they will use for the stt to work properly.
we will need audio from the presentation feeding into it which will be processed and then translated.
Implementation Spec for Issue #1
Scope note: this plan covers four of the requested areas. Live presentation translation (streaming STT + translation of presentation audio, from the comments) is deferred to a later iteration and is not implemented here.
Objective
Add four organiser-requested capabilities to the CM50 app, following the established patterns (OSIS schema-first models,
openrpc_server!/openrpc_client!, the existing SSE real-time hub, Dioxus/wasm UI conventions):Architecture Findings
cm50_server/oschema/<domain>/*.oschema. A[rootobject]gets CRUD auto-generated;sid/created_at/updated_atare auto-injected. Domains today:participants,program,community.openrpc_server!macro incm50_server/main.rsgenerates per-domain traits, the dispatcher, the axum router, and writes specs tocm50_server/openrpc/. Impls live incm50_server/rpc/{domain}_impl.rs. CRUD is deliberately stubbed toforbidden; all access is token-authenticated via theAuthhelper inparticipants_impl.rs.hero_lifecycle::sse::{SseBus, SseEvent, sse_router}.community_impl.rs(chat) is the canonical end-to-end example (bus.publish(thread_sid, SseEvent::json("chat.message", &msg))), with an authenticated SSE router merged via theextrarouter inmain.rs. EventSource/WS auth rides the?token=query string.cm50_ui): routes inroutes.rs, global state inapp.rs, typed clients viaopenrpc_client!inapi/mod.rs, wire↔UI seam inservices/backend.rs, live SSE consumer inservices/live.rs. Per-user preference persistence template:components/theme.rs+services/storage.rs(localStorage).cm50_ui/openrpc/*.jsonare manual copies ofcm50_server/openrpc/*.json. Every schema change requires: build server → copy specs → build UI.cm50_webis a static-file SPA host only; all RPC/SSE lives oncm50_server.Implementation Plan
Step 1 — i18n scaffolding (catalog + LANG global + switcher)
Feature: Multi-language UI. Files: create
cm50_ui/src/i18n/{mod,strings}.rs,cm50_ui/src/components/lang.rs; modifycm50_ui/src/main.rs,services/storage.rs,app.rs. Mirror thetheme.rspattern:Lang { En, Es, Fr },LANG: GlobalSignal<Lang>,t(key)lookup over a static EN/ES/FR table,save_lang/load_lang(localStorage), restore at startup,LangSwitchercomponent. Dependencies: none.Step 2 — Route nav + page headers through i18n
Feature: Multi-language UI. Files:
components/navigation.rsandPageHeadusages acrosspages/*.rs. Replace hardcoded labels witht(...)keys; placeLangSwitcherin the shell. Dependencies: Step 1.Step 3 — Interaction domain schema (shared engine)
Feature: Interactivity. Files: create
cm50_server/oschema/interaction/{00_enums,10_space,20_question,30_poll,40_rsvp,90_rpc}.oschema.PollKind = multiple_choice|word_cloud|open_ended|ranking;InteractionSpace[rootobject]{title@index, session_sid, state, rsvp_enabled, qa_enabled};AudienceQuestion[rootobject]{space_sid@index, author_sid, body, upvotes:[str], answered};Poll[rootobject]{space_sid@index, kind, prompt, options:[str], open}with embeddedPollVote{voter_sid, choice_indices:[u8], text};Rsvp[rootobject]{space_sid@index, participant_sid, state}.service InteractionService:space_admin_save,space_snapshot,spaces_for_session,question_post,question_upvote,poll_admin_save,poll_vote,rsvp_set. Dependencies: none.Step 4 — Interaction server impl + real-time results
Feature: Interactivity. Files: create
cm50_server/rpc/interaction_impl.rs; modifymain.rs,service.toml. Mirrorcommunity_impl.rs(stores,Auth,bus). Guarded methods; on every writebus.publish(space_sid, SseEvent::json("interaction.update", &aggregate)). Add authenticated SSE router keyed onspace_sid. New socketcm50/rpc_interaction.sock(sse=true). Stub generated CRUD to forbidden. Dependencies: Step 3.Step 5 — Meetings (1:1 notes) schema
Feature: 1:1 capture. Files: create
cm50_server/oschema/meetings/{00_enums,10_meeting,90_rpc}.oschema.Meeting[rootobject]{participant_sids:[str] (exactly two), title@index, scheduled_at:otime, notes:[MeetingNote]}; embeddedMeetingNote{author_sid, body, created_at:otime}.service MeetingsService:meeting_create,meetings_for_me(token),meeting_note_add,meeting_get. Visibility: participants-only; NO admin override. Dependencies: none.Step 6 — Meetings server impl (participant-only visibility)
Feature: 1:1 capture. Files: create
cm50_server/rpc/meetings_impl.rs; modifymain.rs,service.toml.meeting_createenforces caller ∈ participants, len==2.meetings_for_mereturns only meetings the caller participates in — no admin special-casing (explicit requirement).meeting_note_add/meeting_getenforce membership. New socketcm50/rpc_meetings.sock. Dependencies: Step 5.Step 7 — Content translation (server)
translateRPCFeature: Translations. Files: modify
cm50_server/oschema/program/{61_ai,90_rpc}.oschema,cm50_server/rpc/program_impl.rs. AddTranslateReq{token, text, target_lang}/TranslateResult{text, available, error}andtranslate(...)toProgramService. Reuse theai_complete_blockingplumbing with a "translate to {ES|FR|EN}, preserve Markdown" prompt viaspawn_blocking. Dependencies: none.Step 8 — Interaction UI models + service layer
Feature: Interactivity. Files: modify
cm50_ui/src/data/models.rs,services/backend.rs,api/mod.rs,services/live.rs; addcm50_ui/openrpc/openrpc_interaction.json(copied after Step 4 build). Add UI structs +interactionclient + service fns (space_snapshot,poll_vote,question_post,question_upvote,rsvp_set, admin saves). Generalizelive.rsinto asubscribe_topic(...)helper reused by interaction + chat. Dependencies: Step 4.Step 9 — Interaction UI pages (RSVP / Q&A / polls / live results)
Feature: Interactivity. Files: create
cm50_ui/src/pages/interact.rs,components/interact.rs; modifyroutes.rs,navigation.rs,main.rs, link frompages/event_program.rs. Render RSVP, Q&A (post + upvote), four poll widgets, and live results (bar chart / tag cloud / list / ranked list); subscribe viasubscribe_topic("interaction.update")keyed on space sid (mirrorcomponents/chat.rsuse_effect + drop). Route/interact/:space_id. Dependencies: Steps 8, 10, 1.Step 10 — Mark sessions interactable + link to spaces
Feature: Interactivity. Files: modify
cm50_server/oschema/program/40_session.oschema,program_impl.rs,cm50_server/seed/sessions.json, plus UIEventSessionmodel +backend.rsconverter. Addinteractable:bool+interaction_space_sid:str; seed a couple of interactable sessions + matchingInteractionSpaceinseed/interaction.json. Dependencies: Step 3 (entity), Step 4 (seed loader).Step 11 — Content translation UI wiring
Feature: Translations. Files: modify
services/backend.rs(addtranslate), add "Translate" affordances tocomponents/chat.rs,pages/event_program.rs,pages/meeting_notes.rs; re-copyopenrpc_program.jsonafter Step 7. Default target = currentLANG; inline result (reuse the AI-note inline pattern in chat). Dependencies: Steps 7, 1.Step 12 — 1:1 meeting notes UI
Feature: 1:1 capture. Files: create
cm50_ui/src/pages/meeting_notes.rs; modifyroutes.rs,navigation.rs,api/mod.rs(meetingsclient),backend.rs,data/models.rs,main.rs; addopenrpc_meetings.json. List my meetings (server-enforced), "New 1:1" creator (pick a directory participant), per-meeting note timeline with add-note. Route/meetings. Dependencies: Steps 6, 1.Step 13 — Integration consolidation + spec re-copy + build verification
Feature: cross-cutting. Files:
main.rs,service.toml, allcm50_ui/openrpc/*.json. Reconcile the macro-generatedserve_domains_with(...)signature and theextrarouter.merge()chain across the new domains; build server (regenerates specs) → copy specs → build wasm UI. Final gate. Dependencies: all prior.Acceptance Criteria
program.translatereturns ES/FR/EN translations; "Translate" affordance on messages and session descriptions shows the result inline, with graceful fallback when the provider is unavailable.InteractionServicepowers RSVP, Q&A (post + upvote), and all four poll types, for bothinteractablesessions and standalone plenary spaces.interaction.update), no reload.meetings_for_mereturns only the caller's meetings; an admin/organiser cannot read or list meetings they are not a participant of.cm50_serverbuilds (regenerating specs); UI specs re-copied;cm50_uiwasm builds.Notes / Risks
serve_domains_witharity changes with each newserviceblock; Steps 4/6 expect a build to discover the signature, and Step 13 reconciles it.cm50_ui/openrpc/*.jsonare manual copies of the server specs — build server → copy → build UI on every schema change (most likely source of compile drift).main.rs/service.toml; edits are serialized, with Step 13 as the reconciliation gate.spawn_blocking(asai_completealready does).require_adminoverride inmeetings_impl.rs(differs from every other domain).Implementation summary
Implemented on branch
development. Live presentation translation (the streaming STT/translation from the comments) is intentionally deferred to a later iteration and is not part of this change.What was built
1. Multi-language UI (EN / ES / FR)
cm50_ui/src/i18n/strings.rs) wired to the UI via aLANGglobal signal and at(key)lookup, mirroring the existing dark-theme preference. Selection persists in localStorage (cm50.lang) and is restored at startup.t(...)and re-render live on switch.2. Content translation (messages / programme / notes across EN/ES/FR)
program.translate(text, target_lang)reusing the existing AI completion plumbing (runs off-thread viaspawn_blocking, same provider-unavailable fallback shape asai_complete).3. Interactivity & live polling (one shared engine)
interactionOSIS domain:InteractionSpace,Poll(with embedded votes),AudienceQuestion,Rsvp. One engine serves both sessions flaggedinteractableand standalone plenary spaces.interaction.update, keyed on the space), reusing the existing live-chat hub — no reload.interactable+interaction_space_sid; the programme surfaces a "Join interaction" link on interactable sessions. A demo interactable session + space are seeded underCM50_DEMO.4. 1:1 meeting capture (participant-only)
meetingsOSIS domain:Meeting(exactly two participants) with an embeddedMeetingNotetimeline.meetings_for_mereturns only the caller's meetings and there is deliberately NO organiser/admin override (this differs from every other domain, and is documented in the code).Architecture notes
forbidden) as in the existing domains.rpc_interaction,rpc_meetings); the interaction live stream rides the existing community SSE socket. OpenRPC specs were regenerated and the UI clients updated.Files (high level)
oschema/interaction/*,oschema/meetings/*,oschema/program/{40_session,61_ai,90_rpc}.oschema,rpc/interaction_impl.rs,rpc/meetings_impl.rs,rpc/program_impl.rs,main.rs,service.toml,seed/{interaction,sessions}.json.i18n/*,components/{lang,interact,navigation,cards,chat}.rs,pages/{interact,meeting_notes,admin}.rs,services/{backend,live,storage}.rs,data/models.rs,api/mod.rs,routes.rs,app.rs, regeneratedopenrpc/*.Build verification
cm50_server:cargo buildsucceeds (warnings only).cm50_ui:cargo check --target wasm32-unknown-unknownreports zero errors;dx build --platform webproduces the bundle successfully.Test results
The hermetic integration suite (
cm50_tests) currently reports 0 passed / 16 failed. This is a pre-existing failure unrelated to this change: the suite fails identically on a clean checkout ofmain(verified by stashing all changes), with every test erroring at the fixture'sparticipant_admin_savebootstrap-secret step (-32001: session expired or unknown). No new tests were affected by this work, and no regression was introduced. The harness bootstrap-secret path should be repaired separately so the suite (and new coverage for the interaction/meetings domains) can run.Follow-ups
cm50_testsbootstrap-secret failure, then add integration coverage for theinteractionandmeetingsdomains.Closing this umbrella in favour of granular, claimable issues (decided 2026-06-24). Mapping:
See the priority board for the full ranked list.