Skip to content

feat(ui): Stream deployment and service action progress#76

Merged
nfebe merged 2 commits into
mainfrom
feat/150/streamed-deployment-actions
Jun 29, 2026
Merged

feat(ui): Stream deployment and service action progress#76
nfebe merged 2 commits into
mainfrom
feat/150/streamed-deployment-actions

Conversation

@nfebe

@nfebe nfebe commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Pairs with flatrun/agent#150 (agent side returns action progress as streamed jobs).

The dashboard now enqueues an action, streams its compose output live, and falls back to polling if the websocket drops. The job id is remembered, so reopening the page resumes an action still in flight and shows its final result. Single-service actions render progress inline under the service (triggerable from the service buttons or the actions dropdown), and the rebuild dialog can rebuild one service at a time. The operation dialog's status moved into a compact header indicator since the streamed output now carries the detail.

Also folds in a dark-mode fix (separate commit): tinted surfaces (modals, badges, status panels) stayed light in dark mode because the status colour tints were never adapted for the dark theme, and the split action menu fell back to a light hover. The dark theme now remaps the tint and on-tint text shades, fixing every tinted surface at once.

nfebe added 2 commits June 29, 2026 16:10
Starting, stopping, restarting and rebuilding a deployment or a single
service showed only a spinner until the request finished, and the outcome
vanished on reload. The dashboard now enqueues the action, streams its
compose output live, and falls back to polling if the socket drops. The
job id is remembered so reopening the page resumes an action still in
flight and shows the final result.

Single-service actions render their progress inline under the service,
and the rebuild dialog can rebuild one service at a time. The operation
dialog's status moves into a compact header indicator since the streamed
output now carries the detail.
Tinted surfaces (modals, badges, status panels) stayed light in dark mode
because the status colour tints were never adapted for the dark theme, and
the split action menu fell back to a light hover colour. The dark theme now
remaps the tint and on-tint text shades so every tinted surface reads
correctly, and the menu hover uses a themed surface.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying flatrun-ui with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1495208
Status: ✅  Deploy successful!
Preview URL: https://d075a7de.flatrun-ui.pages.dev
Branch Preview URL: https://feat-150-streamed-deployment.flatrun-ui.pages.dev

View logs

@sourceant

sourceant Bot commented Jun 29, 2026

Copy link
Copy Markdown

Code Review Summary

The PR successfully implements real-time progress streaming for deployment and service-level actions using WebSockets with a graceful polling fallback. It also includes critical dark-theme accessibility fixes.

🚀 Key Improvements

  • Streamed log output in both global modals and inline service views.
  • Persistence of job IDs in localStorage allowing progress to resume across page reloads.
  • Dark mode color palette adjustments for status tints (-50 to -700 shades).

💡 Minor Suggestions

  • Refactor shared WebSocket/Polling logic between the two new composables.
  • Improve error handling for WebSocket connection failures via onerror.

@sourceant sourceant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review complete. See the overview comment for a summary.

let authed = false;
const token = localStorage.getItem("auth_token");

try {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initializing the WebSocket inside a try-catch block is good, but many browsers do not throw on a malformed URL; they fail asynchronously via the onerror or onclose handlers. It would be safer to explicitly handle socket.onerror to trigger the fallback.

Suggested change
try {
try {
socket = new WebSocket(deploymentJobWsUrl(state.deploymentName, jobId));
socket.onerror = () => {
if (!authed) {
socket?.close();
pollUntilDone(jobId);
}
};
} catch {

fetchDeployments();
onMounted(async () => {
await fetchDeployments();
for (const d of deployments.value) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation resumes the first job it finds and stops checking others. While the UI only supports one global operation modal at a time, this logic assumes only one job across all deployments could be active in the user's session. This is likely correct for the current UI, but break makes it impossible to detect other background tasks if the UI ever supports multi-tasking.

Suggested change
for (const d of deployments.value) {
for (const d of deployments.value) {
await deploymentJob.resume(d.name);
}

@nfebe nfebe merged commit 4081059 into main Jun 29, 2026
5 checks passed
@nfebe nfebe deleted the feat/150/streamed-deployment-actions branch June 29, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant