mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-19 14:00:51 +00:00
Config UI: click-to-reveal redacted env vars and use lightweight re-render (#49399)
* Refactor CSS styles: replace hardcoded colors with CSS variables for accent colors and optimize spacing rules in layout files. * Update CSS styles: streamline selectors, enhance hover effects, and adjust focus states for chat components and layout elements. * Enhance focus styles for chat components: update border colors and box-shadow effects for improved accessibility and visual consistency. * Config UI: click-to-reveal redacted env vars and use lightweight re-render
This commit is contained in:
@@ -962,6 +962,13 @@
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Redacted (click-to-reveal) */
|
||||
.cfg-input--redacted,
|
||||
.cfg-textarea--redacted {
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Number Input */
|
||||
.cfg-number {
|
||||
display: inline-flex;
|
||||
|
||||
@@ -1512,6 +1512,7 @@ export function renderApp(state: AppViewState) {
|
||||
onRawChange: (next) => {
|
||||
state.configRaw = next;
|
||||
},
|
||||
onRequestUpdate: requestHostUpdate,
|
||||
onFormModeChange: (mode) => (state.configFormMode = mode),
|
||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||
onSearchChange: (query) => (state.configSearchQuery = query),
|
||||
@@ -1582,6 +1583,7 @@ export function renderApp(state: AppViewState) {
|
||||
onRawChange: (next) => {
|
||||
state.configRaw = next;
|
||||
},
|
||||
onRequestUpdate: requestHostUpdate,
|
||||
onFormModeChange: (mode) => (state.communicationsFormMode = mode),
|
||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||
onSearchChange: (query) => (state.communicationsSearchQuery = query),
|
||||
@@ -1646,6 +1648,7 @@ export function renderApp(state: AppViewState) {
|
||||
onRawChange: (next) => {
|
||||
state.configRaw = next;
|
||||
},
|
||||
onRequestUpdate: requestHostUpdate,
|
||||
onFormModeChange: (mode) => (state.appearanceFormMode = mode),
|
||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||
onSearchChange: (query) => (state.appearanceSearchQuery = query),
|
||||
@@ -1710,6 +1713,7 @@ export function renderApp(state: AppViewState) {
|
||||
onRawChange: (next) => {
|
||||
state.configRaw = next;
|
||||
},
|
||||
onRequestUpdate: requestHostUpdate,
|
||||
onFormModeChange: (mode) => (state.automationFormMode = mode),
|
||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||
onSearchChange: (query) => (state.automationSearchQuery = query),
|
||||
@@ -1774,6 +1778,7 @@ export function renderApp(state: AppViewState) {
|
||||
onRawChange: (next) => {
|
||||
state.configRaw = next;
|
||||
},
|
||||
onRequestUpdate: requestHostUpdate,
|
||||
onFormModeChange: (mode) => (state.infrastructureFormMode = mode),
|
||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||
onSearchChange: (query) => (state.infrastructureSearchQuery = query),
|
||||
@@ -1838,6 +1843,7 @@ export function renderApp(state: AppViewState) {
|
||||
onRawChange: (next) => {
|
||||
state.configRaw = next;
|
||||
},
|
||||
onRequestUpdate: requestHostUpdate,
|
||||
onFormModeChange: (mode) => (state.aiAgentsFormMode = mode),
|
||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||
onSearchChange: (query) => (state.aiAgentsSearchQuery = query),
|
||||
|
||||
@@ -646,7 +646,6 @@ function renderTextInput(params: {
|
||||
// oxlint-disable typescript/no-base-to-string
|
||||
(schema.default !== undefined ? `Default: ${String(schema.default)}` : ""));
|
||||
const displayValue = sensitiveState.isRedacted ? "" : (value ?? "");
|
||||
const effectiveDisabled = disabled || sensitiveState.isRedacted;
|
||||
const effectiveInputType =
|
||||
sensitiveState.isSensitive && !sensitiveState.isRedacted ? "text" : inputType;
|
||||
|
||||
@@ -658,11 +657,16 @@ function renderTextInput(params: {
|
||||
<div class="cfg-input-wrap">
|
||||
<input
|
||||
type=${effectiveInputType}
|
||||
class="cfg-input"
|
||||
class="cfg-input${sensitiveState.isRedacted ? " cfg-input--redacted" : ""}"
|
||||
placeholder=${placeholder}
|
||||
.value=${displayValue == null ? "" : String(displayValue)}
|
||||
?disabled=${effectiveDisabled}
|
||||
?disabled=${disabled}
|
||||
?readonly=${sensitiveState.isRedacted}
|
||||
@click=${() => {
|
||||
if (sensitiveState.isRedacted && params.onToggleSensitivePath) {
|
||||
params.onToggleSensitivePath(path);
|
||||
}
|
||||
}}
|
||||
@input=${(e: Event) => {
|
||||
if (sensitiveState.isRedacted) {
|
||||
return;
|
||||
@@ -700,7 +704,7 @@ function renderTextInput(params: {
|
||||
type="button"
|
||||
class="cfg-input__reset"
|
||||
title="Reset to default"
|
||||
?disabled=${effectiveDisabled}
|
||||
?disabled=${disabled || sensitiveState.isRedacted}
|
||||
@click=${() => onPatch(path, schema.default)}
|
||||
>↺</button>
|
||||
`
|
||||
@@ -830,7 +834,6 @@ function renderJsonTextarea(params: {
|
||||
isSensitivePathRevealed: params.isSensitivePathRevealed,
|
||||
});
|
||||
const displayValue = sensitiveState.isRedacted ? "" : fallback;
|
||||
const effectiveDisabled = disabled || sensitiveState.isRedacted;
|
||||
|
||||
return html`
|
||||
<div class="cfg-field">
|
||||
@@ -839,12 +842,17 @@ function renderJsonTextarea(params: {
|
||||
${renderTags(tags)}
|
||||
<div class="cfg-input-wrap">
|
||||
<textarea
|
||||
class="cfg-textarea"
|
||||
class="cfg-textarea${sensitiveState.isRedacted ? " cfg-textarea--redacted" : ""}"
|
||||
placeholder=${sensitiveState.isRedacted ? REDACTED_PLACEHOLDER : "JSON value"}
|
||||
rows="3"
|
||||
.value=${displayValue}
|
||||
?disabled=${effectiveDisabled}
|
||||
?disabled=${disabled}
|
||||
?readonly=${sensitiveState.isRedacted}
|
||||
@click=${() => {
|
||||
if (sensitiveState.isRedacted && params.onToggleSensitivePath) {
|
||||
params.onToggleSensitivePath(path);
|
||||
}
|
||||
}}
|
||||
@change=${(e: Event) => {
|
||||
if (sensitiveState.isRedacted) {
|
||||
return;
|
||||
@@ -1253,14 +1261,19 @@ function renderMapField(params: {
|
||||
? html`
|
||||
<div class="cfg-input-wrap">
|
||||
<textarea
|
||||
class="cfg-textarea cfg-textarea--sm"
|
||||
class="cfg-textarea cfg-textarea--sm${sensitiveState.isRedacted ? " cfg-textarea--redacted" : ""}"
|
||||
placeholder=${
|
||||
sensitiveState.isRedacted ? REDACTED_PLACEHOLDER : "JSON value"
|
||||
}
|
||||
rows="2"
|
||||
.value=${sensitiveState.isRedacted ? "" : fallback}
|
||||
?disabled=${disabled || sensitiveState.isRedacted}
|
||||
?disabled=${disabled}
|
||||
?readonly=${sensitiveState.isRedacted}
|
||||
@click=${() => {
|
||||
if (sensitiveState.isRedacted && onToggleSensitivePath) {
|
||||
onToggleSensitivePath(valuePath);
|
||||
}
|
||||
}}
|
||||
@change=${(e: Event) => {
|
||||
if (sensitiveState.isRedacted) {
|
||||
return;
|
||||
|
||||
@@ -56,6 +56,7 @@ export type ConfigProps = {
|
||||
includeSections?: string[];
|
||||
excludeSections?: string[];
|
||||
includeVirtualSections?: boolean;
|
||||
onRequestUpdate?: () => void;
|
||||
};
|
||||
|
||||
// SVG Icons for sidebar (Lucide-style)
|
||||
@@ -672,6 +673,7 @@ export function renderConfig(props: ConfigProps) {
|
||||
const formUnsafe = analysis.schema ? analysis.unsupportedPaths.length > 0 : false;
|
||||
const formMode = showModeToggle ? props.formMode : "form";
|
||||
const envSensitiveVisible = cvs.envRevealed;
|
||||
const requestUpdate = props.onRequestUpdate ?? (() => props.onRawChange(props.raw));
|
||||
|
||||
// Build categorised nav from schema - only include sections that exist in the schema
|
||||
const schemaProps = analysis.schema?.properties ?? {};
|
||||
@@ -905,7 +907,7 @@ export function renderConfig(props: ConfigProps) {
|
||||
class="btn btn--sm"
|
||||
@click=${() => {
|
||||
cvs.validityDismissed = true;
|
||||
props.onRawChange(props.raw);
|
||||
requestUpdate();
|
||||
}}
|
||||
>Don't remind again</button>
|
||||
</div>
|
||||
@@ -982,7 +984,7 @@ export function renderConfig(props: ConfigProps) {
|
||||
title=${envSensitiveVisible ? "Hide env values" : "Reveal env values"}
|
||||
@click=${() => {
|
||||
cvs.envRevealed = !cvs.envRevealed;
|
||||
props.onRawChange(props.raw);
|
||||
requestUpdate();
|
||||
}}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="16" height="16">
|
||||
@@ -1031,7 +1033,7 @@ export function renderConfig(props: ConfigProps) {
|
||||
isSensitivePathRevealed,
|
||||
onToggleSensitivePath: (path) => {
|
||||
toggleSensitivePathReveal(path);
|
||||
props.onRawChange(props.raw);
|
||||
requestUpdate();
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1071,7 +1073,7 @@ export function renderConfig(props: ConfigProps) {
|
||||
aria-pressed=${!blurred}
|
||||
@click=${() => {
|
||||
cvs.rawRevealed = !cvs.rawRevealed;
|
||||
props.onRawChange(props.raw);
|
||||
requestUpdate();
|
||||
}}
|
||||
>
|
||||
${blurred ? icons.eyeOff : icons.eye}
|
||||
|
||||
Reference in New Issue
Block a user