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:
parent
206d1be082
commit
53dcafbec3
@ -962,6 +962,13 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Redacted (click-to-reveal) */
|
||||||
|
.cfg-input--redacted,
|
||||||
|
.cfg-textarea--redacted {
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
/* Number Input */
|
/* Number Input */
|
||||||
.cfg-number {
|
.cfg-number {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
@ -1512,6 +1512,7 @@ export function renderApp(state: AppViewState) {
|
|||||||
onRawChange: (next) => {
|
onRawChange: (next) => {
|
||||||
state.configRaw = next;
|
state.configRaw = next;
|
||||||
},
|
},
|
||||||
|
onRequestUpdate: requestHostUpdate,
|
||||||
onFormModeChange: (mode) => (state.configFormMode = mode),
|
onFormModeChange: (mode) => (state.configFormMode = mode),
|
||||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||||
onSearchChange: (query) => (state.configSearchQuery = query),
|
onSearchChange: (query) => (state.configSearchQuery = query),
|
||||||
@ -1582,6 +1583,7 @@ export function renderApp(state: AppViewState) {
|
|||||||
onRawChange: (next) => {
|
onRawChange: (next) => {
|
||||||
state.configRaw = next;
|
state.configRaw = next;
|
||||||
},
|
},
|
||||||
|
onRequestUpdate: requestHostUpdate,
|
||||||
onFormModeChange: (mode) => (state.communicationsFormMode = mode),
|
onFormModeChange: (mode) => (state.communicationsFormMode = mode),
|
||||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||||
onSearchChange: (query) => (state.communicationsSearchQuery = query),
|
onSearchChange: (query) => (state.communicationsSearchQuery = query),
|
||||||
@ -1646,6 +1648,7 @@ export function renderApp(state: AppViewState) {
|
|||||||
onRawChange: (next) => {
|
onRawChange: (next) => {
|
||||||
state.configRaw = next;
|
state.configRaw = next;
|
||||||
},
|
},
|
||||||
|
onRequestUpdate: requestHostUpdate,
|
||||||
onFormModeChange: (mode) => (state.appearanceFormMode = mode),
|
onFormModeChange: (mode) => (state.appearanceFormMode = mode),
|
||||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||||
onSearchChange: (query) => (state.appearanceSearchQuery = query),
|
onSearchChange: (query) => (state.appearanceSearchQuery = query),
|
||||||
@ -1710,6 +1713,7 @@ export function renderApp(state: AppViewState) {
|
|||||||
onRawChange: (next) => {
|
onRawChange: (next) => {
|
||||||
state.configRaw = next;
|
state.configRaw = next;
|
||||||
},
|
},
|
||||||
|
onRequestUpdate: requestHostUpdate,
|
||||||
onFormModeChange: (mode) => (state.automationFormMode = mode),
|
onFormModeChange: (mode) => (state.automationFormMode = mode),
|
||||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||||
onSearchChange: (query) => (state.automationSearchQuery = query),
|
onSearchChange: (query) => (state.automationSearchQuery = query),
|
||||||
@ -1774,6 +1778,7 @@ export function renderApp(state: AppViewState) {
|
|||||||
onRawChange: (next) => {
|
onRawChange: (next) => {
|
||||||
state.configRaw = next;
|
state.configRaw = next;
|
||||||
},
|
},
|
||||||
|
onRequestUpdate: requestHostUpdate,
|
||||||
onFormModeChange: (mode) => (state.infrastructureFormMode = mode),
|
onFormModeChange: (mode) => (state.infrastructureFormMode = mode),
|
||||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||||
onSearchChange: (query) => (state.infrastructureSearchQuery = query),
|
onSearchChange: (query) => (state.infrastructureSearchQuery = query),
|
||||||
@ -1838,6 +1843,7 @@ export function renderApp(state: AppViewState) {
|
|||||||
onRawChange: (next) => {
|
onRawChange: (next) => {
|
||||||
state.configRaw = next;
|
state.configRaw = next;
|
||||||
},
|
},
|
||||||
|
onRequestUpdate: requestHostUpdate,
|
||||||
onFormModeChange: (mode) => (state.aiAgentsFormMode = mode),
|
onFormModeChange: (mode) => (state.aiAgentsFormMode = mode),
|
||||||
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
onFormPatch: (path, value) => updateConfigFormValue(state, path, value),
|
||||||
onSearchChange: (query) => (state.aiAgentsSearchQuery = query),
|
onSearchChange: (query) => (state.aiAgentsSearchQuery = query),
|
||||||
|
|||||||
@ -646,7 +646,6 @@ function renderTextInput(params: {
|
|||||||
// oxlint-disable typescript/no-base-to-string
|
// oxlint-disable typescript/no-base-to-string
|
||||||
(schema.default !== undefined ? `Default: ${String(schema.default)}` : ""));
|
(schema.default !== undefined ? `Default: ${String(schema.default)}` : ""));
|
||||||
const displayValue = sensitiveState.isRedacted ? "" : (value ?? "");
|
const displayValue = sensitiveState.isRedacted ? "" : (value ?? "");
|
||||||
const effectiveDisabled = disabled || sensitiveState.isRedacted;
|
|
||||||
const effectiveInputType =
|
const effectiveInputType =
|
||||||
sensitiveState.isSensitive && !sensitiveState.isRedacted ? "text" : inputType;
|
sensitiveState.isSensitive && !sensitiveState.isRedacted ? "text" : inputType;
|
||||||
|
|
||||||
@ -658,11 +657,16 @@ function renderTextInput(params: {
|
|||||||
<div class="cfg-input-wrap">
|
<div class="cfg-input-wrap">
|
||||||
<input
|
<input
|
||||||
type=${effectiveInputType}
|
type=${effectiveInputType}
|
||||||
class="cfg-input"
|
class="cfg-input${sensitiveState.isRedacted ? " cfg-input--redacted" : ""}"
|
||||||
placeholder=${placeholder}
|
placeholder=${placeholder}
|
||||||
.value=${displayValue == null ? "" : String(displayValue)}
|
.value=${displayValue == null ? "" : String(displayValue)}
|
||||||
?disabled=${effectiveDisabled}
|
?disabled=${disabled}
|
||||||
?readonly=${sensitiveState.isRedacted}
|
?readonly=${sensitiveState.isRedacted}
|
||||||
|
@click=${() => {
|
||||||
|
if (sensitiveState.isRedacted && params.onToggleSensitivePath) {
|
||||||
|
params.onToggleSensitivePath(path);
|
||||||
|
}
|
||||||
|
}}
|
||||||
@input=${(e: Event) => {
|
@input=${(e: Event) => {
|
||||||
if (sensitiveState.isRedacted) {
|
if (sensitiveState.isRedacted) {
|
||||||
return;
|
return;
|
||||||
@ -700,7 +704,7 @@ function renderTextInput(params: {
|
|||||||
type="button"
|
type="button"
|
||||||
class="cfg-input__reset"
|
class="cfg-input__reset"
|
||||||
title="Reset to default"
|
title="Reset to default"
|
||||||
?disabled=${effectiveDisabled}
|
?disabled=${disabled || sensitiveState.isRedacted}
|
||||||
@click=${() => onPatch(path, schema.default)}
|
@click=${() => onPatch(path, schema.default)}
|
||||||
>↺</button>
|
>↺</button>
|
||||||
`
|
`
|
||||||
@ -830,7 +834,6 @@ function renderJsonTextarea(params: {
|
|||||||
isSensitivePathRevealed: params.isSensitivePathRevealed,
|
isSensitivePathRevealed: params.isSensitivePathRevealed,
|
||||||
});
|
});
|
||||||
const displayValue = sensitiveState.isRedacted ? "" : fallback;
|
const displayValue = sensitiveState.isRedacted ? "" : fallback;
|
||||||
const effectiveDisabled = disabled || sensitiveState.isRedacted;
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="cfg-field">
|
<div class="cfg-field">
|
||||||
@ -839,12 +842,17 @@ function renderJsonTextarea(params: {
|
|||||||
${renderTags(tags)}
|
${renderTags(tags)}
|
||||||
<div class="cfg-input-wrap">
|
<div class="cfg-input-wrap">
|
||||||
<textarea
|
<textarea
|
||||||
class="cfg-textarea"
|
class="cfg-textarea${sensitiveState.isRedacted ? " cfg-textarea--redacted" : ""}"
|
||||||
placeholder=${sensitiveState.isRedacted ? REDACTED_PLACEHOLDER : "JSON value"}
|
placeholder=${sensitiveState.isRedacted ? REDACTED_PLACEHOLDER : "JSON value"}
|
||||||
rows="3"
|
rows="3"
|
||||||
.value=${displayValue}
|
.value=${displayValue}
|
||||||
?disabled=${effectiveDisabled}
|
?disabled=${disabled}
|
||||||
?readonly=${sensitiveState.isRedacted}
|
?readonly=${sensitiveState.isRedacted}
|
||||||
|
@click=${() => {
|
||||||
|
if (sensitiveState.isRedacted && params.onToggleSensitivePath) {
|
||||||
|
params.onToggleSensitivePath(path);
|
||||||
|
}
|
||||||
|
}}
|
||||||
@change=${(e: Event) => {
|
@change=${(e: Event) => {
|
||||||
if (sensitiveState.isRedacted) {
|
if (sensitiveState.isRedacted) {
|
||||||
return;
|
return;
|
||||||
@ -1253,14 +1261,19 @@ function renderMapField(params: {
|
|||||||
? html`
|
? html`
|
||||||
<div class="cfg-input-wrap">
|
<div class="cfg-input-wrap">
|
||||||
<textarea
|
<textarea
|
||||||
class="cfg-textarea cfg-textarea--sm"
|
class="cfg-textarea cfg-textarea--sm${sensitiveState.isRedacted ? " cfg-textarea--redacted" : ""}"
|
||||||
placeholder=${
|
placeholder=${
|
||||||
sensitiveState.isRedacted ? REDACTED_PLACEHOLDER : "JSON value"
|
sensitiveState.isRedacted ? REDACTED_PLACEHOLDER : "JSON value"
|
||||||
}
|
}
|
||||||
rows="2"
|
rows="2"
|
||||||
.value=${sensitiveState.isRedacted ? "" : fallback}
|
.value=${sensitiveState.isRedacted ? "" : fallback}
|
||||||
?disabled=${disabled || sensitiveState.isRedacted}
|
?disabled=${disabled}
|
||||||
?readonly=${sensitiveState.isRedacted}
|
?readonly=${sensitiveState.isRedacted}
|
||||||
|
@click=${() => {
|
||||||
|
if (sensitiveState.isRedacted && onToggleSensitivePath) {
|
||||||
|
onToggleSensitivePath(valuePath);
|
||||||
|
}
|
||||||
|
}}
|
||||||
@change=${(e: Event) => {
|
@change=${(e: Event) => {
|
||||||
if (sensitiveState.isRedacted) {
|
if (sensitiveState.isRedacted) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -56,6 +56,7 @@ export type ConfigProps = {
|
|||||||
includeSections?: string[];
|
includeSections?: string[];
|
||||||
excludeSections?: string[];
|
excludeSections?: string[];
|
||||||
includeVirtualSections?: boolean;
|
includeVirtualSections?: boolean;
|
||||||
|
onRequestUpdate?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
// SVG Icons for sidebar (Lucide-style)
|
// 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 formUnsafe = analysis.schema ? analysis.unsupportedPaths.length > 0 : false;
|
||||||
const formMode = showModeToggle ? props.formMode : "form";
|
const formMode = showModeToggle ? props.formMode : "form";
|
||||||
const envSensitiveVisible = cvs.envRevealed;
|
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
|
// Build categorised nav from schema - only include sections that exist in the schema
|
||||||
const schemaProps = analysis.schema?.properties ?? {};
|
const schemaProps = analysis.schema?.properties ?? {};
|
||||||
@ -905,7 +907,7 @@ export function renderConfig(props: ConfigProps) {
|
|||||||
class="btn btn--sm"
|
class="btn btn--sm"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
cvs.validityDismissed = true;
|
cvs.validityDismissed = true;
|
||||||
props.onRawChange(props.raw);
|
requestUpdate();
|
||||||
}}
|
}}
|
||||||
>Don't remind again</button>
|
>Don't remind again</button>
|
||||||
</div>
|
</div>
|
||||||
@ -982,7 +984,7 @@ export function renderConfig(props: ConfigProps) {
|
|||||||
title=${envSensitiveVisible ? "Hide env values" : "Reveal env values"}
|
title=${envSensitiveVisible ? "Hide env values" : "Reveal env values"}
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
cvs.envRevealed = !cvs.envRevealed;
|
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">
|
<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,
|
isSensitivePathRevealed,
|
||||||
onToggleSensitivePath: (path) => {
|
onToggleSensitivePath: (path) => {
|
||||||
toggleSensitivePathReveal(path);
|
toggleSensitivePathReveal(path);
|
||||||
props.onRawChange(props.raw);
|
requestUpdate();
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1071,7 +1073,7 @@ export function renderConfig(props: ConfigProps) {
|
|||||||
aria-pressed=${!blurred}
|
aria-pressed=${!blurred}
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
cvs.rawRevealed = !cvs.rawRevealed;
|
cvs.rawRevealed = !cvs.rawRevealed;
|
||||||
props.onRawChange(props.raw);
|
requestUpdate();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
${blurred ? icons.eyeOff : icons.eye}
|
${blurred ? icons.eyeOff : icons.eye}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user