JZXYSIYDPBWQZCAMGDZ5BFMN6SU73EVVDIYEGTDJN6DVOSBNHN4QC 6YZAVBWU6E5FYOI5JGEIPXGZLIKAW6LS2AOFIQWEE5DMOPPCD5PQC SWWE2R6MVBX5CNM6X3WLXZTSRTU53PBJL7WJSFVF77XBPXDX4COAC WT3GA27PQ2AOAIGK65O3Q4DMX4AZDVNULBLRL6GF4QW6QCASUEAAC UB2ITZJSDADVINSQEZ3HA6PVGA7OA6JYFG5GMSO7Y7LOXJC4FI7AC EC3TVL4X6VZZVLOKUN63LC73ADPHBHMZO7QMDXGX2ZPURVI4B4XQC S2NVIFXRFER4SRA37WCT5XTXHDHAL5WIGGKY4A4XOTPLTKTZSRGQC YBJRDOTCX3ZRDB5EVXJBR55FX3CADCSIGMYWNYVC2PD5W3GXR3DQC KM5PSZ4A2FJOPHJA6RC7LHZAUXLQDZDQC2DVSE5YUORLFIPZO74QC A5YBC77VWH2LXCZJOPZORQJI5ZYABSCHJWVX5HVNWPM5RABXESLQC D7A7MSIHJS3IAOLEPK52M4CZLDPLO7JB3Y62XACT2AM6UUCPQ6BAC AMPZ2BXK4IGUZO3OPBRSJ6Z4GI5K4PRFMLUGTR6AP4FKKRWQG7LQC MJDGPSHGF62FTVWZBE7MFNJTUQD42OBVJEOSVPBT553UFJLTEMXQC ONRCENKTUB4JJMPXNAQQYEWDYD54TAGOLWH742GF4EH3KTHV7YLQC 4ELJZGRJNL6FXB33QTYDNPY57JA3WZPUXKLQRTGSLDM7W65PD3YQC FR52XEMWD22VH3GKSARXJUJXOGO7ZSQEHWPXFRWHLGRAJU3WRKCAC CALXOZXANFZ64NBZBTR2KYTZ6ZLLCJXNFAEALBB2EYAVDVJJ6X6AC L6KSEFQIWICZJ6HJUFKLZQDEH6X2QMFM4Z7ZZUGMLDMFF7EHRXWAC BFN2VHZS7VCBUHQ4S3CQ3LFQV2V4M6VANNAF32XMRFQVWRGYSZ6AC GWZGYNIBQP2AA7WYULNBS2BCV4B36IHK4OS7XHVOTUUG27E76XFQC 3SYSJKYLVCXR54LRUPL6GOQISSJS6XWK4M6PRQRCKZN7F23NNVEAC 23SFYK4Q5NKBPJG53PQNPWQH6UOUU2YKJEL7RLXYBRLJOJYV7AWQC OPXFZKEBDHZZLXEJ2JRDYBOJH6YIN7UZNZYHVHMWMQVDTE2ZD53QC 3QVNMRNMI63L2VOFVTMPCVPXH3J4JXLXVTIIPNOMACQCPCAPWILQC MYGIBRRHHXPKVRAMQQRJTZH74L2XOK3SF7J57JPCRKSVRLZ2D6NQC PKJCFSBMXXA2H3US47IJEB7QMIYLEKTLGWQUYEZSKCDODDQTD6HQC XSZZB47UXR6KGYFZZQFQR63X2LDKOH6TPNNBRRGHUCI5JJ4JIWVAC 3BK22XE5LPOH2EK5AMRXFXHNQNCJ54HEPYRINHJT4DA7INT32I7AC WGID4LS4EISIOXB5Y5SOFGEF5PLBJSCPFCETH2CGRTFN3NC4WGJQC VCNKFNUF7OWVSWC6I5D25KUZ3XZZICZ3LHWVPF2N5ZSP7LQ2JOUQC 6F7Q4ZLR5DGYT557MYMSHMZGQ7EVEB3LZGLZFCWHGIOI66STIANQC ACDXXAX26ZJJFKJDGRC2GOSJY5JHQWCSTP55SYI6D6LH5UIRYUBAC X6AK4QPXKTGTWIMJ5CIR46CVIXVUXV5WKTP73CNQOIRANQN4MD5QC KMB6FND35LWT4XTRUNEJZ7SQXFZEUYIJAADGWJVB4RY4IIAT4KSQC YYKXNBFL44LLOBABLXBKOF7IFUIGIEL2SYIPLGDH6UOEY5EZZZSQC 5CYU7UT74NXJWCC36GNQGVBXH676BHBXWZQVIINRMPDEJ27SBRGAC ESMM3FELOBYIX7FUNOU37FYKRJHFU2IMX6LY6EGJTVPTBDU3SEEQC TSFQFCB2NXDOBLBRUSAT63VJIXLPPTJGSTIDNOTLGHVVWSHITRNQC 7SSBM4UQMYVRL6L3ICYZQPSMYLZZQNMDWH6JKA3KOOSXZDJHESHQC SWDPAGF6BGUA2L6KFP6LAVCA3SX4QA5FOZRLLOAWLNZ6RNCIV4RQC I2AG42PAVOII4V4TWDJV5ZVNDIHKBRDT254BFQLFUIY723TW6CCQC SASAN2XCWDQ2VEHZ7TAQEN2R3Y7AG7JUGEFVRL4DZAGHXDFEZFRQC YKHE3XMWOWPGOWYSISF73MIAKN7WB3AHCV2OA4ECAFPF47YHUXEAC KEPKF3WO7ZZ2VB2DRVVTWTGPL7TCA52BMYUPHUNUJH6WO3HAT6JQC KWTBNTO3QUUE2YADF6SYW6G6ZOKYEWRJQKIWDGZXR33S3YNDVIZQC 5MUEECMJHU44FL5RDUR3VFBIWK3H4X2L5MVJ73J37PYHZWLUKU2AC 3TLPJ57B2OD5OWJN5WMS7A4W7IGFUWJJHVIXRM34VT6KUN6R4YSAC AI3IMKC3HRPMTWQCU5HGWKUHGKTJ22QF7V4AAEI6IEBIZ4WYWCKQC WXQBBQ2ACNPKCTDF7OTBLP342324ZIOJK42PUO2KT2IYVJ2ETCMAC PTWZYQFRWWUOE2WMQT26CKZKFSHAIJVJS3QWHJFYUFDRRTVPHSUAC RDRBP7AL74NBFNZSQFTU7VQCMWTGJO5RZWGPCWVVS5WRTXJ77DFAC UR4J677RWA3OFG6HQTD46BUUE5YFPSBEFCJAEM5OMT4V5A7SBNNQC K63JN6CRRCP4S5NY2FPH46Q7QLWH4B6QXQHKFPVQBHHIHNDVXFDQC A6Z4O6RC33HYWP7JIVQ6FDWE4EOCQWQTIGENK2WAHUGSHDDLSA7QC 7BLZN73OYUAJEYTJ6WWHRZ7S7ONDGRBKNJGFGW62NAIZBUK3CECQC }MsgIn::SwitchToChannel(name) => {let state: State;(internal_state, state) = spawn_blocking(move || {switch_to_channel(&mut internal_state, name);let state = get_state(&internal_state);(internal_state, state)}).await.unwrap();let _ = msg_out_tx.send(MsgOut::Refreshed(state));
}/// Get all channels excluding the current channelfn other_channels(repo: &pijul::Repository) -> Vec<String> {let txn = repo.pristine.txn_begin().unwrap();let current = txn.current_channel().unwrap_or(pijul::DEFAULT_CHANNEL);txn.channels("").unwrap().into_iter().filter_map(|channel| {let channel = channel.read();let name = txn.name(&*channel);(name != current).then(|| name.to_string())}).collect()
fn switch_to_channel(state: &mut InternalState, name: String) {let repo = &state.repo;let current_channel = current_channel(repo);let txn = repo.pristine.arc_txn_begin().unwrap();if name == current_channel {return;} else {// Check there are no unrecorded changeslet channel = {let txn = txn.read();txn.load_channel(¤t_channel).unwrap()};if let Some(channel) = channel&& has_unrecorded_changes(txn.clone(), channel.clone(), repo){error!("Cannot change channel, as there are unrecorded changes.");return;}}txn.write().set_current_channel(&name).unwrap();txn.commit().unwrap();}
fn has_unrecorded_changes(txn: pijul::ArcTxn<libpijul::pristine::sanakirja::MutTxn<()>>,channel: pijul::ChannelRef<libpijul::pristine::sanakirja::MutTxn<()>>,repo: &pijul::Repository,) -> bool {let mut state = libpijul::RecordBuilder::new();state.record(txn,libpijul::Algorithm::default(),false,&libpijul::DEFAULT_SEPARATOR,channel,&repo.working_copy,&repo.changes,"",std::thread::available_parallelism().unwrap().get(),).unwrap();let rec = state.finish();debug!("actions = {:?}", rec.actions);!rec.actions.is_empty()}
#[derive(Debug, Clone)]pub struct Channel {pub ix: usize,pub name: String,}
el(text(": ")),el(button(text(channel)), /* TODO* .on_press(Message) */),]));
el(text(", channel: ")),el(button(text(channel)).on_press_maybe(can_switch_channel.then_some(Msg::SwitchChannel))),]).align_y(alignment::Vertical::Center));
let is_selected = matches!(selection.as_ref() ,Some(Selection::UntrackedFile{ ix: selected_ix, .. }) if &ix == selected_ix
let is_selected = matches!(status_selection.as_ref() ,Some(StatusSelection::UntrackedFile{ ix: selected_ix, .. }) if &ix == selected_ix
let is_selected = matches!(selection.as_ref(),Some(Selection::ChangedFile{ ix: selected_ix, .. }) if &ix == selected_ix
let is_selected = matches!(status_selection.as_ref(),Some(StatusSelection::ChangedFile{ ix: selected_ix, .. }) if &ix == selected_ix
Selection::UntrackedFile { .. }| Selection::ChangedFile { .. }| Selection::Log { file: None, .. } => 1,Selection::Log { file: Some(_), .. } => 2,
StatusSelection::UntrackedFile { .. }| StatusSelection::ChangedFile { .. }| StatusSelection::Log { file: None, .. } => 1,StatusSelection::Log { file: Some(_), .. } => 2,
let main = if let Some(col_2) = col_2 {
let view_channels = || {other_channels.iter().enumerate().map(|(ix, channel)| {let is_selected = matches!(channel_selection,Some(selection::Channel{ ix: selected_ix, .. }) if &ix == selected_ix);el(button(text(channel)).on_press(Msg::Confirm).class(selectable_button_class(is_selected)))})};let main = if *switching_channel {if other_channels.is_empty() {el(column([el(text(format!("Current channel: {channel}")))]))} else {el(column([el(text(format!("Current channel: {channel}. Switch to:"))),el(column(view_channels())),]))}} else if let Some(col_2) = col_2 {
}if repo.switching_channel {let sub_state = if repo.state.other_channels.is_empty() {SwitchingChannelState::NoOtherChannels} else if channel_selection.is_some() {SwitchingChannelState::SomethingSelected} else {SwitchingChannelState::NothingSelected};return ActionState::SwitchingChannel(sub_state);
state.cursor.view.selection.as_ref().unwrap(),cursor::Selection::UntrackedFile { ix, path, diff_selected }
state.selection.view.status.as_ref().unwrap(),selection::Status::UntrackedFile { ix, path, diff_selected }
state.cursor.view.selection.as_ref().unwrap(),cursor::Selection::ChangedFile { ix, path, diff_selected }
state.selection.view.status.as_ref().unwrap(),selection::Status::ChangedFile { ix, path, diff_selected }
state.cursor.view.selection.as_ref().unwrap(),cursor::Selection::ChangedFile { ix, path, diff_selected }
state.selection.view.status.as_ref().unwrap(),selection::Status::ChangedFile { ix, path, diff_selected }
pub use inflorescence_view::cursor::{Dir, HeldKey, LogChangeFileSelection, Msg, Select, Selection,State as ViewState,
pub use inflorescence_view::selection::{Channel, Dir, HeldKey, LogChangeFileSelection, Msg, Select,State as ViewState, Status,
let (selection, task) = match state.view.selection.take() {Some(Selection::UntrackedFile {
if *switching_channel {let channels = &repo_state.other_channels;let selection = match state.view.channel.take() {Some(Channel { ix, name: _ }) => {let ix = if ix == channels.len() - 1 { 0 } else { ix + 1 };channel_selection(ix, channels)}None => {let ix = 0;channel_selection(ix, channels)}};state.view.channel = Some(selection);return Task::none();}let (selection, task) = match state.view.status.take() {Some(Status::UntrackedFile {
let (selection, task) = match state.view.selection.take() {Some(Selection::UntrackedFile {
if *switching_channel {let channels = &repo_state.other_channels;let selection = match state.view.channel.take() {Some(Channel { ix, name: _ }) => {let ix = if ix == 0 { channels.len() - 1 } else { ix - 1 };channel_selection(ix, channels)}None => {let ix = channels.len() - 1;channel_selection(ix, channels)}};state.view.channel = Some(selection);return Task::none();}let (selection, task) = match state.view.status.take() {Some(Status::UntrackedFile {
let (selection, task): (Option<Selection>, Task<crate::Msg>) =match state.view.selection.take() {Some(Selection::LogChange {
let (selection, task): (Option<Status>, Task<crate::Msg>) =match state.view.status.take() {Some(Status::LogChange {
selection @ (Some(Selection::UntrackedFile { .. })| Some(Selection::ChangedFile { .. })| Some(Selection::LogChange { file: None, .. })
selection @ (Some(Status::UntrackedFile { .. })| Some(Status::ChangedFile { .. })| Some(Status::LogChange { file: None, .. })
app::Msg::Cursor(msg) => cursor::update(
app::Msg::Confirm => {if let Some(repo) = state.repo.as_mut()&& repo.switching_channel&& let Some(selection::Channel { ix: _, name }) =state.selection.view.channel.take(){state.repo_tx_in.send(repo::MsgIn::SwitchToChannel(name)).unwrap();repo.switching_channel = false;}Task::none()}app::Msg::Cancel => {if let Some(repo) = state.repo.as_mut()&& repo.switching_channel{repo.switching_channel = false;state.selection.view.channel = None;}Task::none()}app::Msg::Selection(msg) => selection::update(
let (selection, selection_task) = cursor::untracked_file_selection(repo_state,status_nav,ix,&mut state.files,&mut state.files_diffs,cursor::VertDir::Down,);
let (selection, selection_task) =selection::untracked_file_selection(repo_state,status_nav,ix,&mut state.files,&mut state.files_diffs,selection::VertDir::Down,);
// Reset most thingsstate.selection = selection::State::default();state.record_msg = None;state.files_diffs.diffs_nav = None;state.logs.changes_nav = None;state.logs.diffs_nav = None;let app::Repo {state: repo_state,status_nav,switching_channel: _,} = state.repo.as_mut().unwrap();repo_state.changed_files = repo::ChangedFiles::default();state.files_diffs.diffs_nav = None;// Re-initialize status navlet contents_count = repo::nav_contents_count(repo_state);let (new_status_nav, status_nav_task) =iced_nav_scrollable::init(contents_count, HashSet::default());*status_nav = new_status_nav;return status_nav_task.map(|msg| Msg::View(app::Msg::StatusNav(msg)));
// Re-index cursor selectionlet cursor_task = reindex_selection(&mut state.cursor,
// Re-index selectionlet selection_task = reindex_selection(&mut state.selection,
if let Some(selection) = cursor.view.selection.take() {let (selection, task) = match selection {cursor::Selection::UntrackedFile {
if let Some(current_selection) = selection.view.status.take() {let (new_selection, task) = match current_selection {selection::Status::UntrackedFile {
"j" => Some(Msg::View(app::Msg::Cursor(cursor::Msg::PressDir(cursor::Dir::Down),
"c" => Some(Msg::View(app::Msg::SwitchChannel)),"j" => Some(Msg::View(app::Msg::Selection(selection::Msg::PressDir(selection::Dir::Down),
Key::Named(key::Named::ArrowDown) => Some(Msg::View(app::Msg::Cursor(cursor::Msg::PressDir(cursor::Dir::Down)),)),Key::Named(key::Named::ArrowUp) => Some(Msg::View(app::Msg::Cursor(cursor::Msg::PressDir(cursor::Dir::Up)),)),Key::Named(key::Named::ArrowLeft) => Some(Msg::View(app::Msg::Cursor(cursor::Msg::PressDir(cursor::Dir::Left)),)),Key::Named(key::Named::ArrowRight) => Some(Msg::View(app::Msg::Cursor(cursor::Msg::PressDir(cursor::Dir::Right)),)),
Key::Named(key::Named::Enter) => {Some(Msg::View(app::Msg::Confirm))}Key::Named(key::Named::Escape) => {Some(Msg::View(app::Msg::Cancel))}Key::Named(key::Named::ArrowDown) => {Some(Msg::View(app::Msg::Selection(selection::Msg::PressDir(selection::Dir::Down),)))}Key::Named(key::Named::ArrowUp) => {Some(Msg::View(app::Msg::Selection(selection::Msg::PressDir(selection::Dir::Up),)))}Key::Named(key::Named::ArrowLeft) => {Some(Msg::View(app::Msg::Selection(selection::Msg::PressDir(selection::Dir::Left),)))}Key::Named(key::Named::ArrowRight) => {Some(Msg::View(app::Msg::Selection(selection::Msg::PressDir(selection::Dir::Right),)))}
Some(Msg::View(app::Msg::Cursor(cursor::Msg::ReleaseDir(cursor::Dir::Down,))))
Some(Msg::View(app::Msg::Selection(selection::Msg::ReleaseDir(selection::Dir::Down),)))}Key::Named(key::Named::ArrowUp) => {Some(Msg::View(app::Msg::Selection(selection::Msg::ReleaseDir(selection::Dir::Up),)))
let diff_nav_subs = match state.cursor.view.selection.as_ref() {Some(cursor::Selection::UntrackedFile {
let diff_nav_subs = match state.selection.view.status.as_ref() {Some(selection::Status::UntrackedFile {
Some(cursor::Selection::UntrackedFile { .. })| Some(cursor::Selection::ChangedFile { .. })| Some(cursor::Selection::LogChange { .. })
Some(selection::Status::UntrackedFile { .. })| Some(selection::Status::ChangedFile { .. })| Some(selection::Status::LogChange { .. })
let selection = match cursor.view.selection.as_ref() {Some(cursor::Selection::UntrackedFile {
let status_selection = match selection.view.status.as_ref() {Some(selection::Status::UntrackedFile {