authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-02 21:17:44-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-02 21:38:18-07:00
loge1eb16f90d89d57b6c0a1003cb2a9468c121c530
tree804f377636765446eb0a78d4bcb57ccedf3793af
parentc574cadb7f1d18457fb810169f3ec9cd230b9be6
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

fix: Open Shared Notebook sits in the sidebar's footer and beside Open Existing

With a notebook open, Open Shared Notebook was only in the File menu and the command palette (⌘⇧P), both of which work, but people looked where Open Existing is. The sidebar's footer now offers Open Shared… between Open Existing and Options, and the welcome screen lists it straight after Open Existing. Assisted-by: claude-opus-5.5

2 files changed, 50 insertions(+), 6 deletions(-)

crates/snowbound/src/sidebar.rs+17-6
......@@ -39,6 +39,8 @@ pub enum Action {
3939 Fold(String),
4040 NewNotebook,
4141 OpenNotebook,
42 #[cfg(feature = "live")]
43 OpenShared,
4244 /// Signs in again to the notebook opened from its server at this location.
4345 SignIn(String),
4446 Options,
......@@ -273,6 +275,13 @@ fn sidebar(
273275 "Open Existing",
274276 Action::OpenNotebook,
275277 ),
278 #[cfg(feature = "live")]
279 (
280 "shared",
281 Leading::Icon(art::LINK),
282 "Open Shared…",
283 Action::OpenShared,
284 ),
276285 (
277286 "options",
278287 Leading::Icon(art::OPTIONS),
......@@ -843,6 +852,8 @@ impl crate::State {
843852 Some(Action::Renamed(keep)) => self.finish_renaming(keep),
844853 Some(Action::NewNotebook) => self.commands.push(crate::Command::NewNotebook),
845854 Some(Action::OpenNotebook) => self.commands.push(crate::Command::OpenNotebook),
855 #[cfg(feature = "live")]
856 Some(Action::OpenShared) => self.open_shared(),
846857 Some(Action::SignIn(location)) => self
847858 .commands
848859 .push(crate::Command::OpenFromServer(Some(location))),
......@@ -1252,12 +1263,6 @@ impl crate::State {
12521263 "Open Existing",
12531264 crate::Command::OpenNotebook,
12541265 ),
1255 (
1256 "server",
1257 Leading::Icon(art::SERVER),
1258 "Open Notebook from Server…",
1259 crate::Command::OpenFromServer(None),
1260 ),
12611266 #[cfg(feature = "live")]
12621267 (
12631268 "shared",
......@@ -1265,6 +1270,12 @@ impl crate::State {
12651270 "Open Shared Notebook…",
12661271 crate::Command::OpenShared,
12671272 ),
1273 (
1274 "server",
1275 Leading::Icon(art::SERVER),
1276 "Open Notebook from Server…",
1277 crate::Command::OpenFromServer(None),
1278 ),
12681279 ]
12691280 .into_iter()
12701281 .chain(crate::guide::OFFERED.then_some((
crates/snowbound/tests/replay.rs+33
......@@ -549,3 +549,36 @@ fn stop_sharing_ends_the_share() {
549549 let kept = std::fs::read_to_string(kept).unwrap_or_default();
550550 assert!(!kept.contains("code"), "still kept: {kept}");
551551}
552
553/// Open Shared Notebook is where people look with notebooks open: in the sidebar's footer,
554/// and in the palette, which opens it.
555#[cfg(feature = "live")]
556#[test]
557fn open_shared_notebook_is_offered_with_notebooks_open() {
558 let scratch = Scratch::new("open-shared");
559 std::fs::write(
560 scratch.0.join("settings.json"),
561 r#"{"user_name": "Snowbound Test", "sidebar": true}"#,
562 )
563 .unwrap();
564 let notebook =
565 Path::new(env!("CARGO_MANIFEST_DIR")).join("../../corpus/cross-container/candidate");
566 let mut steps = vec!["accessibility sidebar", "modifiers command shift", "key p"];
567 steps.extend([
568 "modifiers",
569 "settle",
570 "type Open Shared",
571 "settle",
572 "key Enter",
573 ]);
574 steps.extend(["settle", "accessibility dialog"]);
575 let variables = [("SNOWBOUND_LIVE", "off"), ("SNOWBOUND_LIVE_RELAY", "off")];
576 let [sidebar, dialog] = replay_with(&scratch, Some(&notebook), &steps, &variables)
577 .try_into()
578 .unwrap();
579 assert!(sidebar.contains(r#""Open Shared…""#), "{sidebar}");
580 assert!(
581 dialog.contains(r#"Dialog "Open Shared Notebook""#),
582 "{dialog}"
583 );
584}