Modul-Seiten: alles zu einer Funktion an einem Ort
Deploy / check (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

Bisher war eine Funktion über vier Bereiche verteilt: Schalter unter Module,
Kanal unter Support, Texte unter Texte, Werte unter Werte. Wer die
Begrüßung ändern wollte, brauchte drei Bereiche.

Jetzt öffnet ein Klick auf die Karte die Seite dieser Funktion — mit
Schalter, ihren Kanälen und Feldern, ihren Texten, ihren Werten und dem Weg
zum passenden Werkzeug. Für „Willkommen" also Kanal, Karte, Farben,
Vorschau und beide Texte untereinander.

Bewusst keine 35 Einträge in der Seitenleiste: das Modul-Raster ist die
Navigation, wie bei MEE6. Die Seitenleiste bleibt so lang wie vorher.

Die Seite wird aus den Registern gebaut, nicht von Hand: modules.js kennt
die Felder (neu: `fields` für alles jenseits der Pflichtkanäle, `tool` für
den Verweis auf einen Editor), templates.js hatte die Zuordnung schon,
tuning.js hat sie bekommen. Eine neue Funktion bekommt ihre Seite dadurch
geschenkt — es gibt keinen Ort, an dem man sie vergessen könnte.

Was ein eigenes Werkzeug hat — Rollen-Menüs, Bewerbungen, Composer,
Server-Monitor —, bleibt in seinem Bereich; die Modul-Seite verlinkt nur
dorthin. Dort ist mehr Platz, und die Editoren lassen sich nicht sinnvoll
aus Registern erzeugen.

Texte und Werte bleiben zusätzlich als flache Übersicht: wer alle Texte am
Stück durchgehen will, soll dafür nicht 27 Seiten öffnen müssen.

Beim Umbau ist mir ein Skript in die Quelldatei gelaufen — der Schreibvorgang
scheiterte an einem Emoji und hinterließ modules.js leer. Wiederhergestellt
aus dem letzten Commit; die Skripte schreiben jetzt erst in eine
Zwischendatei und benennen danach um.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 21:54:31 +02:00
co-authored by Claude Fable 5
parent bea35234c6
commit c88926aba0
7 changed files with 575 additions and 41 deletions
+45 -20
View File
@@ -9,6 +9,7 @@ import {
IconSearch, IconSystem as IconTune,
} from '../icons.jsx';
import { moduleIcon } from '../module-icons.jsx';
import ModulePage from '../components/ModulePage.jsx';
import { findSettings, SETTING_INDEX } from '../setting-index.js';
import { Markdown } from '../markdown.jsx';
@@ -245,10 +246,11 @@ function fmtBytes(b) {
export default function Settings({ me }) {
// Tab steht in der Adresse (#texte), damit Neuladen und Links den Bereich behalten
const [tab, setTabState] = useState(
() => TABS.find((t) => t.id === window.location.hash.slice(1))?.id ?? 'status'
() => TABS.find((t) => t.id === window.location.hash.slice(1).split('/')[0])?.id ?? 'status'
);
const setTab = (id) => {
setTabState(id);
setOpenModuleState(null);
window.history.replaceState(null, '', `#${id}`);
};
const [tabSearch, setTabSearch] = useState('');
@@ -304,6 +306,14 @@ export default function Settings({ me }) {
const [modules, setModules] = useState([]);
const [moduleGroups, setModuleGroups] = useState([]);
const [modFilter, setModFilter] = useState('alle');
// Offene Modul-Seite steht mit in der Adresse: #module/welcome
const [openModule, setOpenModuleState] = useState(
() => window.location.hash.split('/')[1] ?? null
);
const setOpenModule = (id) => {
setOpenModuleState(id);
window.history.replaceState(null, '', id ? `#module/${id}` : '#module');
};
const [creating, setCreating] = useState(null); // Einstellung, die gerade angelegt wird
// Grundgerüst-Assistent
const [plan, setPlan] = useState(null); // Vorschau, null = zu
@@ -459,7 +469,7 @@ export default function Settings({ me }) {
// Zurück-Taste und von Hand geänderte Adressen sollen den Bereich mitwechseln
useEffect(() => {
const onHash = () => {
const id = TABS.find((t) => t.id === window.location.hash.slice(1))?.id;
const id = TABS.find((t) => t.id === window.location.hash.slice(1).split('/')[0])?.id;
if (id) setTabState(id);
};
window.addEventListener('hashchange', onHash);
@@ -2355,7 +2365,21 @@ export default function Settings({ me }) {
];
const groupLabel = (id) => moduleGroups.find((g) => g.id === id)?.label ?? '';
const tabModule = (
const tabModule = openModule ? (
<ModulePage
id={openModule}
onBack={() => setOpenModule(null)}
onToggle={toggleModule}
form={form}
setForm={setForm}
PickSelect={PickSelect}
channelItems={channelItems}
roleItems={roleItems}
welcomePreviewUrl={welcomePreviewUrl}
flash={flash}
goTab={setTab}
/>
) : (
<div className="settings-section">
<h2 className="settings-title">// Module</h2>
<p className="section-intro">
@@ -2387,11 +2411,17 @@ export default function Settings({ me }) {
return (
<div className={`card mod-card${m.enabled ? ' on' : ''}`} key={m.id}>
<div className="mod-head">
<span className="mod-icon"><Icon size={20} /></span>
<span className="mod-titles">
<span className="mod-name">{m.name}</span>
<span className="mod-group">{groupLabel(m.group)}</span>
</span>
<button
className="mod-open"
title={`${m.name} einrichten`}
onClick={() => setOpenModule(m.id)}
>
<span className="mod-icon"><Icon size={20} /></span>
<span className="mod-titles">
<span className="mod-name">{m.name}</span>
<span className="mod-group">{groupLabel(m.group)}</span>
</span>
</button>
<button
className={`mod-switch${m.enabled ? ' on' : ''}`}
title={m.enabled ? 'Ausschalten' : 'Einschalten'}
@@ -2411,16 +2441,9 @@ export default function Settings({ me }) {
<span className="mod-ready with-icon"><IconCheck size={13} /> Einsatzbereit</span>
)}
{!m.enabled && <span className="mod-off">Ausgeschaltet</span>}
{m.tab && (
<button
className="mod-link"
onClick={() => (m.missing.length > 0
? jumpToMissing(m.missing[0], m.tab)
: setTab(m.tab))}
>
Einstellungen
</button>
)}
<button className="mod-link" onClick={() => setOpenModule(m.id)}>
Einrichten
</button>
</div>
</div>
);
@@ -2434,7 +2457,8 @@ export default function Settings({ me }) {
<div className="settings-section">
<h2 className="settings-title">// Texte</h2>
<p className="section-intro">
Alles, was der Bot nach außen schreibt. Zeile anklicken zum Bearbeiten
Alle Texte am Stück wer nur die einer Funktion sucht, findet sie
auch auf deren Modul-Seite. Zeile anklicken zum Bearbeiten
die Bausteine in geschweiften Klammern setzt der Bot beim Senden ein,
ein Klick darauf fügt sie an der Cursor-Position ein. Ein leeres Feld
bedeutet: wieder der Standardtext.
@@ -2541,7 +2565,8 @@ export default function Settings({ me }) {
<div className="settings-section">
<h2 className="settings-title">// Werte</h2>
<p className="section-intro">
Zahlen, die das Verhalten steuern wie viel XP eine Nachricht bringt,
Alle Werte am Stück; jeder steht zusätzlich auf der Seite seiner
Funktion. Zahlen, die das Verhalten steuern wie viel XP eine Nachricht bringt,
wie oft geprüft wird, wann gepostet wird. Geänderte Intervalle greifen ab
dem nächsten Durchlauf, ohne Neustart. Neben jedem Feld steht der erlaubte
Bereich; angepasste Werte lassen sich einzeln zurücksetzen.