Add full shell, replaceable Omics adapter and bilingual interaction

Implement approved specification #32 and tickets #33-#37. Keep host authentication server-verified and pin session interaction language. Compile scoped base selectors for browser compatibility and retain full gutters during CSS pruning.
This commit is contained in:
Codex
2026-09-13 14:26:39 +02:00
parent 2d1b714ebe
commit d8a29bfbdd
207 changed files with 8570 additions and 2164 deletions
@@ -1,3 +1,4 @@
import { useI18n } from "../../i18n";
import {
useEffect,
useId,
@@ -50,6 +51,7 @@ export function FleetActionSelector<ActionId extends string>({
renderContext,
label = "Batch action",
}: FleetActionSelectorProps<ActionId>) {
const { t } = useI18n();
const [selectedActionId, setSelectedActionId] = useState<ActionId | "">("");
const hintId = useId();
const groups = useMemo(() => {
@@ -63,10 +65,10 @@ export function FleetActionSelector<ActionId extends string>({
grouped.get(action.group)!.push(action);
}
return order.map((group) => ({ group, actions: grouped.get(group)! }));
}, [actions]);
}, [actions, t]);
const selectedAction = actions.find((action) => action.id === selectedActionId);
const unavailableReason = selectedAction?.disabled
? selectedAction.disabledReason ?? "This action is not available for the current selection."
? selectedAction.disabledReason ?? t("This action is not available for the current selection.")
: null;
const actionScope = selectedAction?.scopeLabel;
@@ -85,33 +87,33 @@ export function FleetActionSelector<ActionId extends string>({
return (
<div className="thot-fleet-action-selector">
<div className="thot-fleet-action-selector__scope" aria-live="polite">
<strong>{actionScope ?? `${selectedCount} selected`}</strong>
<strong>{actionScope ? t(actionScope) : t("{count} selected", { count: selectedCount })}</strong>
{actionScope ? (
<span>Action scope</span>
<span>{t("Action scope")}</span>
) : hiddenSelectedCount > 0 ? (
<span>{hiddenSelectedCount} hidden by filter</span>
<span>{hiddenSelectedCount} {t("hidden by filter")}</span>
) : (
<span>Selected rows</span>
<span>{t("Selected rows")}</span>
)}
</div>
<label className="sr-only" htmlFor={hintId}>{label}</label>
<label className="sr-only" htmlFor={hintId}>{t(label)}</label>
<select
ref={selectRef}
id={hintId}
className="thot-fleet-action-selector__select"
aria-label={label}
aria-label={t(label)}
value={selectedActionId}
disabled={busy || selectedCount === 0}
onChange={(event) => setSelectedActionId(event.target.value as ActionId | "")}
>
<option value="">Choose an action…</option>
<option value="">{t("Choose an action…")}</option>
{groups.map(({ group, actions: groupActions }) => (
<optgroup key={group} label={group}>
<optgroup key={group} label={t(group)}>
{groupActions.map((action) => (
<option key={action.id} value={action.id}>
{action.label}
{t(action.label)}
{action.disabled && action.disabledReason
? `, unavailable: ${action.disabledReason}`
? t(", unavailable: {reason}", { reason: t(action.disabledReason) })
: ""}
</option>
))}
@@ -129,14 +131,12 @@ export function FleetActionSelector<ActionId extends string>({
onClick={() => void run()}
>
<Play aria-hidden="true" />
Run action
</Button>
{t("Run action")}</Button>
<Button type="button" size="sm" variant="ghost" disabled={busy} onClick={onClear}>
<X aria-hidden="true" /> Clear
</Button>
<X aria-hidden="true" /> {t("Clear")}</Button>
{unavailableReason ? (
<span id={`${hintId}-reason`} className="thot-fleet-action-selector__reason" role="status">
{unavailableReason}
{t(unavailableReason)}
</span>
) : null}
</div>