← Files Biological Sequence & Alignment ViewerARCHIVED FILE

src/ui/workbench-feedback.ts

1.98 KB · Sep 30, 2026 · 23:01 UTC

↓ Download file

import { useCallback, useContext, useLayoutEffect, useRef } from "react";

import type { SequenceViewerCommandResult } from "../viewer-commands";
import type { WorkbenchFeedbackSnapshot } from "./workbench-tool-controller";
import { WorkbenchToolControllerContext } from "./workbench-tools";

/** Register only harmless feedback dismissal, never approval or overwrite actions. */
export function useWorkbenchFeedback({
  id,
  kind,
  label,
  message,
  onDismiss,
  visible,
}: {
  id: `${"alignment" | "sequence"}.${string}`;
  kind: WorkbenchFeedbackSnapshot["kind"];
  label: string;
  message?: string;
  onDismiss: () => void;
  visible: boolean;
}): () => SequenceViewerCommandResult {
  const controller = useContext(WorkbenchToolControllerContext);
  const stateRef = useRef({ id, kind, label, message, onDismiss, visible });
  stateRef.current = { id, kind, label, message, onDismiss, visible };

  const getSnapshot = useCallback((): WorkbenchFeedbackSnapshot => {
    const current = stateRef.current;
    return {
      id: current.id,
      kind: current.kind,
      label: current.label,
      message: current.message?.slice(0, 1_000) ?? null,
      visible: current.visible,
    };
  }, []);
  const dismiss = useCallback((): SequenceViewerCommandResult => {
    const snapshot = getSnapshot();
    if (snapshot.visible) {
      stateRef.current.onDismiss();
      stateRef.current = {
        ...stateRef.current,
        message: undefined,
        visible: false,
      };
    }
    return {
      applied: true,
      message: snapshot.visible
        ? `Dismissed ${snapshot.label}.`
        : `${snapshot.label} is already dismissed.`,
      state: { feedback: { ...snapshot, message: null, visible: false } },
    };
  }, [getSnapshot]);

  useLayoutEffect(
    () => controller?.registerFeedback(id, { dismiss, getSnapshot }),
    [controller, dismiss, getSnapshot, id],
  );
  useLayoutEffect(() => {
    controller?.notifyChanged();
  }, [controller, kind, label, message, visible]);
  return dismiss;
}

SHA-256: f155586db355fe3c665d4ab67dd5e6c14593695faeecd30bf67fb6df75de50e1