Files
2026-07-17 21:48:37 +05:30

5.8 KiB

name, description, metadata
name description metadata
features-and-modes The full Slack-grade feature set, the two backend modes, and how to run IIOS live
type
project

Two backend modes (env BFF_BACKEND in apps/web/.env.local, read server-side):

  • mock (default) — rich in-memory JSON workspace; every feature works & persists in-process. Best for demos. Data in apps/web/lib/mock/data.ts + store.ts.
  • iios — proxies the live IIOS NestJS service (real Postgres). Seeded via node scripts/seed-iios.mjs which writes apps/web/lib/iios/seed-manifest.json (real thread ids → channel directory). Bootstrap/channels come from the manifest; messages/inbox/tickets from IIOS live. Verified: 5 channels + 6 users + messages render through the app BFF.

Running IIOS live (no Docker): native PostgreSQL 18, role iios/iios, db iios on 5432 (a separate 5434 cluster crashes under the sandbox's process limits). packages/iios-service/.envDATABASE_URL=postgresql://iios:iios@localhost:5432/iios?schema=public, IIOS_DEV_TOKENS=1. Build: allow prisma/esbuild in D:\iios\pnpm-workspace.yaml, pnpm install && pnpm -r build, prisma generate + migrate deploy, node packages/iios-service/dist/main.js. Redis NOT needed (in-memory socket adapter without REDIS_URL). Full steps: docs/IIOS_INTEGRATION.md.

Slack-grade features (all working, verified via Playwright): channels/DMs/threads; WYSIWYG composer (bold/italic/code/lists); @mention + #channel autocomplete (typing pops a picker; rendered as blue chips by lib/rich-text.tsx); full searchable emoji picker (168, categorized, lib/emoji.ts) in composer + reactions + status; :shortcode: → emoji (:tick:); reactions (hover picker fixed); pins; save (+ Saved page); message edit + delete; attachment upload (file → data-URL preview → send → image preview + Open/Download); scheduled send; huddles; status (emoji picker + clear-after incl. custom datetime); avatar change (color swatches + image upload); copy message link + jump-to-message (scroll); thread "also send to channel"; command palette (⌘K, arrow-nav); notifications; sidebar collapse; inbox; Support Center; light/dark + live theming.

AI + translation (Gemini, server-side only): apps/web/lib/gemini.ts calls Gemini (?key= auth, model gemini-2.0-flash) and returns a discriminated result so 429/errors degrade gracefully. Two BFF routes: POST /api/bff/ai (assistant) and POST /api/bff/translate. Client methods client.ai(prompt, context?) / client.translate(text, lang) use reqSoft (does NOT throw on non-2xx — returns the {ok:false,error} body so the UI shows a clean message). UI: AiPanel right-drawer (open via openAi({context}) from a message's toolbar button; quick actions Summarize / What-should-I-reply / Improve / Explain + free prompt); per-message Translate (LanguageMenu searchable dropdown → inline translated card w/ "Show original"); whole-chat Translate (header ✈ button → banner + autoTranslateTo prop on every MessageItem); composer translate-before-send (✈ toolbar btn → pick language → preview popover → "Use translation" replaces draft). Flags aiAssist + translation. KEY is server-side only (GEMINI_API_KEY, no NEXT_PUBLIC_; verified not in client bundle). The provided free-tier key is quota-exhausted (429) — wiring is proven end-to-end; real output needs a fresh/paid key.

More Slack features added: presence toggle Active/Away/DND (ProfileDrawer → Availability → client.updateMe({presence})); typing indicator moved to a fixed bar ABOVE the composer (incl. DMs); channel create — public adds the whole team automatically, private shows a multi-user selector; invite modal turns emails into chips on ,/Enter; thread composer is the FULL editor, thread replies have edit/delete; editor lists: Enter=new bullet (doesn't send), Tab/Shift- Tab indent/outdent; cross-view live sync via notifyChannelChanged mutation bus (delete + also- send reflect without refresh); threadRootId "Replied to a thread → open" chip; copy-message toolbar button.

Jump-to-message flash (working in a production build): pnpm --filter @lynkd/web build + start; StrictMode double-mount is gone so the WAAPI amber animate() on [data-mid-body] fires. ChannelView reads ?msg via useSearchParams() (jumpMsg) and the effect is keyed on [hasMessages, channelId, jumpMsg]gate on the boolean hasMessages = messages.length>0, NOT the raw count, or every send/incoming message re-fires the jump and yanks the viewport back to the old message. As soon as the target resolves (or after ~2.5s of not finding it) the effect strips ?msg with router.replace(pathname,{scroll:false}) so it's one-shot AND re-clicking the same result re-fires (identical-URL push is a no-op otherwise). Search + notification results now deep-link with ?msg= (CommandPalette.go, NotificationsPanel; Notification/mock data gained an optional messageId; the Liam-mention notif/inbox point at m_21 in d_liam, not m_23 which is d_ava). A search hit on a thread reply (msg with parentId) opens the thread panel (openThread) instead of a ?msg= jump that would never find a main-list node — SearchResult gained parentId. In dev the flash is unreliable (node churn); the scroll always works.

Editor lists: inside a list BOTH Enter (native) and Shift+Enter (execCommand insertParagraph) make the next bullet/number; Tab/Shift-Tab indent 3 levels. Outside a list Enter sends, Shift+Enter soft-newlines. Composer has a visible amber " AI" button (flags.aiAssist) → openAi().

Gotchas: reactStrictMode:false in next.config. Bump __lynkdStore_vN in store.ts after adding store methods (HMR singleton). See qa-and-fixes, iios-relationship, architecture-decisions.