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

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, the queue snapshot logic in entrypoints/utils/translateQueue.ts, and custom DOM events that signal translation lifecycle changes.

Architecture Overview

The implementation separates concerns across three distinct layers:

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:

<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:

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:

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, which exports getQueueStatus():

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 provides a thin wrapper to decouple the UI from queue internals:

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 manages its own visibility lifecycle, integration requires minimal boilerplate. Import and declare the component in any root layout or main view:

<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, 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.
  • Queue state is maintained in 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 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. While the queue module consumes this value directly, the configuration itself typically resides in entrypoints/utils/config.ts, allowing users or developers to adjust throughput based on API rate limits or hardware constraints.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →