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
+9 -6
View File
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
import { Check, Copy, TriangleAlert } from "lucide-react";
import { highlightSql } from "./highlight";
import { statusBadgeClass } from "./statusBadge";
import { useI18n } from "../i18n";
export interface SqlBlock {
name: string;
@@ -20,6 +21,7 @@ const STATUS_LABELS: Record<string, string> = {
};
function Block({ block }: { block: SqlBlock }) {
const { t } = useI18n();
const [open, setOpen] = useState(true);
const [html, setHtml] = useState<string>("");
const [copyState, setCopyState] = useState<"idle" | "copied" | "failed">("idle");
@@ -61,21 +63,21 @@ function Block({ block }: { block: SqlBlock }) {
<span className="truncate font-semibold">{block.name}</span>
{block.fields !== undefined && (
<span className="shrink-0 text-xs text-muted-foreground">
{block.fields} campi
{t(block.fields === 1 ? "{count} field" : "{count} fields", { count: block.fields })}
</span>
)}
{badge && (
<span
className={`ml-auto shrink-0 rounded-md px-1.5 py-0.5 text-xs ${badgeClass}`}
>
{badge}
{t(badge)}
</span>
)}
</button>
<button
type="button"
aria-label={`Copy SQL for ${block.name}`}
title="Copy SQL"
aria-label={t("Copy SQL for {name}", { name: block.name })}
title={t("Copy SQL")}
onClick={copySql}
className="flex shrink-0 items-center gap-1.5 border-l border-border/70 px-2.5 text-xs text-muted-foreground transition-colors hover:bg-muted/40 hover:text-foreground"
>
@@ -87,7 +89,7 @@ function Block({ block }: { block: SqlBlock }) {
<Copy className="size-3.5" aria-hidden="true" />
)}
<span role="status" aria-live="polite">
{copyState === "copied" ? "Copied" : copyState === "failed" ? "Copy failed" : ""}
{copyState === "copied" ? t("Copied") : copyState === "failed" ? t("Copy failed") : ""}
</span>
</button>
</div>
@@ -117,6 +119,7 @@ function Block({ block }: { block: SqlBlock }) {
}
export function SqlViewer({ blocks }: { blocks: SqlBlock[] }) {
const { t } = useI18n();
const [layout, setLayout] = useState<Layout>("vertical");
return (
@@ -129,7 +132,7 @@ export function SqlViewer({ blocks }: { blocks: SqlBlock[] }) {
setLayout((l) => (l === "vertical" ? "horizontal" : "vertical"))
}
>
{layout === "vertical" ? "Horizontal" : "Vertical"}
{t(layout === "vertical" ? "Horizontal" : "Vertical")}
</button>
</div>
)}