Commit b857e4c4 authored by Kantz's avatar Kantz
Browse files

Enter sendet nun Nachricht und Modus help wird angezeigt

parent 1a2a006d
...@@ -46,7 +46,7 @@ export default function MessageInput({ ...@@ -46,7 +46,7 @@ export default function MessageInput({
value={value} value={value}
onChange={(event) => onChange(event.target.value)} onChange={(event) => onChange(event.target.value)}
onKeyDown={(event) => { onKeyDown={(event) => {
if (event.key === "Enter" && event.ctrlKey) { if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault(); event.preventDefault();
onSend(); onSend();
} }
......
import { useId } from "react";
import type { OrchestratorName } from "../../api/orchestratorApi"; import type { OrchestratorName } from "../../api/orchestratorApi";
import { t } from "../../i18n"; import { t } from "../../i18n";
...@@ -8,23 +9,83 @@ type OrchestratorSelectProps = { ...@@ -8,23 +9,83 @@ type OrchestratorSelectProps = {
disabled?: boolean; disabled?: boolean;
}; };
const labelMap: Record<OrchestratorName, string> = { type ModeMeta = {
qa: "QA", labelKey:
tutor: "Tutor", | "orchestratorModeQaLabel"
task: "Task", | "orchestratorModeTutorLabel"
| "orchestratorModeTaskLabel";
descriptionKey:
| "orchestratorModeQaDescription"
| "orchestratorModeTutorDescription"
| "orchestratorModeTaskDescription";
}; };
const modeMetaMap: Record<OrchestratorName, ModeMeta> = {
qa: {
labelKey: "orchestratorModeQaLabel",
descriptionKey: "orchestratorModeQaDescription",
},
tutor: {
labelKey: "orchestratorModeTutorLabel",
descriptionKey: "orchestratorModeTutorDescription",
},
task: {
labelKey: "orchestratorModeTaskLabel",
descriptionKey: "orchestratorModeTaskDescription",
},
};
type ModeDescriptionDisplay = "tooltip" | "helperText" | "optionSuffix";
// Variants kept local for easy switching without touching page-level integrations.
const MODE_DESCRIPTION_DISPLAY: ModeDescriptionDisplay = "tooltip";
export default function OrchestratorSelect({ export default function OrchestratorSelect({
value, value,
options, options,
onChange, onChange,
disabled = false, disabled = false,
}: OrchestratorSelectProps) { }: OrchestratorSelectProps) {
const selectId = useId();
const helpPopoverId = useId();
const selectedModeMeta = modeMetaMap[value];
const renderTooltip = MODE_DESCRIPTION_DISPLAY === "tooltip";
const renderHelperText = MODE_DESCRIPTION_DISPLAY === "helperText";
const renderOptionSuffix = MODE_DESCRIPTION_DISPLAY === "optionSuffix";
return ( return (
<label className="orchestrator-select-wrap" htmlFor="orchestrator-select"> <div className="orchestrator-select-wrap">
<span className="orchestrator-select-label">{t("orchestratorMode")}</span> <div className="orchestrator-select-label-row">
<label className="orchestrator-select-label" htmlFor={selectId}>
{t("orchestratorMode")}
</label>
{renderTooltip ? (
<div className="orchestrator-help">
<button
type="button"
className="orchestrator-help-trigger"
aria-label={t("orchestratorModeDescriptionTitle")}
aria-describedby={helpPopoverId}
>
i
</button>
<div id={helpPopoverId} role="tooltip" className="orchestrator-help-popover">
<div className="orchestrator-help-title">{t("orchestratorModeDescriptionTitle")}</div>
<div className="orchestrator-help-list">
{options.map((option) => (
<div key={option} className="orchestrator-help-item">
<span className="orchestrator-help-mode">{t(modeMetaMap[option].labelKey)}:</span>
<span>{t(modeMetaMap[option].descriptionKey)}</span>
</div>
))}
</div>
</div>
</div>
) : null}
</div>
<select <select
id="orchestrator-select" id={selectId}
className="task-select orchestrator-select" className="task-select orchestrator-select"
value={value} value={value}
onChange={(event) => onChange(event.target.value as OrchestratorName)} onChange={(event) => onChange(event.target.value as OrchestratorName)}
...@@ -32,10 +93,15 @@ export default function OrchestratorSelect({ ...@@ -32,10 +93,15 @@ export default function OrchestratorSelect({
> >
{options.map((option) => ( {options.map((option) => (
<option key={option} value={option}> <option key={option} value={option}>
{labelMap[option]} {renderOptionSuffix
? `${t(modeMetaMap[option].labelKey)} - ${t(modeMetaMap[option].descriptionKey)}`
: t(modeMetaMap[option].labelKey)}
</option> </option>
))} ))}
</select> </select>
</label> {renderHelperText ? (
<div className="orchestrator-mode-helper">{t(selectedModeMeta.descriptionKey)}</div>
) : null}
</div>
); );
} }
...@@ -17,6 +17,13 @@ ...@@ -17,6 +17,13 @@
newChat: "New Chat", newChat: "New Chat",
loading: "Loading...", loading: "Loading...",
orchestratorMode: "Mode", orchestratorMode: "Mode",
orchestratorModeQaLabel: "QA",
orchestratorModeTutorLabel: "Tutor",
orchestratorModeTaskLabel: "Task",
orchestratorModeDescriptionTitle: "Mode help",
orchestratorModeQaDescription: "Direct answers based on the script",
orchestratorModeTutorDescription: "Help with your own questions",
orchestratorModeTaskDescription: "Help with textbook exercises",
directChildren: "Direct children", directChildren: "Direct children",
taskChildren: "Task sources", taskChildren: "Task sources",
indirectChildren: "Indirect children", indirectChildren: "Indirect children",
...@@ -75,6 +82,13 @@ ...@@ -75,6 +82,13 @@
newChat: "neuer Chat", newChat: "neuer Chat",
loading: "Lade...", loading: "Lade...",
orchestratorMode: "Modus", orchestratorMode: "Modus",
orchestratorModeQaLabel: "QA",
orchestratorModeTutorLabel: "Tutor",
orchestratorModeTaskLabel: "Task",
orchestratorModeDescriptionTitle: "Modus-Hilfe",
orchestratorModeQaDescription: "Direkte Antworten basierend auf dem Skript",
orchestratorModeTutorDescription: "Hilfe bei selbst gestellten Fragen",
orchestratorModeTaskDescription: "Hilfe bei Aufgaben aus dem Lehrwerk",
directChildren: "Direkte Quellen", directChildren: "Direkte Quellen",
taskChildren: "Aufgaben-Quellen", taskChildren: "Aufgaben-Quellen",
indirectChildren: "Indirekte Quellen", indirectChildren: "Indirekte Quellen",
......
...@@ -67,6 +67,7 @@ body { ...@@ -67,6 +67,7 @@ body {
.orchestrator-select-wrap { .orchestrator-select-wrap {
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap;
gap: 8px; gap: 8px;
padding: 4px 8px; padding: 4px 8px;
border: 1px solid #d8d1c4; border: 1px solid #d8d1c4;
...@@ -74,6 +75,12 @@ body { ...@@ -74,6 +75,12 @@ body {
background: #fef9f0; background: #fef9f0;
} }
.orchestrator-select-label-row {
display: flex;
align-items: center;
gap: 6px;
}
.orchestrator-select-label { .orchestrator-select-label {
font-size: 12px; font-size: 12px;
color: #6f675d; color: #6f675d;
...@@ -84,6 +91,83 @@ body { ...@@ -84,6 +91,83 @@ body {
padding: 4px 8px; padding: 4px 8px;
} }
.orchestrator-help {
position: relative;
}
.orchestrator-help-trigger {
width: 18px;
height: 18px;
border-radius: 999px;
border: 1px solid #b9b2a4;
background: #fff;
color: #6f675d;
font-size: 11px;
font-weight: 600;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: help;
}
.orchestrator-help-trigger:focus-visible {
outline: 2px solid #6f675d;
outline-offset: 1px;
}
.orchestrator-help-popover {
position: absolute;
top: calc(100% + 8px);
left: 0;
width: 320px;
padding: 10px;
border: 1px solid #d8d1c4;
border-radius: 12px;
background: #fff;
box-shadow: 0 10px 26px rgba(29, 27, 22, 0.15);
color: #1d1b16;
font-size: 12px;
z-index: 40;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 0.15s ease;
}
.orchestrator-help:hover .orchestrator-help-popover,
.orchestrator-help:focus-within .orchestrator-help-popover {
opacity: 1;
visibility: visible;
}
.orchestrator-help-title {
font-weight: 600;
margin-bottom: 6px;
}
.orchestrator-help-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.orchestrator-help-item {
display: flex;
gap: 6px;
}
.orchestrator-help-mode {
font-weight: 600;
min-width: 42px;
}
.orchestrator-mode-helper {
flex-basis: 100%;
font-size: 12px;
color: #6f675d;
}
.pill { .pill {
padding: 6px 12px; padding: 6px 12px;
border-radius: 999px; border-radius: 999px;
...@@ -582,6 +666,13 @@ body { ...@@ -582,6 +666,13 @@ body {
width: 100%; width: 100%;
} }
.orchestrator-help-popover {
left: auto;
right: 0;
width: auto;
max-width: min(92vw, 360px);
}
.chat-window { .chat-window {
min-height: 360px; min-height: 360px;
} }
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment