# How the Translation Status Panel Works in FluentRead: Vue 3 Queue Visualization Explained

> Discover how FluentRead's Vue 3 translation status panel visualizes the translation queue with real-time updates and event tracking. Learn its implementation details.

- Repository: [ThinkStu/fluentread](https://github.com/bistutu/fluentread)
- Tags: internals
- Published: 2026-02-26

---

**The translation status panel in FluentRead is a self-contained Vue 3 component that polls the translation queue every 500 milliseconds, listens for custom browser events to track floating-ball translation activity, and automatically manages visibility based on active task counts, pending tasks, and user interactions.**

FluentRead is an open-source browser extension that provides real-time webpage translation through a concurrent task queue. Understanding how its **translation status panel** displays live queue progress requires examining the interplay between the reactive UI layer in [`components/TranslationStatus.vue`](https://github.com/bistutu/fluentread/blob/main/components/TranslationStatus.vue), the queue snapshot logic in [`entrypoints/utils/translateQueue.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/translateQueue.ts), and custom DOM events that signal translation lifecycle changes.

## Architecture Overview

The implementation separates concerns across three distinct layers:

- **Presentation Layer**: [`components/TranslationStatus.vue`](https://github.com/bistutu/fluentread/blob/main/components/TranslationStatus.vue) handles rendering, user interactions (closing the panel), and reactive visibility logic.
- **API Layer**: [`entrypoints/utils/translateApi.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/translateApi.ts) exposes `getTranslationStatus()`, providing a stable interface for UI components to query queue state.
- **Queue Engine**: [`entrypoints/utils/translateQueue.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/translateQueue.ts) maintains the authoritative state of `activeTranslations`, `pendingTranslations`, and concurrency limits.

These layers communicate through polling intervals and custom browser events rather than direct coupling, ensuring the UI remains responsive without blocking the translation engine.

## UI Implementation in TranslationStatus.vue

The panel component uses conditional rendering based on three reactive boolean refs: `isVisible`, `isFloatingBallTranslating`, and `userClosed`. The template only renders when all conditions are satisfied:

```html
<div class="translation-status-container"
     v-if="isVisible && isFloatingBallTranslating && !userClosed">

```

Inside the container, the component displays the current concurrency utilization (`activeTranslations / maxConcurrent`), the pending task count, and a CSS-driven progress bar. The bar width is computed via a `progressStyle` property that calculates percentage values from the queue snapshot.

### Real-Time Status Polling

The component initializes a polling mechanism on mount to keep the display synchronized with the queue state:

```typescript
import { getTranslationStatus } from '../entrypoints/utils/translateApi';

let statusUpdateTimer: number;

const updateStatus = () => {
  const currentStatus = getTranslationStatus();
  status.value = currentStatus;
  isVisible.value = currentStatus.activeTranslations > 0 || 
                    currentStatus.pendingTranslations > 0;
};

onMounted(() => {
  updateStatus();
  statusUpdateTimer = setInterval(updateStatus, 500);
});

onUnmounted(() => {
  clearInterval(statusUpdateTimer);
});

```

This 500-millisecond interval ensures the progress bar and task counters update smoothly without overwhelming the event loop.

### Event-Driven Visibility Control

Beyond polling, the panel reacts to custom events emitted by the floating-ball translator:

```typescript
document.addEventListener('fluentread-translation-started', () => {
  isFloatingBallTranslating.value = true;
  if (!isVisible.value) userClosed.value = false;
});

document.addEventListener('fluentread-translation-ended', () => {
  isFloatingBallTranslating.value = false;
});

```

When the user clicks the close button ("×"), the `userClosed` ref is set to `true`, suppressing the panel until the next translation starts or the page changes.

### Automatic Reset on Context Changes

The component implements automatic state reset to prevent stale UI states across navigation:

- A `visibilitychange` listener clears `userClosed` when the browser tab is hidden.
- A `MutationObserver` watches for URL mutations (single-page navigation) to reset the closed state.

This ensures the panel reappears correctly when users switch back to a tab or navigate to a new page that triggers translations.

## Queue Management and Data Flow

### The Translation Queue Core

The authoritative state resides in [`entrypoints/utils/translateQueue.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/translateQueue.ts), which exports `getQueueStatus()`:

```typescript
export function getQueueStatus() {
  const maxConcurrent = getMaxConcurrentTranslations();
  return {
    activeTranslations,
    pendingTranslations: pendingTranslations.length,
    maxConcurrent,
    isQueueFull: activeTranslations >= maxConcurrent,
    totalTasksInProcess: activeTranslations + pendingTranslations.length
  };
}

```

This module tracks `activeTranslations` (currently executing tasks) and `pendingTranslations` (queued task array), respecting a configurable `maxConcurrentTranslations` limit (defaulting to 6).

### The API Facade

[`entrypoints/utils/translateApi.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/translateApi.ts) provides a thin wrapper to decouple the UI from queue internals:

```typescript
export function getTranslationStatus() {
  return getQueueStatus();
}

```

This abstraction allows the queue implementation to evolve without modifying the Vue component's import statements or call signatures.

## Integrating the Panel

Because [`TranslationStatus.vue`](https://github.com/bistutu/fluentread/blob/main/TranslationStatus.vue) manages its own visibility lifecycle, integration requires minimal boilerplate. Import and declare the component in any root layout or main view:

```vue
<template>
  <main>
    <!-- Application routes and content -->
    <TranslationStatus />
  </main>
</template>

<script setup lang="ts">
import TranslationStatus from '@/components/TranslationStatus.vue';
</script>

```

The component automatically appears whenever the floating ball initiates a translation and the queue contains active or pending tasks.

## Summary

- The **translation status panel** is implemented as a Vue 3 single-file component in [`components/TranslationStatus.vue`](https://github.com/bistutu/fluentread/blob/main/components/TranslationStatus.vue), utilizing conditional rendering based on queue state and user interaction flags.
- **Real-time updates** are achieved through a 500-millisecond polling interval calling `getTranslationStatus()` from [`entrypoints/utils/translateApi.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/translateApi.ts).
- **Queue state** is maintained in [`entrypoints/utils/translateQueue.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/translateQueue.ts), which tracks `activeTranslations`, `pendingTranslations`, and `maxConcurrentTranslations`.
- **Lifecycle synchronization** relies on custom browser events (`fluentread-translation-started`, `fluentread-translation-ended`) and automatic reset logic triggered by `visibilitychange` events and URL mutation observers.
- The architecture decouples the UI from the translation engine via an API facade pattern, ensuring maintainability and testability.

## Frequently Asked Questions

### How does the translation status panel detect when a translation starts?

The panel listens for the custom browser event `fluentread-translation-started` dispatched by the floating-ball translator. When this event fires, the component sets `isFloatingBallTranslating` to true and automatically resets the `userClosed` flag, forcing the panel to become visible if tasks are present in the queue.

### What determines the width of the progress bar displayed in the panel?

The progress bar width is calculated as a percentage of `activeTranslations` divided by `maxConcurrentTranslations`. This value is computed in a reactive `progressStyle` property within [`TranslationStatus.vue`](https://github.com/bistutu/fluentread/blob/main/TranslationStatus.vue) and bound directly to the element's CSS width attribute, updating every 500 milliseconds as the polling interval refreshes the status snapshot.

### Why does the panel sometimes disappear when switching browser tabs?

The component registers a `visibilitychange` event listener that clears the `userClosed` state when the document becomes hidden. Additionally, a `MutationObserver` monitors the URL for navigation changes. Both mechanisms ensure the panel resets its visibility state appropriately, preventing the panel from remaining permanently closed when users return to the tab or navigate to a new page.

### Where is the maximum concurrent translation limit configured?

The concurrency limit is defined in the runtime configuration (default value of 6) and accessed via `getMaxConcurrentTranslations()` within [`entrypoints/utils/translateQueue.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/translateQueue.ts). While the queue module consumes this value directly, the configuration itself typically resides in [`entrypoints/utils/config.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/config.ts), allowing users or developers to adjust throughput based on API rate limits or hardware constraints.