1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//! **The spine between frames** (bl-b52c): the draft a fork is composed from,
//! and the one act it spends.
//!
//! # The records pane held no state, and now it holds exactly one draft
//!
//! DESIGN §4.18 recorded that this pane holds nothing at all — *"not even a
//! draft, which is one less than tuning holds"* — and that was true of a pane
//! whose whole content was two reads. The spine brings an ACT, and an act with
//! parameters needs somewhere to type them, so the pane arrives at §4.17's
//! shape after all: **one piece of state, the draft of the gesture**, because
//! two words typed into two boxes are not a fact about anything until they are
//! sent.
//!
//! It is a bare field rather than an option, exactly as the composer's own two
//! parameter boxes are (`Model::reason`, `Model::typed`): the boxes are on the
//! glass whenever the half is, so there is no *closed* state for an option to
//! represent.
//!
//! # The goal is spent and the role is kept, and the split is the acts' own
//!
//! The composer already draws this line twice. A flag's reason is **taken** on
//! firing, because what a flag says is said; an unmaking's arming is **cloned**,
//! because the refusal is the common answer and clearing it would charge a
//! retype for the engine's *no*. A fork's goal is the first of those — it was
//! said, to a child that now exists — and its role is the second: a role is a
//! name off a config lineage, the same name the next attempt off the same spine
//! wants, and re-typing it per notch would be a toll on the ordinary act.
use super::Model;
/// **The draft of a fork**: the two words the operator supplies, beside the
/// `from` the control itself carries off the notch it hangs on.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Forking {
/// The role, which is the model: litany resolves the provider and model id
/// from this name against the fork point's own governing config.
pub role: String,
/// What the child is being asked to do.
pub goal: String,
}
impl Forking {
/// **Whether both words are there.** The wire refuses a fork with an empty
/// `from`, `role` or `goal`, so the control is disabled until the two this
/// box holds are non-blank — the tuning pane's `set`, not the composer's
/// second start: the parameters are missing, not the subject.
pub fn ready(&self) -> bool {
!self.role.trim().is_empty() && !self.goal.trim().is_empty()
}
}
impl Model {
/// **Fork the selected conversation from `commit`**, or do nothing where
/// the draft is half-typed, nothing is aimed at, or nothing is selected.
///
/// The three gates are one reading rather than three arms of a control:
/// every state they exclude is one the pane cannot paint a fork control in,
/// so this is what makes them unreachable rather than merely unlikely —
/// `crate::ui::model::tuning::tune`'s own shape, one noun over.
pub fn post_fork(&mut self, commit: String) {
let (Some(aim), Some(parent)) = (self.aim.clone(), self.conversation.clone()) else {
return;
};
if !self.forking.ready() {
return;
}
let role = self.forking.role.trim().to_owned();
let goal = std::mem::take(&mut self.forking.goal);
self.outbox.push(super::Posted::act(crate::verbs::fork(
aim.address,
parent,
commit,
role,
goal.trim().to_owned(),
)));
}
}
#[cfg(test)]
mod tests;