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
2 changes: 1 addition & 1 deletion app/src/features/chat/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export const ChatView = memo(function ChatView({ bot, stopped }: { bot: Bot; sto
</div>
)}
<SeenSince.Provider value={openedAt}>
<ol aria-label={t.chat.view.messages} className="flex flex-col gap-6">
<ol aria-label={t.chat.view.messages} className="offscreen-rows flex flex-col gap-6">
{list}
</ol>
</SeenSince.Provider>
Expand Down
2 changes: 1 addition & 1 deletion app/src/features/messages/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function Timeline({
</p>
)}
{count === 0 && !loading && !error && <p className="p-5 text-muted text-sm">{empty}</p>}
<ol aria-label={text.list}>
<ol aria-label={text.list} className="offscreen-rows">
{messages.map((message) => (
<MessageRow key={message.id} message={message} />
))}
Expand Down
1 change: 1 addition & 0 deletions app/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
@import "./effort.css";
@import "./motion-less.css";
@import "./paused.css";
@import "./offscreen.css";

/* Colors are tokens so the light and dark themes swap in one place
(spec 15.3). State colors always go with an icon and a label. */
Expand Down
9 changes: 9 additions & 0 deletions app/src/offscreen.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* Long lists (the chat, a crew's messages) keep every row in the page.
Rows out of sight skip layout and paint until they scroll near, and
keep the size they last had so the scrollbar stays still. The newest
rows are left out: that is where cards arrive with a glow that would
be clipped, and where the owner looks. */
.offscreen-rows > li:not(:nth-last-child(-n + 8)) {
content-visibility: auto;
contain-intrinsic-size: auto 120px;
}
2 changes: 2 additions & 0 deletions docs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,8 @@ Componentes dependem só de `BotloftApi` e `Host`, nunca do cliente concreto nem

O store recarrega crews, bots e `system.status` a cada (re)conexão e depois segue as notificações. `system.status` não tem notificação e é relido a cada 15 s, menos com a janela fora de vista (minimizada, escondida perto do relógio ou aberta escondida no login do Windows): aí para, e é relido quando ela volta. Fora de vista, as animações também param onde estão (`paused.css`) e continuam quando a janela volta. O Windows pode não avisar a página quando o próprio app esconde a janela, então o app marca isso ele mesmo (`shell/visibility.ts`), e a janela voltar à frente desmarca.

O chat e as mensagens de uma equipe guardam todas as páginas já carregadas. As linhas fora de vista não são diagramadas nem pintadas até chegarem perto da tela (`content-visibility: auto`, em `offscreen.css`), e guardam o tamanho que tiveram para a barra de rolagem não pular. As 8 mais novas ficam de fora: é onde os cartões chegam com o brilho de atenção, que a contenção cortaria.

- **Deliveries:** entram no store pela message (cada message tem uma). Vêm as 500 atualizadas mais recentemente e todas as mortas, depois cada `delivery.changed`.
- **Tasks:** todas, depois cada `task.changed`.
- **Chat e timeline** não ficam no store. Cada um carrega uma página (50) do que mostra, pede as anteriores sob demanda com `before` e acrescenta o que chega por notificação (`chat.item`, `chat.delta`, `message.created`).
Expand Down
Loading