Improve settings hints and workspace log clarity

This commit is contained in:
2026-02-21 12:29:53 -03:00
parent a18545fb18
commit df401b9e55
4 changed files with 92 additions and 6 deletions

View File

@@ -1,5 +1,6 @@
/**
* Dedicated settings screen for providers, task model bindings, and catalog controls.
* Uses concise helper hints for advanced runtime and provider settings.
*/
import { useCallback, useEffect, useMemo, useState } from 'react';
import type { JSX } from 'react';
@@ -67,7 +68,7 @@ function clampProcessingLogUnboundEntries(value: number): number {
}
/**
* Renders compact human-oriented settings controls.
* Renders compact human-oriented settings controls with plain-language hints.
*/
export default function SettingsScreen({
settings,
@@ -367,6 +368,7 @@ export default function SettingsScreen({
onChange={(nextPath) => setUploadDefaults({ ...uploadDefaults, logical_path: nextPath })}
suggestions={knownPaths}
/>
<span className="settings-field-hint">Used when you upload without choosing a path.</span>
</label>
<label className="settings-field settings-field-wide">
Default Tags
@@ -375,6 +377,7 @@ export default function SettingsScreen({
onChange={(nextTags) => setUploadDefaults({ ...uploadDefaults, tags: nextTags })}
suggestions={knownTags}
/>
<span className="settings-field-hint">Added automatically when no tags are selected.</span>
</label>
<label className="settings-field">
Cards Per Page
@@ -385,7 +388,14 @@ export default function SettingsScreen({
value={cardsPerPageInput}
onChange={(event) => setCardsPerPageInput(event.target.value)}
/>
<span className="settings-field-hint">Controls how many documents you see at once.</span>
</label>
<div className="settings-subsection-divider">
<h4>Processing Log Controls</h4>
<p className="small">
These settings affect processing logs only. They do not change default path, tags, or document cards.
</p>
</div>
<label className="settings-field">
Keep document sessions
<input
@@ -403,6 +413,7 @@ export default function SettingsScreen({
}
}}
/>
<span className="settings-field-hint">How many recent log sessions to keep for each document.</span>
</label>
<label className="settings-field">
Keep unbound entries
@@ -421,8 +432,9 @@ export default function SettingsScreen({
}
}}
/>
<span className="settings-field-hint">How many standalone log entries to keep.</span>
</label>
<label className="inline-checkbox settings-checkbox-field">
<label className="inline-checkbox settings-checkbox-field settings-checkbox-with-hint">
<input
type="checkbox"
checked={displaySettings.log_typing_animation_enabled}
@@ -430,7 +442,10 @@ export default function SettingsScreen({
setDisplaySettings({ ...displaySettings, log_typing_animation_enabled: event.target.checked })
}
/>
Processing log typing animation enabled
<span className="settings-checkbox-copy">
Processing log typing animation enabled
<span className="settings-field-hint">Shows new log text as a type-in animation.</span>
</span>
</label>
<p className="small settings-helper-text">
Processing-log retention values are used by backend trim routines when pruning historical entries.
@@ -565,6 +580,7 @@ export default function SettingsScreen({
)
}
/>
<span className="settings-field-hint">Task settings use this ID to select the provider.</span>
</label>
<label className="settings-field">
Label
@@ -596,6 +612,7 @@ export default function SettingsScreen({
);
}}
/>
<span className="settings-field-hint">Stop waiting after this many seconds if a call hangs.</span>
</label>
<label className="settings-field settings-field-wide">
Base URL
@@ -609,6 +626,7 @@ export default function SettingsScreen({
)
}
/>
<span className="settings-field-hint">API endpoint root for this provider.</span>
</label>
<label className="settings-field settings-field-wide">
API Key
@@ -624,6 +642,7 @@ export default function SettingsScreen({
)
}
/>
<span className="settings-field-hint">Leave blank to keep the current stored key.</span>
</label>
<label className="inline-checkbox settings-checkbox-field">
<input
@@ -678,10 +697,12 @@ export default function SettingsScreen({
<label className="settings-field">
Model
<input value={ocrTask.model} onChange={(event) => setOcrTask({ ...ocrTask, model: event.target.value })} />
<span className="settings-field-hint">Model name sent to the selected provider.</span>
</label>
<label className="settings-field settings-field-wide">
OCR Prompt
<textarea value={ocrTask.prompt} onChange={(event) => setOcrTask({ ...ocrTask, prompt: event.target.value })} />
<span className="settings-field-hint">Instructions used when reading handwriting text.</span>
</label>
</div>
</div>
@@ -708,6 +729,7 @@ export default function SettingsScreen({
<label className="settings-field">
Model
<input value={summaryTask.model} onChange={(event) => setSummaryTask({ ...summaryTask, model: event.target.value })} />
<span className="settings-field-hint">Model name sent to the selected provider.</span>
</label>
<label className="settings-field">
Max Input Tokens
@@ -723,10 +745,12 @@ export default function SettingsScreen({
}
}}
/>
<span className="settings-field-hint">Long inputs are trimmed to this size before summarizing.</span>
</label>
<label className="settings-field settings-field-wide">
Summary Prompt
<textarea value={summaryTask.prompt} onChange={(event) => setSummaryTask({ ...summaryTask, prompt: event.target.value })} />
<span className="settings-field-hint">Instructions that shape the generated summary.</span>
</label>
</div>
</div>
@@ -753,42 +777,56 @@ export default function SettingsScreen({
<label className="settings-field">
Model
<input value={routingTask.model} onChange={(event) => setRoutingTask({ ...routingTask, model: event.target.value })} />
<span className="settings-field-hint">Model name sent to the selected provider.</span>
</label>
<label className="settings-field">
Neighbor Count
<input type="number" value={routingTask.neighbor_count} onChange={(event) => setRoutingTask({ ...routingTask, neighbor_count: Number.parseInt(event.target.value, 10) || routingTask.neighbor_count })} />
<span className="settings-field-hint">How many close matches to compare before routing.</span>
</label>
<label className="settings-field">
Min Neighbor Similarity
<input type="number" step="0.01" min="0" max="1" value={routingTask.neighbor_min_similarity} onChange={(event) => setRoutingTask({ ...routingTask, neighbor_min_similarity: Number.parseFloat(event.target.value) || routingTask.neighbor_min_similarity })} />
<span className="settings-field-hint">Ignore neighbors below this match score.</span>
</label>
<label className="settings-field">
Auto Apply Confidence
<input type="number" step="0.01" min="0" max="1" value={routingTask.auto_apply_confidence_threshold} onChange={(event) => setRoutingTask({ ...routingTask, auto_apply_confidence_threshold: Number.parseFloat(event.target.value) || routingTask.auto_apply_confidence_threshold })} />
<span className="settings-field-hint">Minimum model confidence for automatic changes.</span>
</label>
<label className="settings-field">
Auto Apply Neighbor Similarity
<input type="number" step="0.01" min="0" max="1" value={routingTask.auto_apply_neighbor_similarity_threshold} onChange={(event) => setRoutingTask({ ...routingTask, auto_apply_neighbor_similarity_threshold: Number.parseFloat(event.target.value) || routingTask.auto_apply_neighbor_similarity_threshold })} />
<span className="settings-field-hint">Minimum neighbor score for automatic changes.</span>
</label>
<label className="inline-checkbox settings-checkbox-field">
<label className="inline-checkbox settings-checkbox-field settings-checkbox-with-hint">
<input type="checkbox" checked={routingTask.neighbor_path_override_enabled} onChange={(event) => setRoutingTask({ ...routingTask, neighbor_path_override_enabled: event.target.checked })} />
Dominant neighbor path override enabled
<span className="settings-checkbox-copy">
Dominant neighbor path override enabled
<span className="settings-field-hint">
If a strong top match disagrees with the model, use the top match path instead.
</span>
</span>
</label>
<label className="settings-field">
Override Min Similarity
<input type="number" step="0.01" min="0" max="1" value={routingTask.neighbor_path_override_min_similarity} onChange={(event) => setRoutingTask({ ...routingTask, neighbor_path_override_min_similarity: Number.parseFloat(event.target.value) || routingTask.neighbor_path_override_min_similarity })} />
<span className="settings-field-hint">Top neighbor must reach this score to override.</span>
</label>
<label className="settings-field">
Override Min Gap
<input type="number" step="0.01" min="0" max="1" value={routingTask.neighbor_path_override_min_gap} onChange={(event) => setRoutingTask({ ...routingTask, neighbor_path_override_min_gap: Number.parseFloat(event.target.value) || routingTask.neighbor_path_override_min_gap })} />
<span className="settings-field-hint">Top match must beat the second match by at least this gap.</span>
</label>
<label className="settings-field">
Override Max LLM Confidence
<input type="number" step="0.01" min="0" max="1" value={routingTask.neighbor_path_override_max_confidence} onChange={(event) => setRoutingTask({ ...routingTask, neighbor_path_override_max_confidence: Number.parseFloat(event.target.value) || routingTask.neighbor_path_override_max_confidence })} />
<span className="settings-field-hint">Override only runs when model confidence is at or below this level.</span>
</label>
<label className="settings-field settings-field-wide">
Routing Prompt
<textarea value={routingTask.prompt} onChange={(event) => setRoutingTask({ ...routingTask, prompt: event.target.value })} />
<span className="settings-field-hint">Instructions used when deciding document path and tags.</span>
</label>
</div>
</div>
@@ -805,26 +843,32 @@ export default function SettingsScreen({
<label className="settings-field settings-field-wide">
Typesense Embedding Model Slug
<input value={handwritingStyle.embed_model} onChange={(event) => setHandwritingStyle({ ...handwritingStyle, embed_model: event.target.value })} />
<span className="settings-field-hint">Embedding model used to compare handwriting style similarity.</span>
</label>
<label className="settings-field">
Neighbor Limit
<input type="number" min={1} max={32} value={handwritingStyle.neighbor_limit} onChange={(event) => setHandwritingStyle({ ...handwritingStyle, neighbor_limit: Number.parseInt(event.target.value, 10) || handwritingStyle.neighbor_limit })} />
<span className="settings-field-hint">How many nearby samples to check during matching.</span>
</label>
<label className="settings-field">
Match Min Similarity
<input type="number" step="0.01" min="0" max="1" value={handwritingStyle.match_min_similarity} onChange={(event) => setHandwritingStyle({ ...handwritingStyle, match_min_similarity: Number.parseFloat(event.target.value) || handwritingStyle.match_min_similarity })} />
<span className="settings-field-hint">Minimum similarity needed to treat two styles as a match.</span>
</label>
<label className="settings-field">
Bootstrap Match Min Similarity
<input type="number" step="0.01" min="0" max="1" value={handwritingStyle.bootstrap_match_min_similarity} onChange={(event) => setHandwritingStyle({ ...handwritingStyle, bootstrap_match_min_similarity: Number.parseFloat(event.target.value) || handwritingStyle.bootstrap_match_min_similarity })} />
<span className="settings-field-hint">Stricter match score used only while bootstrapping new clusters.</span>
</label>
<label className="settings-field">
Bootstrap Sample Size
<input type="number" min={1} max={30} value={handwritingStyle.bootstrap_sample_size} onChange={(event) => setHandwritingStyle({ ...handwritingStyle, bootstrap_sample_size: Number.parseInt(event.target.value, 10) || handwritingStyle.bootstrap_sample_size })} />
<span className="settings-field-hint">Number of samples used to start each new style cluster.</span>
</label>
<label className="settings-field">
Max Image Side (px)
<input type="number" min={256} max={4096} value={handwritingStyle.image_max_side} onChange={(event) => setHandwritingStyle({ ...handwritingStyle, image_max_side: Number.parseInt(event.target.value, 10) || handwritingStyle.image_max_side })} />
<span className="settings-field-hint">Resizes large images to this limit before analysis.</span>
</label>
</div>
</div>

View File

@@ -976,6 +976,32 @@ button:disabled {
color: var(--color-text-muted);
}
.settings-field-hint {
margin: 0;
font-size: 0.72rem;
font-weight: 400;
color: #95a6c4;
line-height: 1.35;
}
.settings-subsection-divider {
grid-column: span 12;
display: grid;
gap: 0.2rem;
padding-top: 0.2rem;
border-top: 1px solid rgba(70, 89, 122, 0.55);
}
.settings-subsection-divider h4 {
margin: 0;
font-family: var(--font-display);
font-size: 0.82rem;
}
.settings-subsection-divider p {
margin: 0;
}
.inline-checkbox {
display: grid;
grid-template-columns: auto 1fr;
@@ -1004,6 +1030,15 @@ button:disabled {
color: #dbe8ff;
}
.settings-checkbox-with-hint {
align-items: start;
}
.settings-checkbox-copy {
display: grid;
gap: 0.15rem;
}
.settings-catalog-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));