use super::{ConfigState, NEW_WORKFLOW_HINT, RELOAD_HINT, form_ui, status};
use crate::config_edit::form::{self, Schema};
use crate::config_edit::lernie_global::Editor;
use crate::model_pick::grammar::declare_model;
use crate::theme;
use status::{describe_saved, reload_status, status_line};
use std::path::PathBuf;
const DECLARE_HINT: &str = "The model id to declare in models.yaml. Declare drafts the entry; \
Apply writes it. The entry is the id and its context_window — the \
denominator of the context-fullness figure, and the one fact anything reads \
out of this table. Typed, the whole file is `/config models <text…>`.";
pub(super) fn render(ui: &mut egui::Ui, config: &mut ConfigState, provider_rows: &[String]) {
ui.heading(egui::RichText::new("lernie global config").color(theme::integration_hue("lernie")));
ui.monospace(config.lernie_editor.path().display().to_string());
if config.lernie_editor.is_new() {
ui.weak("(new file)");
}
match schema(config) {
Some(schema) => settings(ui, config, &schema, provider_rows),
None => {
ui.weak(form_ui::NO_READER);
}
}
raw(ui, config);
ui.horizontal(|ui| {
if ui
.button("Apply")
.on_hover_text(
"Write this file: the draft replaces it whole, refused only if it \
changed underneath you since it was read. Typed, it is \
`/config models <text…>`.",
)
.clicked()
{
config.lernie_status = describe_saved(config.lernie_editor.apply(&config.io));
}
if ui.button("Reload").on_hover_text(RELOAD_HINT).clicked() {
config.lernie_status = reload_status(config.lernie_editor.reload(&config.io));
}
if ui
.button("models.yaml")
.on_hover_text(
"Load lernie's global models.yaml into this editor. `/config models` \
with no text reads the same bytes.",
)
.clicked()
{
open(config, config.lernie.models());
}
});
workflow_list(ui, config);
let create = crate::shell::row::control_last(
ui,
|ui| {
ui.label("new workflow:").on_hover_text(NEW_WORKFLOW_HINT);
ui.add(
egui::TextEdit::singleline(&mut config.new_workflow).desired_width(f32::INFINITY),
)
.on_hover_text(NEW_WORKFLOW_HINT);
},
|ui| {
ui.button("Create")
.on_hover_text(
"Open an empty editor for a workflow file of that name. Nothing is \
written to disk until you press Apply — `/config workflow <name> \
<text…>` writes one outright.",
)
.clicked()
},
)
.1;
if create {
new_workflow(config);
}
status_line(ui, &config.lernie_status);
}
fn schema(config: &ConfigState) -> Option<Schema> {
let name = config.lernie_editor.path().file_name()?;
form::schema_for(&name.to_string_lossy())
}
fn settings(
ui: &mut egui::Ui,
config: &mut ConfigState,
schema: &Schema,
provider_rows: &[String],
) {
let groups = form::read(schema, config.lernie_editor.draft(), provider_rows);
if let Some((row, value)) = form_ui::render(ui, "lernie", &groups, provider_rows) {
match form::write(schema, config.lernie_editor.draft(), &row, &value) {
Ok(text) => {
config.lernie_editor.set_draft(text);
config.lernie_status =
format!("{}.{} drafted — Apply to write", row.entry, row.field);
}
Err(e) => config.lernie_status = e.to_string(),
}
}
declare(ui, config, schema);
}
fn declare(ui: &mut egui::Ui, config: &mut ConfigState, schema: &Schema) {
if schema.block != crate::model_pick::grammar::MODELS {
return;
}
let typed = &mut config.new_model;
let declare = crate::shell::row::control_last(
ui,
|ui| {
ui.label("declare model:");
ui.add(egui::TextEdit::singleline(typed).desired_width(f32::INFINITY))
.on_hover_text(DECLARE_HINT);
},
|ui| {
ui.button("Declare")
.on_hover_text(
"Draft a `models:` entry for that id. Nothing lands until Apply. \
The entry declares the context window yog measures fullness \
against; picking a model does not write one. Typed, the whole \
file is `/config models <text…>`.",
)
.clicked()
},
)
.1;
if declare {
config.lernie_status = declared(config);
}
}
fn declared(config: &mut ConfigState) -> String {
match declare_model(config.lernie_editor.draft(), config.new_model.trim()) {
Ok(Some(text)) => {
config.lernie_editor.set_draft(text);
"declared — Apply to write".to_string()
}
Ok(None) => "already declared".to_string(),
Err(e) => e.to_string(),
}
}
fn raw(ui: &mut egui::Ui, config: &mut ConfigState) {
egui::CollapsingHeader::new("raw text")
.id_salt("raw-models")
.show(ui, |ui| {
form_ui::raw_editor(ui, config.lernie_editor.draft_mut());
})
.header_response
.on_hover_text(
"Open the file's raw text — everything it carries beyond the settings \
above. Edits here change nothing until Apply. No key of its own: Tab \
reaches it, Space presses it.",
);
}
fn workflow_list(ui: &mut egui::Ui, config: &mut ConfigState) {
let files = config.workflows.clone();
ui.horizontal_wrapped(|ui| {
for file in files {
let label = file.file_name().map(|n| n.to_string_lossy().into_owned());
if ui
.button(label.unwrap_or_default())
.on_hover_text(
"Load this workflow file into this editor. \
`/config workflow <name>` with no text reads the same bytes.",
)
.clicked()
{
open(config, file);
}
}
});
}
fn new_workflow(config: &mut ConfigState) {
match config.lernie.new_workflow(&config.new_workflow) {
Ok(path) => {
config.lernie_editor = Editor::seeded(path, b"");
config.lernie_status = "new workflow ready".to_string();
}
Err(e) => config.lernie_status = e.to_string(),
}
}
fn open(config: &mut ConfigState, path: PathBuf) {
match Editor::load(path.clone(), &config.io) {
Ok(editor) => config.lernie_editor = editor,
Err(_) => config.lernie_editor = Editor::seeded(path, b""),
}
}