feat(admin): Einstellungsseite überarbeitet, auch für das Handy
Verbindung: Probleme vor der Anleitung. Tools: Suche, Filter, Alle ein-/ausklappen, Zurücksetzen je Gruppe. Divi-Reiter in Klartext mit Statussymbolen. Rollenhinweise, Hilfe-Tab, Reiter als Raster und Karten auf dem Handy, Kontrast, Tippflächen, einheitliche Du-Anrede. Bestätigungen über data-lit-mcp-confirm statt Inline-onclick. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
165e61d7fe
commit
96fa58188b
+72
-12
@@ -87,6 +87,7 @@ final class ToolsTab implements AdminTab
|
||||
echo '<form method="post" action="options.php">';
|
||||
settings_fields(self::GROUP);
|
||||
submit_button(null, 'primary', 'submit-top');
|
||||
$this->renderToolbar();
|
||||
// The admin script mirrors a group's mode onto its rows before saving; it needs the label pattern.
|
||||
printf(
|
||||
'<div class="lit-mcp-groups" data-like-group="%s">',
|
||||
@@ -105,10 +106,11 @@ final class ToolsTab implements AdminTab
|
||||
private function renderIntro(): void
|
||||
{
|
||||
echo '<p class="description">'
|
||||
. esc_html__('Lege fest, welche Tools der KI-Assistent nutzen kann.', 'wp-lit-mcp') . ' '
|
||||
. esc_html__('Änderungen gelten ab der nächsten Verbindung.', 'wp-lit-mcp') . ' '
|
||||
. esc_html__('Im KI-Assistenten die Verbindung dafür gegebenenfalls neu herstellen.', 'wp-lit-mcp')
|
||||
. '</p>';
|
||||
. esc_html__('Lege fest, welche Tools der KI-Assistent nutzen kann.', 'wp-lit-mcp') . '</p>';
|
||||
echo '<div class="notice notice-info inline"><p><strong>'
|
||||
. esc_html__('Änderungen gelten ab der nächsten Verbindung.', 'wp-lit-mcp') . '</strong> '
|
||||
. esc_html__('Stelle die Verbindung im KI-Assistenten danach neu her.', 'wp-lit-mcp')
|
||||
. '</p></div>';
|
||||
|
||||
echo '<dl class="lit-mcp-legend">';
|
||||
$legend = [
|
||||
@@ -123,6 +125,26 @@ final class ToolsTab implements AdminTab
|
||||
echo '</dl>';
|
||||
}
|
||||
|
||||
/** Search, "only changed" and open/close for the many tools; needs the admin script, so it hides without JS. */
|
||||
private function renderToolbar(): void
|
||||
{
|
||||
printf(
|
||||
'<div class="lit-mcp-toolbar hide-if-no-js">'
|
||||
. '<label class="screen-reader-text" for="lit-mcp-tool-search">%1$s</label>'
|
||||
. '<input type="search" id="lit-mcp-tool-search" class="lit-mcp-tool-search" placeholder="%1$s">'
|
||||
. '<label><input type="checkbox" class="lit-mcp-only-changed"> %2$s</label>'
|
||||
. '<button type="button" class="button lit-mcp-collapse-all">%3$s</button>'
|
||||
. '<button type="button" class="button lit-mcp-expand-all">%4$s</button>'
|
||||
. '<span class="lit-mcp-filter-status" role="status"></span></div>',
|
||||
esc_attr__('Tool suchen', 'wp-lit-mcp'),
|
||||
esc_html__('Nur Tools mit eigener Einstellung', 'wp-lit-mcp'),
|
||||
esc_html__('Alle einklappen', 'wp-lit-mcp'),
|
||||
esc_html__('Alle aufklappen', 'wp-lit-mcp')
|
||||
);
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped in kindLegend().
|
||||
echo '<p class="description lit-mcp-kind-legend">' . $this->kindLegend() . '</p>';
|
||||
}
|
||||
|
||||
/** Only when the direct tools get many: then the count is worth a warning, otherwise it is noise. */
|
||||
private function renderCounter(int $direct): void
|
||||
{
|
||||
@@ -196,7 +218,7 @@ final class ToolsTab implements AdminTab
|
||||
)),
|
||||
esc_html($this->summary($modes, $policy->isReadonly($group)))
|
||||
);
|
||||
$this->renderGroupControls($policy, $group, $mixed);
|
||||
// Title, toggle, controls: the same order for the eye and for Tab, so the stylesheet needs no "order".
|
||||
printf(
|
||||
'<div class="handle-actions hide-if-no-js"><button type="button" class="handlediv" aria-expanded="%s">'
|
||||
. '<span class="screen-reader-text">%s</span>'
|
||||
@@ -208,6 +230,7 @@ final class ToolsTab implements AdminTab
|
||||
$label
|
||||
))
|
||||
);
|
||||
$this->renderGroupControls($policy, $group, $label, $mixed);
|
||||
echo '</div><div class="inside">';
|
||||
|
||||
printf(
|
||||
@@ -239,7 +262,7 @@ final class ToolsTab implements AdminTab
|
||||
return $readonly ? __('nur lesen', 'wp-lit-mcp') . ' · ' . $summary : $summary;
|
||||
}
|
||||
|
||||
private function renderGroupControls(ExposurePolicy $policy, string $group, bool $mixed): void
|
||||
private function renderGroupControls(ExposurePolicy $policy, string $group, string $label, bool $mixed): void
|
||||
{
|
||||
$selectId = 'lit-mcp-group-mode-' . self::idPart($group);
|
||||
$groupMode = $policy->groupMode($group);
|
||||
@@ -275,6 +298,15 @@ final class ToolsTab implements AdminTab
|
||||
checked($policy->isReadonly($group), true, false),
|
||||
esc_html__('Nur lesen', 'wp-lit-mcp')
|
||||
);
|
||||
printf(
|
||||
'<button type="button" class="button-link lit-mcp-group-reset hide-if-no-js" aria-label="%s">%s</button>',
|
||||
esc_attr(sprintf(
|
||||
/* translators: %s: group label */
|
||||
__('Auf Standard zurücksetzen: %s', 'wp-lit-mcp'),
|
||||
$label
|
||||
)),
|
||||
esc_html__('Auf Standard zurücksetzen', 'wp-lit-mcp')
|
||||
);
|
||||
if ($mixed) {
|
||||
echo '<span class="description">'
|
||||
. esc_html__('gemischt – einzelne Tools haben eigene Einstellungen', 'wp-lit-mcp')
|
||||
@@ -389,13 +421,12 @@ final class ToolsTab implements AdminTab
|
||||
private function kind(Ability $ability): string
|
||||
{
|
||||
$annotations = $ability->annotations();
|
||||
$styles = self::kindStyles();
|
||||
[$icon, $text] = match (true) {
|
||||
isset(self::DELETING[$ability->name()]) => ['trash', self::DELETING[$ability->name()] === 'trash'
|
||||
? __('Papierkorb', 'wp-lit-mcp')
|
||||
: __('löscht', 'wp-lit-mcp')],
|
||||
($annotations['destructive'] ?? false) === true => ['update', __('überschreibt', 'wp-lit-mcp')],
|
||||
($annotations['readonly'] ?? false) === true => ['visibility', __('liest', 'wp-lit-mcp')],
|
||||
default => ['edit', __('schreibt', 'wp-lit-mcp')],
|
||||
isset(self::DELETING[$ability->name()]) => $styles[self::DELETING[$ability->name()]],
|
||||
($annotations['destructive'] ?? false) === true => $styles['overwrite'],
|
||||
($annotations['readonly'] ?? false) === true => $styles['read'],
|
||||
default => $styles['write'],
|
||||
};
|
||||
|
||||
return sprintf(
|
||||
@@ -406,6 +437,35 @@ final class ToolsTab implements AdminTab
|
||||
);
|
||||
}
|
||||
|
||||
/** @return array<string, array{string, string}> Kind → [dashicon, word]. */
|
||||
private static function kindStyles(): array
|
||||
{
|
||||
return [
|
||||
'read' => ['visibility', __('liest', 'wp-lit-mcp')],
|
||||
'write' => ['edit', __('schreibt', 'wp-lit-mcp')],
|
||||
'overwrite' => ['update', __('überschreibt', 'wp-lit-mcp')],
|
||||
'delete' => ['trash', __('löscht', 'wp-lit-mcp')],
|
||||
'trash' => ['trash', __('Papierkorb', 'wp-lit-mcp')],
|
||||
];
|
||||
}
|
||||
|
||||
/** On phones the "Art" column shows icons only; this line says what they mean (touch has no tooltips). */
|
||||
private function kindLegend(): string
|
||||
{
|
||||
$styles = self::kindStyles();
|
||||
$parts = [];
|
||||
foreach (['read', 'write', 'overwrite', 'delete'] as $key) {
|
||||
[$icon, $text] = $styles[$key];
|
||||
$parts[] = sprintf(
|
||||
'<span class="dashicons dashicons-%s" aria-hidden="true"></span> %s',
|
||||
esc_attr($icon),
|
||||
esc_html($key === 'delete' ? $text . ' / ' . $styles['trash'][1] : $text)
|
||||
);
|
||||
}
|
||||
|
||||
return implode(' · ', $parts);
|
||||
}
|
||||
|
||||
/** Safe fragment for an HTML id from a group id or tool name ("lit/guide" → "lit-guide"). */
|
||||
private static function idPart(string $value): string
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user