| author | |
| committer | |
| log | a4a0ccf055b14aa599b027e96a679054fea6b5a2 |
| tree | 2b104b885f46f98cc0eb5991d1b8a67bdaa4e801 |
| parent | 9361b752ce57b06d0f55af85dc5f515f794ea8f2 |
| signature | Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU |
Clover: no ellipses in action titles, on desktop, web or iOS. The command
table, context menus, dialog buttons and iOS menus name actions without
them; "…" stays only on progress text and truncated text.
Assisted-by: claude-opus-5.531 files changed, 100 insertions(+), 102 deletions(-)
apps/ios/Snowbound/Arranging.swift+2-2| ... | ... | @@ -342,7 +342,7 @@ extension PagesViewController: UITableViewDragDelegate, UITableViewDropDelegate |
| 342 | 342 | if indentable(rows, at) { add("Make Subpage") { [weak self] in self?.indent(id, by: 1) } } |
| 343 | 343 | if rows[at].level > 1 { add("Promote Subpage") { [weak self] in self?.indent(id, by: -1) } } |
| 344 | 344 | if !moveActions(item).isEmpty { |
| 345 | add("Move to Section…") { [weak self] in | |
| 345 | add("Move to Section") { [weak self] in | |
| 346 | 346 | guard let self, let row = items.firstIndex(where: { $0.id == id }), |
| 347 | 347 | let cell = tableView.cellForRow(at: IndexPath(row: row, section: 0)) |
| 348 | 348 | else { return } |
| ... | ... | @@ -571,7 +571,7 @@ extension NotebooksViewController: UICollectionViewDragDelegate, UICollectionVie |
| 571 | 571 | if at < order.count - 1 { add("Move Down") { [weak self] in self?.shift(path, of: notebook, by: 1) } } |
| 572 | 572 | } |
| 573 | 573 | if !notebook.destinations(of: path).isEmpty { |
| 574 | add("Move…") { [weak self, weak cell] in | |
| 574 | add("Move") { [weak self, weak cell] in | |
| 575 | 575 | guard let self, let cell else { return } |
| 576 | 576 | sheet("Move", moveActions(path, of: notebook), from: cell, in: self) |
| 577 | 577 | } |
apps/ios/Snowbound/Lists.swift+7-7| ... | ... | @@ -94,10 +94,10 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke |
| 94 | 94 | }) |
| 95 | 95 | var actions: [UIMenuElement] = [ |
| 96 | 96 | UIMenu(title: "New Notebook", options: .displayInline, children: places), |
| 97 | UIAction(title: "Open from Server…", image: UIImage(systemName: "server.rack")) { [weak self] _ in | |
| 97 | UIAction(title: "Open from Server", image: UIImage(systemName: "server.rack")) { [weak self] _ in | |
| 98 | 98 | self?.connect() |
| 99 | 99 | }, |
| 100 | UIAction(title: "Open Folder…", image: UIImage(systemName: "folder")) { [weak self] _ in | |
| 100 | UIAction(title: "Open Folder", image: UIImage(systemName: "folder")) { [weak self] _ in | |
| 101 | 101 | self?.openFolder() |
| 102 | 102 | }, |
| 103 | 103 | ] |
| ... | ... | @@ -114,7 +114,7 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke |
| 114 | 114 | let more = UIMenu(children: [ |
| 115 | 115 | UIDeferredMenuElement.uncached { [weak self] provide in |
| 116 | 116 | var actions: [UIMenuElement] = [ |
| 117 | UIAction(title: "Sync Status…", image: UIImage(systemName: "arrow.triangle.2.circlepath")) { | |
| 117 | UIAction(title: "Sync Status", image: UIImage(systemName: "arrow.triangle.2.circlepath")) { | |
| 118 | 118 | [weak self] _ in self.map(SyncViewController.present) |
| 119 | 119 | } |
| 120 | 120 | ] |
| ... | ... | @@ -125,7 +125,7 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke |
| 125 | 125 | }) |
| 126 | 126 | } |
| 127 | 127 | actions.append( |
| 128 | UIAction(title: "Personalize…", image: UIImage(systemName: "person.crop.circle")) { [weak self] _ in | |
| 128 | UIAction(title: "Personalize", image: UIImage(systemName: "person.crop.circle")) { [weak self] _ in | |
| 129 | 129 | guard let self else { return } |
| 130 | 130 | Author.ask(from: self) {} |
| 131 | 131 | }) |
| ... | ... | @@ -385,7 +385,7 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke |
| 385 | 385 | return [UIMenu(options: .displayInline, children: [details, now])] |
| 386 | 386 | } ?? [] |
| 387 | 387 | let actions = [ |
| 388 | UIAction(title: "New Section…", image: UIImage(systemName: "plus.rectangle.portrait")) { | |
| 388 | UIAction(title: "New Section", image: UIImage(systemName: "plus.rectangle.portrait")) { | |
| 389 | 389 | [weak self] _ in self?.newSection(in: notebook) |
| 390 | 390 | }, |
| 391 | 391 | UIAction(title: "Tags Summary", image: UIImage(systemName: "tag")) { [weak self] _ in |
| ... | ... | @@ -403,13 +403,13 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke |
| 403 | 403 | return UIMenu(children: status + actions + [close]) |
| 404 | 404 | } |
| 405 | 405 | let file = [ |
| 406 | UIAction(title: "Rename…", image: UIImage(systemName: "pencil")) { [weak self] _ in | |
| 406 | UIAction(title: "Rename", image: UIImage(systemName: "pencil")) { [weak self] _ in | |
| 407 | 407 | self?.rename(folder, of: notebook) |
| 408 | 408 | }, |
| 409 | 409 | UIAction(title: "Duplicate", image: UIImage(systemName: "plus.square.on.square")) { [weak self] _ in |
| 410 | 410 | self?.duplicate(folder, of: notebook) |
| 411 | 411 | }, |
| 412 | UIAction(title: "Share…", image: UIImage(systemName: "square.and.arrow.up")) { [weak self] _ in | |
| 412 | UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up")) { [weak self] _ in | |
| 413 | 413 | self?.share(folder, from: notebook) |
| 414 | 414 | }, |
| 415 | 415 | UIAction(title: "Show in Files", image: UIImage(systemName: "folder")) { _ in |
apps/ios/Snowbound/PageViewController.swift+3-3| ... | ... | @@ -202,7 +202,7 @@ final class PageViewController: UIViewController, PHPickerViewControllerDelegate |
| 202 | 202 | ]) |
| 203 | 203 | return (Prototype.reading ? [readingAction()] : []) + [ |
| 204 | 204 | insert, |
| 205 | UIAction(title: "Page Background…", image: UIImage(systemName: "paintpalette"), attributes: editable) { | |
| 205 | UIAction(title: "Page Background", image: UIImage(systemName: "paintpalette"), attributes: editable) { | |
| 206 | 206 | [weak self] _ in self?.showPaper() |
| 207 | 207 | }, |
| 208 | 208 | themeMenu(), |
| ... | ... | @@ -211,10 +211,10 @@ final class PageViewController: UIViewController, PHPickerViewControllerDelegate |
| 211 | 211 | scene?.showTags(of: section.notebook, from: self) |
| 212 | 212 | }, |
| 213 | 213 | UIMenu(options: .displayInline, children: [ |
| 214 | UIAction(title: "Print…", image: UIImage(systemName: "printer")) { [weak self] _ in | |
| 214 | UIAction(title: "Print", image: UIImage(systemName: "printer")) { [weak self] _ in | |
| 215 | 215 | self?.printPage(export: false) |
| 216 | 216 | }, |
| 217 | UIAction(title: "Export as PDF…", image: UIImage(systemName: "doc.richtext")) { [weak self] _ in | |
| 217 | UIAction(title: "Export as PDF", image: UIImage(systemName: "doc.richtext")) { [weak self] _ in | |
| 218 | 218 | self?.printPage(export: true) |
| 219 | 219 | }, |
| 220 | 220 | ]), |
apps/ios/Snowbound/Prototypes.swift+2-2| ... | ... | @@ -52,8 +52,8 @@ extension Prototype { |
| 52 | 52 | open.title = "Open Existing Notebook" |
| 53 | 53 | welcome.secondaryButton = open |
| 54 | 54 | var elsewhere = [ |
| 55 | UIAction(title: "Open Folder…", image: UIImage(systemName: "folder")) { _ in openFolder() }, | |
| 56 | UIAction(title: "Connect to Server…", image: UIImage(systemName: "server.rack")) { _ in connect() }, | |
| 55 | UIAction(title: "Open Folder", image: UIImage(systemName: "folder")) { _ in openFolder() }, | |
| 56 | UIAction(title: "Connect to Server", image: UIImage(systemName: "server.rack")) { _ in connect() }, | |
| 57 | 57 | ] |
| 58 | 58 | if !ICloud.signedIn { |
| 59 | 59 | elsewhere.append( |
arc/platforms.md+2-2| ... | ... | @@ -52,7 +52,7 @@ keyboard, the toolbar and the macOS menu bar all run commands from it. |
| 52 | 52 | frame with the window's new size instead of stretching the last one. |
| 53 | 53 | - A notebook on a mounted SMB share is opened through the embedded SMB client, |
| 54 | 54 | signed in with the password the keychain keeps for that mount (see |
| 55 | [sync](sync.md) for why the mount itself isn't enough). Open Notebook from Server… reaches one | |
| 55 | [sync](sync.md) for why the mount itself isn't enough). Open Notebook from Server reaches one | |
| 56 | 56 | by address instead, and keeps a password it asks to remember as the Finder does, an |
| 57 | 57 | SMB internet password. |
| 58 | 58 | - The menu bar (`menubar.rs`) is laid out as OneNote for Mac's. Its items are |
| ... | ... | @@ -147,7 +147,7 @@ keyboard, the toolbar and the macOS menu bar all run commands from it. |
| 147 | 147 | other writers) and takes OneNote's coordination bytes with byte-range |
| 148 | 148 | locks, so both apps can have a section open on one machine. |
| 149 | 149 | ReadDirectoryChangesW reports changes below a notebook, including other |
| 150 | clients' on a share. Open Notebook from Server… still uses the embedded | |
| 150 | clients' on a share. Open Notebook from Server still uses the embedded | |
| 151 | 151 | client, its passwords in the Credential Manager. |
| 152 | 152 | - The common file dialogs, task dialogs, the date and time picker, and the |
| 153 | 153 | Spell Checking API (from Windows 8) are the system's; audio plays and |
arc/sync.md+1-1| ... | ... | @@ -192,7 +192,7 @@ client with the account the system keeps for that mount. It falls back to the |
| 192 | 192 | mount only when it can't sign in that way. |
| 193 | 193 | |
| 194 | 194 | A notebook can also be opened from its server's address, with no mount at all (File ▸ |
| 195 | Open Notebook from Server…), which is how Mac OS X 10.6 reaches a server that no longer | |
| 195 | Open Notebook from Server), which is how Mac OS X 10.6 reaches a server that no longer | |
| 196 | 196 | speaks SMB1, the only version its Finder has. The notebook is kept by that address |
| 197 | 197 | (`smb://[domain;]user@server/share/folder`), never with a password; the password lives in |
| 198 | 198 | the keychain (the Secret Service on Linux) when the user asks to remember it, and |
corpus/page-versions/README.md+1-1| ... | ... | @@ -47,7 +47,7 @@ Versions is chosen. An open version is read-only under a yellow bar (RGB 255, 23 |
| 47 | 47 | "This is an earlier version of the page. It will be deleted over time. Click here to restore |
| 48 | 48 | or delete this version." What the version changed since the one before it is banded green |
| 49 | 49 | (RGB 214, 255, 214). The bar's menu (`shots/02-version-menu.png`): Restore Version, Delete |
| 50 | Version, Copy Page To…; Delete All Versions in Section, Section Group, Notebook; Disable | |
| 50 | Version, Copy Page To; Delete All Versions in Section, Section Group, Notebook; Disable | |
| 51 | 51 | History for This Notebook; Hide Page Versions. Share also has Hide Authors, Recent Edits and |
| 52 | 52 | Find by Author (`shots/14`–`16`). |
| 53 | 53 |
corpus/section-color/README.md+1-1| ... | ... | @@ -6,7 +6,7 @@ ends with Section Color, sixteen named colours and None (`section-color-menu.png |
| 6 | 6 | appends a revision to the section's own file setting its metadata's `0x14001cbe` |
| 7 | 7 | (COLORREF; `teal.one`), and None sets `0xFFFFFFFF` (`none.one`); the table of contents is |
| 8 | 8 | left byte for byte. Notebook Properties (`notebook-properties.png`, from the notebook's |
| 9 | Rename… or Properties…) offers the same sixteen without None (`notebook-colors.png`) and | |
| 9 | Rename or Properties) offers the same sixteen without None (`notebook-colors.png`) and | |
| 10 | 10 | appends a revision to the root `Open Notebook.onetoc2` setting its root's `0x14001cbe` |
| 11 | 11 | (`before.onetoc2`, `after.onetoc2`). Its Display name is written to neither file: OneNote |
| 12 | 12 | keeps it in its local `OneNoteOfflineCache.onecache`, so the hierarchy's `nickname` |
crates/snowbound/linux/README.md+1-1| ... | ... | @@ -11,7 +11,7 @@ cd snowbound-linux-x86_64 |
| 11 | 11 | ./bin/snowbound |
| 12 | 12 | ``` |
| 13 | 13 | |
| 14 | Choose **Open Notebook…** in the notebook list and pick the notebook folder, | |
| 14 | Choose **Open Notebook** in the notebook list and pick the notebook folder, | |
| 15 | 15 | the one holding the `.one` section files and `Open Notebook.onetoc2`. |
| 16 | 16 | Snowbound remembers the notebooks you open. `--notebook FOLDER` opens one |
| 17 | 17 | straight away. |
crates/snowbound/src/background.rs+1-1| ... | ... | @@ -161,7 +161,7 @@ pub fn menu( |
| 161 | 161 | } |
| 162 | 162 | match cells[index] { |
| 163 | 163 | Cell::Color(None) | Cell::Art(None) | Cell::Rules(None) => label(ui, "None"), |
| 164 | Cell::ShowAll => label(ui, "Show All…"), | |
| 164 | Cell::ShowAll => label(ui, "Show All"), | |
| 165 | 165 | Cell::Color(Some(color)) => { |
| 166 | 166 | ui.leaf( |
| 167 | 167 | "swatch", |
crates/snowbound/src/commands.rs+20-20| ... | ... | @@ -260,12 +260,12 @@ const NONE: &[Chord] = &[]; |
| 260 | 260 | |
| 261 | 261 | /// OneNote 2010's chords off macOS; there, AppKit's and OneNote for Mac's. |
| 262 | 262 | pub const COMMANDS: &[Command] = &[ |
| 263 | row!(Id::Settings, "Settings…", &[cmd(',')], &[cmd(',')]), | |
| 264 | row!(Id::NewNotebook, "New Notebook…", NONE, NONE), | |
| 265 | row!(Id::OpenNotebook, "Open Notebook…", &[cmd('o')], &[cmd('o')]), | |
| 266 | row!(Id::OpenFromServer, "Open Notebook from Server…", NONE, NONE), | |
| 267 | row!(Id::OpenShared, "Open Shared Notebook…", NONE, NONE), | |
| 268 | row!(Id::LiveShare, "Live Share…", NONE, NONE), | |
| 263 | row!(Id::Settings, "Settings", &[cmd(',')], &[cmd(',')]), | |
| 264 | row!(Id::NewNotebook, "New Notebook", NONE, NONE), | |
| 265 | row!(Id::OpenNotebook, "Open Notebook", &[cmd('o')], &[cmd('o')]), | |
| 266 | row!(Id::OpenFromServer, "Open Notebook from Server", NONE, NONE), | |
| 267 | row!(Id::OpenShared, "Open Shared Notebook", NONE, NONE), | |
| 268 | row!(Id::LiveShare, "Live Share", NONE, NONE), | |
| 269 | 269 | row!(Id::CloseNotebook, "Close This Notebook", NONE, NONE), |
| 270 | 270 | row!(Id::NewSection, "New Section", &[cmd('t')], &[cmd('t')]), |
| 271 | 271 | row!(Id::NewSectionGroup, "New Section Group", NONE, NONE), |
| ... | ... | @@ -280,7 +280,7 @@ pub const COMMANDS: &[Command] = &[ |
| 280 | 280 | row!(Id::CopyPageLink, "Copy Link to Page", NONE, NONE), |
| 281 | 281 | row!( |
| 282 | 282 | Id::PasswordProtect, |
| 283 | "Password Protect This Section…", | |
| 283 | "Password Protect This Section", | |
| 284 | 284 | NONE, |
| 285 | 285 | NONE |
| 286 | 286 | ), |
| ... | ... | @@ -302,7 +302,7 @@ pub const COMMANDS: &[Command] = &[ |
| 302 | 302 | ), |
| 303 | 303 | row!( |
| 304 | 304 | Id::SaveAs, |
| 305 | "Save As…", | |
| 305 | "Save As", | |
| 306 | 306 | &[cmd('s').shift()], |
| 307 | 307 | &[cmd('s').shift()] |
| 308 | 308 | ), |
| ... | ... | @@ -318,11 +318,11 @@ pub const COMMANDS: &[Command] = &[ |
| 318 | 318 | NONE |
| 319 | 319 | ), |
| 320 | 320 | row!(Id::NextUnread, "Next Unread", NONE, NONE), |
| 321 | row!(Id::ExportPdf, "Export as PDF…", NONE, NONE), | |
| 321 | row!(Id::ExportPdf, "Export as PDF", NONE, NONE), | |
| 322 | 322 | // Go to takes OneNote's Ctrl+P, and Pause its Ctrl+Alt+P. |
| 323 | 323 | row!( |
| 324 | 324 | Id::Print, |
| 325 | "Print…", | |
| 325 | "Print", | |
| 326 | 326 | &[cmd('p').option().shift()], |
| 327 | 327 | &[cmd('p').option().shift()] |
| 328 | 328 | ), |
| ... | ... | @@ -351,10 +351,10 @@ pub const COMMANDS: &[Command] = &[ |
| 351 | 351 | &[cmd('f').option()], |
| 352 | 352 | &[key('o').option()], |
| 353 | 353 | ), |
| 354 | row!(Id::GoTo, "Go to Page or Section…", &[cmd('p')], &[cmd('p')]), | |
| 354 | row!(Id::GoTo, "Go to Page or Section", &[cmd('p')], &[cmd('p')]), | |
| 355 | 355 | row!( |
| 356 | 356 | Id::CommandPalette, |
| 357 | "Command Palette…", | |
| 357 | "Command Palette", | |
| 358 | 358 | &[cmd('p').shift()], |
| 359 | 359 | &[cmd('p').shift()] |
| 360 | 360 | ), |
| ... | ... | @@ -393,7 +393,7 @@ pub const COMMANDS: &[Command] = &[ |
| 393 | 393 | row!(Id::MarkdownShortcuts, "Markdown Shortcuts", NONE, NONE), |
| 394 | 394 | row!( |
| 395 | 395 | Id::Spelling, |
| 396 | "Spelling…", | |
| 396 | "Spelling", | |
| 397 | 397 | &[named(NamedKey::F7)], |
| 398 | 398 | &[named(NamedKey::F7)] |
| 399 | 399 | ), |
| ... | ... | @@ -406,10 +406,10 @@ pub const COMMANDS: &[Command] = &[ |
| 406 | 406 | ), |
| 407 | 407 | row!(Id::PageColor, "Page Color", NONE, NONE), |
| 408 | 408 | row!(Id::Table, "Table", NONE, NONE), |
| 409 | row!(Id::Picture, "Picture…", NONE, NONE), | |
| 409 | row!(Id::Picture, "Picture", NONE, NONE), | |
| 410 | 410 | row!(Id::ScreenClipping, "Screen Clipping", NONE, NONE), |
| 411 | row!(Id::Attachment, "Attach File…", NONE, NONE), | |
| 412 | row!(Id::Link, "Link…", &[cmd('k')], &[cmd('k')]), | |
| 411 | row!(Id::Attachment, "Attach File", NONE, NONE), | |
| 412 | row!(Id::Link, "Link", &[cmd('k')], &[cmd('k')]), | |
| 413 | 413 | row!(Id::InsertSpace, "Insert Space", NONE, NONE), |
| 414 | 414 | row!( |
| 415 | 415 | Id::Equation, |
| ... | ... | @@ -465,7 +465,7 @@ pub const COMMANDS: &[Command] = &[ |
| 465 | 465 | NONE, |
| 466 | 466 | NONE |
| 467 | 467 | ), |
| 468 | row!(Id::Transport(Transport::SeekTo), "Seek To…", NONE, NONE), | |
| 468 | row!(Id::Transport(Transport::SeekTo), "Seek To", NONE, NONE), | |
| 469 | 469 | row!( |
| 470 | 470 | Id::Transport(Transport::SeePlayback), |
| 471 | 471 | "See Playback", |
| ... | ... | @@ -596,8 +596,8 @@ pub const COMMANDS: &[Command] = &[ |
| 596 | 596 | row!(Id::Style(8), "Quote", NONE, NONE), |
| 597 | 597 | row!(Id::Style(9), "Code", NONE, NONE), |
| 598 | 598 | row!(Id::Style(10), "Normal", NONE, NONE), |
| 599 | row!(Id::Themes, "Customize Theme…", NONE, NONE), | |
| 600 | row!(Id::CustomizeTags, "Customize Tags…", NONE, NONE), | |
| 599 | row!(Id::Themes, "Customize Theme", NONE, NONE), | |
| 600 | row!(Id::CustomizeTags, "Customize Tags", NONE, NONE), | |
| 601 | 601 | row!( |
| 602 | 602 | Id::RemoveTags, |
| 603 | 603 | "Remove Tag", |
| ... | ... | @@ -608,7 +608,7 @@ pub const COMMANDS: &[Command] = &[ |
| 608 | 608 | row!(Id::ToDoList, "Make To-Do List", NONE, NONE), |
| 609 | 609 | row!(Id::BulletedList, "Make Bulleted List", NONE, NONE), |
| 610 | 610 | row!(Id::Help, "Snowbound Help", NONE, NONE), |
| 611 | row!(Id::CheckForUpdates, "Check for Updates…", NONE, NONE), | |
| 611 | row!(Id::CheckForUpdates, "Check for Updates", NONE, NONE), | |
| 612 | 612 | ]; |
| 613 | 613 | |
| 614 | 614 | /// The most tags a list holds: MS-ONE's action types 0 to 99 number them. |
crates/snowbound/src/history.rs+1-1| ... | ... | @@ -105,7 +105,7 @@ pub fn menu( |
| 105 | 105 | drawn("Delete Version", art::DELETE), |
| 106 | 106 | ui::popup::Item { |
| 107 | 107 | disabled: sections.is_empty(), |
| 108 | ..drawn("Copy Page To…", art::COPY) | |
| 108 | ..drawn("Copy Page To", art::COPY) | |
| 109 | 109 | }, |
| 110 | 110 | ui::popup::Item { |
| 111 | 111 | separated: true, |
crates/snowbound/src/keys.rs+1-1| ... | ... | @@ -78,7 +78,7 @@ fn label(chord: Chord) -> String { |
| 78 | 78 | fn title(id: Command) -> String { |
| 79 | 79 | match id { |
| 80 | 80 | Command::Tag(place) => format!("Tag {}", place + 1), |
| 81 | id => crate::title(id).to_owned(), | |
| 81 | id => commands::command(id).title.to_owned(), | |
| 82 | 82 | } |
| 83 | 83 | } |
| 84 | 84 |
crates/snowbound/src/link.rs+8-8| ... | ... | @@ -303,7 +303,7 @@ impl State { |
| 303 | 303 | item("Ignore"), |
| 304 | 304 | Item { |
| 305 | 305 | separated: true, |
| 306 | ..drawn("Spelling…", art::SPELLING) | |
| 306 | ..drawn("Spelling", art::SPELLING) | |
| 307 | 307 | }, |
| 308 | 308 | ], |
| 309 | 309 | Some(correction) => { |
| ... | ... | @@ -326,7 +326,7 @@ impl State { |
| 326 | 326 | drawn("Add to Dictionary", art::ADD_TO_DICTIONARY), |
| 327 | 327 | Item { |
| 328 | 328 | separated: true, |
| 329 | ..drawn("Spelling…", art::SPELLING) | |
| 329 | ..drawn("Spelling", art::SPELLING) | |
| 330 | 330 | }, |
| 331 | 331 | ]); |
| 332 | 332 | items |
| ... | ... | @@ -344,7 +344,7 @@ impl State { |
| 344 | 344 | .collect::<Vec<_>>(); |
| 345 | 345 | items.extend(if context.attachment.is_some() { |
| 346 | 346 | // OneNote 2010's commands for the file itself; its clipboard holds text alone. |
| 347 | vec![drawn("Open", art::OPEN), drawn("Save As…", art::SAVE)] | |
| 347 | vec![drawn("Open", art::OPEN), drawn("Save As", art::SAVE)] | |
| 348 | 348 | } else { |
| 349 | 349 | let mut items = vec![ |
| 350 | 350 | Item { |
| ... | ... | @@ -362,7 +362,7 @@ impl State { |
| 362 | 362 | Some(_) => items.extend([ |
| 363 | 363 | Item { |
| 364 | 364 | separated: true, |
| 365 | ..drawn("Edit Link…", art::LINK) | |
| 365 | ..drawn("Edit Link", art::LINK) | |
| 366 | 366 | }, |
| 367 | 367 | drawn("Copy Link to Paragraph", art::COPY_LINK), |
| 368 | 368 | Item { |
| ... | ... | @@ -375,7 +375,7 @@ impl State { |
| 375 | 375 | None => items.extend([ |
| 376 | 376 | Item { |
| 377 | 377 | separated: true, |
| 378 | ..drawn("Link…", art::LINK) | |
| 378 | ..drawn("Link", art::LINK) | |
| 379 | 379 | }, |
| 380 | 380 | drawn("Copy Link to Paragraph", art::COPY_LINK), |
| 381 | 381 | ]), |
| ... | ... | @@ -414,7 +414,7 @@ impl State { |
| 414 | 414 | self.view.correct(&correction, &chosen_text)? |
| 415 | 415 | } |
| 416 | 416 | "Delete Repeated Word" => self.view.correct(&correction, "")?, |
| 417 | "Spelling…" => { | |
| 417 | "Spelling" => { | |
| 418 | 418 | self.open_spelling_pane(); |
| 419 | 419 | return Ok(()); |
| 420 | 420 | } |
| ... | ... | @@ -441,7 +441,7 @@ impl State { |
| 441 | 441 | } |
| 442 | 442 | let response = match chosen_text.as_str() { |
| 443 | 443 | "Open" => return self.open_attachment(&context.attachment.unwrap()), |
| 444 | "Save As…" => return self.save_attachment(&context.attachment.unwrap()), | |
| 444 | "Save As" => return self.save_attachment(&context.attachment.unwrap()), | |
| 445 | 445 | "Cut" => self.view.copy(true)?, |
| 446 | 446 | "Copy" => self.view.copy(false)?, |
| 447 | 447 | "Paste" => { |
| ... | ... | @@ -449,7 +449,7 @@ impl State { |
| 449 | 449 | .push(Command::Page(canvas::interaction::Request::Paste)); |
| 450 | 450 | return Ok(()); |
| 451 | 451 | } |
| 452 | "Edit Link…" | "Link…" => { | |
| 452 | "Edit Link" | "Link" => { | |
| 453 | 453 | self.open_link_dialog(); |
| 454 | 454 | return Ok(()); |
| 455 | 455 | } |
crates/snowbound/src/main.rs+15-13| ... | ... | @@ -2413,7 +2413,7 @@ impl State { |
| 2413 | 2413 | } else if ui::shell::split_button( |
| 2414 | 2414 | ui, |
| 2415 | 2415 | part, |
| 2416 | title(id), | |
| 2416 | commands::command(id).title, | |
| 2417 | 2417 | icon, |
| 2418 | 2418 | bar, |
| 2419 | 2419 | None, |
| ... | ... | @@ -2496,7 +2496,7 @@ impl State { |
| 2496 | 2496 | } else if ui::shell::split_button( |
| 2497 | 2497 | ui, |
| 2498 | 2498 | part, |
| 2499 | title(id), | |
| 2499 | commands::command(id).title, | |
| 2500 | 2500 | icon, |
| 2501 | 2501 | None, |
| 2502 | 2502 | status.checked, |
| ... | ... | @@ -2798,7 +2798,7 @@ impl State { |
| 2798 | 2798 | if ui::shell::split_button( |
| 2799 | 2799 | ui, |
| 2800 | 2800 | "pen", |
| 2801 | title(Cmd::Pen), | |
| 2801 | commands::command(Cmd::Pen).title, | |
| 2802 | 2802 | art::PEN, |
| 2803 | 2803 | bar, |
| 2804 | 2804 | status.checked, |
| ... | ... | @@ -4724,7 +4724,7 @@ fn conflict_bar(ui: &mut Ui, bar: Bar, sections: &[&str], steps: [bool; 2]) -> O |
| 4724 | 4724 | ..Default::default() |
| 4725 | 4725 | }, |
| 4726 | 4726 | ui::popup::Item { |
| 4727 | text: "Copy Page To…", | |
| 4727 | text: "Copy Page To", | |
| 4728 | 4728 | icon: Some(art::COPY), |
| 4729 | 4729 | disabled: sections.is_empty(), |
| 4730 | 4730 | ..Default::default() |
| ... | ... | @@ -5349,7 +5349,9 @@ fn dropdown( |
| 5349 | 5349 | Head::Split(id) if status_of(id).enabled => { |
| 5350 | 5350 | let icon = artwork(id).unwrap_or_default(); |
| 5351 | 5351 | let on = status_of(id).checked; |
| 5352 | if ui::shell::split_button(ui, part, title(id), icon, None, on, menu).clicked { | |
| 5352 | if ui::shell::split_button(ui, part, commands::command(id).title, icon, None, on, menu) | |
| 5353 | .clicked | |
| 5354 | { | |
| 5353 | 5355 | choice = Some(Choice::Command(id)); |
| 5354 | 5356 | } |
| 5355 | 5357 | tip(ui, id); |
| ... | ... | @@ -5368,7 +5370,7 @@ fn dropdown( |
| 5368 | 5370 | Head::Split(id) => { |
| 5369 | 5371 | let icon = artwork(id).unwrap_or_default(); |
| 5370 | 5372 | let anchor = ui::shell::menu_button(ui, part, icon, None, menu); |
| 5371 | name(ui, button, title(id)); | |
| 5373 | name(ui, button, commands::command(id).title); | |
| 5372 | 5374 | anchor |
| 5373 | 5375 | } |
| 5374 | 5376 | Head::Tag(_, tag) => { |
| ... | ... | @@ -5422,7 +5424,7 @@ fn dropdown( |
| 5422 | 5424 | let options: Vec<_> = entries |
| 5423 | 5425 | .iter() |
| 5424 | 5426 | .map(|entry| match *entry { |
| 5425 | Entry::Open(_, id) if runs(id) => format!("{} Options", title(id)), | |
| 5427 | Entry::Open(_, id) if runs(id) => format!("{} Options", commands::command(id).title), | |
| 5426 | 5428 | _ => String::new(), |
| 5427 | 5429 | }) |
| 5428 | 5430 | .collect(); |
| ... | ... | @@ -5723,12 +5725,12 @@ fn tag_tip(ui: &mut Ui, place: usize, tag: &canvas::editor::NoteTag) { |
| 5723 | 5725 | |
| 5724 | 5726 | /// A tooltip naming command `id` and its chord on the box built last. |
| 5725 | 5727 | fn tip(ui: &mut Ui, id: commands::Id) { |
| 5726 | ui::popup::tooltip(ui, title(id), &commands::shortcut(id), None); | |
| 5727 | } | |
| 5728 | ||
| 5729 | /// Command `id`'s title without the menu's ellipsis, naming the command, not a dialog it opens. | |
| 5730 | fn title(id: commands::Id) -> &'static str { | |
| 5731 | commands::command(id).title.trim_end_matches('…') | |
| 5728 | ui::popup::tooltip( | |
| 5729 | ui, | |
| 5730 | commands::command(id).title, | |
| 5731 | &commands::shortcut(id), | |
| 5732 | None, | |
| 5733 | ); | |
| 5732 | 5734 | } |
| 5733 | 5735 | |
| 5734 | 5736 | /// Whether each toolbar group stands on a face of its own. |
crates/snowbound/src/menubar.rs+1-1| ... | ... | @@ -102,7 +102,7 @@ const MENUS: &[Item] = &[ |
| 102 | 102 | C(Id::Search), |
| 103 | 103 | C(Id::SearchResults), |
| 104 | 104 | S, |
| 105 | Item::System("Start Dictation…", "startDictation:", None), | |
| 105 | Item::System("Start Dictation", "startDictation:", None), | |
| 106 | 106 | Item::System( |
| 107 | 107 | "Emoji & Symbols", |
| 108 | 108 | "orderFrontCharacterPalette:", |
crates/snowbound/src/menus.rs+9-9| ... | ... | @@ -342,7 +342,7 @@ impl State { |
| 342 | 342 | item(Action::Copy, "Copy To", nowhere(), false), |
| 343 | 343 | ], |
| 344 | 344 | Target::Section { path, .. } if crate::recycle::binned(path) => vec![ |
| 345 | item(Action::SaveAs, "Save As…", false, false), | |
| 345 | item(Action::SaveAs, "Save As", false, false), | |
| 346 | 346 | item(Action::Move, "Restore To", nowhere(), false), |
| 347 | 347 | item(Action::EmptyRecycleBin, "Empty Recycle Bin", false, true), |
| 348 | 348 | item(Action::Colors, "Section Color", false, true), |
| ... | ... | @@ -360,7 +360,7 @@ impl State { |
| 360 | 360 | item(Action::NewPage, "New Page", false, true), |
| 361 | 361 | // OneNote offers this beside New Page, which is a plain + here. |
| 362 | 362 | item(Action::NewSubpage, "New Subpage", false, false), |
| 363 | item(Action::Theme, "Theme…", false, true), | |
| 363 | item(Action::Theme, "Theme", false, true), | |
| 364 | 364 | ]; |
| 365 | 365 | // Levels and versions are known once the section is open. |
| 366 | 366 | let Some(session) = self.session.as_ref().filter(|_| self.open(library, path)) |
| ... | ... | @@ -400,7 +400,7 @@ impl State { |
| 400 | 400 | let section = matches!(target, Target::Section { .. }); |
| 401 | 401 | let mut actions = vec![item(Action::Rename, "Rename", false, false)]; |
| 402 | 402 | if section { |
| 403 | actions.push(item(Action::SaveAs, "Save As…", false, false)); | |
| 403 | actions.push(item(Action::SaveAs, "Save As", false, false)); | |
| 404 | 404 | } |
| 405 | 405 | actions.extend([ |
| 406 | 406 | item(Action::Delete, "Delete", false, false), |
| ... | ... | @@ -412,7 +412,7 @@ impl State { |
| 412 | 412 | actions.extend([ |
| 413 | 413 | item( |
| 414 | 414 | Action::Password, |
| 415 | "Password Protect This Section…", | |
| 415 | "Password Protect This Section", | |
| 416 | 416 | library.catalog().is_none(), |
| 417 | 417 | true, |
| 418 | 418 | ), |
| ... | ... | @@ -423,7 +423,7 @@ impl State { |
| 423 | 423 | section_file(library, path).is_none(), |
| 424 | 424 | false, |
| 425 | 425 | ), |
| 426 | item(Action::Theme, "Theme…", false, false), | |
| 426 | item(Action::Theme, "Theme", false, false), | |
| 427 | 427 | ]); |
| 428 | 428 | } |
| 429 | 429 | actions |
| ... | ... | @@ -433,10 +433,10 @@ impl State { |
| 433 | 433 | let last = self.notebooks.len().saturating_sub(1); |
| 434 | 434 | let unread = self.unread_notebook(library); |
| 435 | 435 | vec![ |
| 436 | item(Action::Rename, "Rename…", false, false), | |
| 436 | item(Action::Rename, "Rename", false, false), | |
| 437 | 437 | item( |
| 438 | 438 | Action::SaveAs, |
| 439 | "Save As…", | |
| 439 | "Save As", | |
| 440 | 440 | library.catalog().is_none(), |
| 441 | 441 | false, |
| 442 | 442 | ), |
| ... | ... | @@ -493,7 +493,7 @@ impl State { |
| 493 | 493 | library.folder().is_none(), |
| 494 | 494 | true, |
| 495 | 495 | ), |
| 496 | item(Action::Theme, "Theme…", false, false), | |
| 496 | item(Action::Theme, "Theme", false, false), | |
| 497 | 497 | item( |
| 498 | 498 | Action::RecycleBin, |
| 499 | 499 | "Notebook Recycle Bin", |
| ... | ... | @@ -502,7 +502,7 @@ impl State { |
| 502 | 502 | ), |
| 503 | 503 | item( |
| 504 | 504 | Action::Properties, |
| 505 | "Properties…", | |
| 505 | "Properties", | |
| 506 | 506 | library.catalog().is_none(), |
| 507 | 507 | false, |
| 508 | 508 | ), |
crates/snowbound/src/options.rs+1-1| ... | ... | @@ -992,7 +992,7 @@ fn installed(state: &mut State, _: &mut Options) { |
| 992 | 992 | let ui = &mut state.ui; |
| 993 | 993 | if crate::desktop::uninstallable() { |
| 994 | 994 | path(ui, &crate::desktop::binary().unwrap_or_default()); |
| 995 | if ui::button(ui, "uninstall", "Uninstall…").clicked { | |
| 995 | if ui::button(ui, "uninstall", "Uninstall").clicked { | |
| 996 | 996 | crate::desktop::uninstall(&state.proxy); |
| 997 | 997 | } |
| 998 | 998 | } else if crate::desktop::installable() { |
crates/snowbound/src/print.rs+1-5| ... | ... | @@ -257,11 +257,7 @@ impl State { |
| 257 | 257 | }); |
| 258 | 258 | crate::buttons(ui); |
| 259 | 259 | let cancel = ui::button(ui, "cancel", "Cancel").clicked; |
| 260 | let action = if dialog.export { | |
| 261 | "Export…" | |
| 262 | } else { | |
| 263 | "Print…" | |
| 264 | }; | |
| 260 | let action = if dialog.export { "Export" } else { "Print" }; | |
| 265 | 261 | let go = ui::button(ui, "go", action).clicked || entered; |
| 266 | 262 | ui.close(); |
| 267 | 263 | ui.close(); |
crates/snowbound/src/protection.rs+5-5| ... | ... | @@ -442,9 +442,9 @@ impl State { |
| 442 | 442 | }; |
| 443 | 443 | text(ui, "status", &status, false); |
| 444 | 444 | buttons = if protected { |
| 445 | &["Change Password…", "Remove Password…", "Lock All", "Close"] | |
| 445 | &["Change Password", "Remove Password", "Lock All", "Close"] | |
| 446 | 446 | } else { |
| 447 | &["Set Password…", "Lock All", "Close"] | |
| 447 | &["Set Password", "Lock All", "Close"] | |
| 448 | 448 | }; |
| 449 | 449 | } |
| 450 | 450 | Dialog::Set([value, confirm], refused) => { |
| ... | ... | @@ -527,9 +527,9 @@ impl State { |
| 527 | 527 | self.ui.close_popup(id()); |
| 528 | 528 | return self.lock_all(); |
| 529 | 529 | } |
| 530 | (Some("Set Password…"), _) => Some(Dialog::Set(Default::default(), None)), | |
| 531 | (Some("Change Password…"), _) => Some(Dialog::Change(Default::default(), None)), | |
| 532 | (Some("Remove Password…"), _) => Some(Dialog::Remove(Zeroizing::default(), false)), | |
| 530 | (Some("Set Password"), _) => Some(Dialog::Set(Default::default(), None)), | |
| 531 | (Some("Change Password"), _) => Some(Dialog::Change(Default::default(), None)), | |
| 532 | (Some("Remove Password"), _) => Some(Dialog::Remove(Zeroizing::default(), false)), | |
| 533 | 533 | (Some(_), Dialog::Unlock(value, _)) => match library.unlock(&path, &value) { |
| 534 | 534 | Ok(()) => { |
| 535 | 535 | self.commands |
crates/snowbound/src/recording.rs+1-1| ... | ... | @@ -495,7 +495,7 @@ impl State { |
| 495 | 495 | let title = |
| 496 | 496 | crate::commands::command(crate::commands::Id::Transport(Transport::SeekTo)) |
| 497 | 497 | .title; |
| 498 | node.set_label(title.trim_end_matches('…')); | |
| 498 | node.set_label(title); | |
| 499 | 499 | node.set_value(clock_text); |
| 500 | 500 | } |
| 501 | 501 | if clicked { |
crates/snowbound/src/save_as.rs+1-1| ... | ... | @@ -135,7 +135,7 @@ impl State { |
| 135 | 135 | } |
| 136 | 136 | crate::buttons(ui); |
| 137 | 137 | let cancel = ui::button(ui, "cancel", "Cancel").clicked; |
| 138 | let go = ui::button(ui, "go", "Save As…").clicked || entered; | |
| 138 | let go = ui::button(ui, "go", "Save As").clicked || entered; | |
| 139 | 139 | ui.close(); |
| 140 | 140 | ui.close(); |
| 141 | 141 | if !go && !cancel { |
crates/snowbound/src/sidebar.rs+3-3| ... | ... | @@ -279,7 +279,7 @@ fn sidebar( |
| 279 | 279 | ( |
| 280 | 280 | "shared", |
| 281 | 281 | Leading::Icon(art::LINK), |
| 282 | "Open Shared…", | |
| 282 | "Open Shared", | |
| 283 | 283 | Action::OpenShared, |
| 284 | 284 | ), |
| 285 | 285 | ( |
| ... | ... | @@ -1267,13 +1267,13 @@ impl crate::State { |
| 1267 | 1267 | ( |
| 1268 | 1268 | "shared", |
| 1269 | 1269 | Leading::Icon(art::LINK), |
| 1270 | "Open Shared Notebook…", | |
| 1270 | "Open Shared Notebook", | |
| 1271 | 1271 | crate::Command::OpenShared, |
| 1272 | 1272 | ), |
| 1273 | 1273 | ( |
| 1274 | 1274 | "server", |
| 1275 | 1275 | Leading::Icon(art::SERVER), |
| 1276 | "Open Notebook from Server…", | |
| 1276 | "Open Notebook from Server", | |
| 1277 | 1277 | crate::Command::OpenFromServer(None), |
| 1278 | 1278 | ), |
| 1279 | 1279 | ] |
crates/snowbound/src/symbol.rs+1-1| ... | ... | @@ -101,7 +101,7 @@ pub fn gallery(ui: &mut Ui, id: Id, anchor: Anchor, symbols: &[char]) -> Option< |
| 101 | 101 | "more", |
| 102 | 102 | Spec { |
| 103 | 103 | size: [fill(), fill()], |
| 104 | text: Some("More Symbols…"), | |
| 104 | text: Some("More Symbols"), | |
| 105 | 105 | icon: Some(art::SYMBOL), |
| 106 | 106 | gap: 6.0, |
| 107 | 107 | ..Spec::default() |
crates/snowbound/src/sync.rs+1-1| ... | ... | @@ -739,7 +739,7 @@ fn build(ui: &mut Ui, facts: &Facts) -> Picked { |
| 739 | 739 | &format!("Using the system’s connection. Snowbound couldn’t sign in: {notice}"), |
| 740 | 740 | theme.text_dim, |
| 741 | 741 | ); |
| 742 | picked.sign_in = ui::button(ui, "sign-in", "Sign In…").clicked; | |
| 742 | picked.sign_in = ui::button(ui, "sign-in", "Sign In").clicked; | |
| 743 | 743 | } |
| 744 | 744 | ui.close(); |
| 745 | 745 | } |
crates/snowbound/src/tags.rs+3-3| ... | ... | @@ -515,11 +515,11 @@ impl State { |
| 515 | 515 | let new = button( |
| 516 | 516 | ui, |
| 517 | 517 | "new", |
| 518 | "New Tag…", | |
| 518 | "New Tag", | |
| 519 | 519 | list.tags.len() < commands::TAGS && list.editor.is_none(), |
| 520 | 520 | ); |
| 521 | 521 | modify = modify.or_else(|| { |
| 522 | button(ui, "modify", "Modify Tag…", list.selected.is_some()) | |
| 522 | button(ui, "modify", "Modify Tag", list.selected.is_some()) | |
| 523 | 523 | .then_some(list.selected) |
| 524 | 524 | .flatten() |
| 525 | 525 | }); |
| ... | ... | @@ -1035,7 +1035,7 @@ fn gallery(ui: &mut Ui, id: Id, anchor: Anchor, snowbound: bool) -> Option<Pick> |
| 1035 | 1035 | ..Spec::default() |
| 1036 | 1036 | }, |
| 1037 | 1037 | ); |
| 1038 | if ui::button(ui, "picture", "Custom Image…").clicked { | |
| 1038 | if ui::button(ui, "picture", "Custom Image").clicked { | |
| 1039 | 1039 | picked = Some(Pick::Picture); |
| 1040 | 1040 | } |
| 1041 | 1041 | } |
crates/snowbound/src/themes.rs+1-1| ... | ... | @@ -807,7 +807,7 @@ pub(crate) fn gallery( |
| 807 | 807 | "customize", |
| 808 | 808 | Spec { |
| 809 | 809 | size: [fill(), fill()], |
| 810 | text: Some("Customize…"), | |
| 810 | text: Some("Customize"), | |
| 811 | 811 | ..Spec::default() |
| 812 | 812 | }, |
| 813 | 813 | ); |
crates/snowbound/src/unpack.rs+1-1| ... | ... | @@ -154,7 +154,7 @@ impl State { |
| 154 | 154 | ..Spec::default() |
| 155 | 155 | }, |
| 156 | 156 | ); |
| 157 | let browse = ui::button(ui, "browse", "Browse…").clicked; | |
| 157 | let browse = ui::button(ui, "browse", "Browse").clicked; | |
| 158 | 158 | let root = dialog.folder.join(dialog.name.trim()); |
| 159 | 159 | let full = format!("Full path: {}", root.display()); |
| 160 | 160 | ui.leaf( |
crates/snowbound/tests/replay.rs+2-2| ... | ... | @@ -452,7 +452,7 @@ fn a_version_copied_into_its_section_is_listed_and_found() { |
| 452 | 452 | let notebook = |
| 453 | 453 | Path::new(env!("CARGO_MANIFEST_DIR")).join("../../corpus/page-versions/candidate-restore"); |
| 454 | 454 | let mut steps = version_menu(); |
| 455 | // Copy Page To…, then the section itself. | |
| 455 | // Copy Page To, then the section itself. | |
| 456 | 456 | steps.extend(["key Down", "key Down", "key Down", "key Enter", "settle"].map(String::from)); |
| 457 | 457 | steps.extend(["key Down", "key Enter", "accessibility copied"].map(String::from)); |
| 458 | 458 | steps.extend(search("Second author", "found")); |
| ... | ... | @@ -674,7 +674,7 @@ fn open_shared_notebook_is_offered_with_notebooks_open() { |
| 674 | 674 | let [sidebar, dialog] = replay_with(&scratch, Some(&notebook), &steps, &variables) |
| 675 | 675 | .try_into() |
| 676 | 676 | .unwrap(); |
| 677 | assert!(sidebar.contains(r#""Open Shared…""#), "{sidebar}"); | |
| 677 | assert!(sidebar.contains(r#""Open Shared""#), "{sidebar}"); | |
| 678 | 678 | assert!( |
| 679 | 679 | dialog.contains(r#"Dialog "Open Shared Notebook""#), |
| 680 | 680 | "{dialog}" |
tools/RELEASE.md+2-2| ... | ... | @@ -230,7 +230,7 @@ frames from the symbol table. |
| 230 | 230 | |
| 231 | 231 | `update.rs` runs one thread. With automatic checks on (Options, General) and a |
| 232 | 232 | published build, it checks shortly after launch and then daily, skipping while |
| 233 | Work Offline is on; Check for Updates… (the app menu on macOS, the command | |
| 233 | Work Offline is on; Check for Updates (the app menu on macOS, the command | |
| 234 | 234 | palette elsewhere) checks at once and reports what it found. A check reads |
| 235 | 235 | `latest.json`, then the named build's `build.json` and signature, then |
| 236 | 236 | `history.json` and the `build.json` of each build since its own, and |
| ... | ... | @@ -241,7 +241,7 @@ the app unpacked into `.Snowbound.app.update` next to the bundle on macOS, the |
| 241 | 241 | executable into `.snowbound.update` next to it on Linux (`.snowbound.exe.update` on |
| 242 | 242 | Windows). The sync status icon gets a dot, and its popup says what the update |
| 243 | 243 | changes, unfolding to the titles, and offers Build Folder and Restart to |
| 244 | Update; Check for Updates… lists them in its dialog. | |
| 244 | Update; Check for Updates lists them in its dialog. | |
| 245 | 245 | |
| 246 | 246 | Restart to Update quits the app and starts the old executable with |
| 247 | 247 | `--finish-update`, which waits for the app to exit, renames the old bundle (or |
tools/canvas/README.md+1-1| ... | ... | @@ -50,7 +50,7 @@ snowbound title bar · toolbar · section frame · page tabs · the page as a |
| 50 | 50 | |
| 51 | 51 | `--notebook FOLDER` opens a notebook laid out as OneNote lays one out. The title bar shows the application's icon and the notebook's name; below it two rows of toolbar buttons follow the Home ribbon's Basic Text group, the first nine tags and zoom (only zoom acts yet). Readable top-level sections are tabs in the notebook's order, each leaning over the next at 45° with a faint highlight along its top, and the open one on top, casting a soft shadow and framing the page and the page tabs on the right in its colour, which shades slightly down the window; a tab that opens rises to meet the frame as its outline fades in. The frame's top corners and the page's corners are rounded concentric with the window's. The open page's tab is the page's colour and joins it with rounded inside corners; subpages are indented. The interface follows the system's light or dark appearance, and in the dark one the page's paper darkens and text left at OneNote's automatic colour turns light (except on a highlight), resolved when painting so nothing re-lays out; explicit colours, highlights, pictures and ink keep theirs, except that the background art of OneNote 2010's page templates, recognised by the picture's hash, paints from bundled vector recreations drawn for the paper. Sections take their stored colour's hue at a saturation and lightness chosen for each appearance. The section's colour is also the interface accent, easing to the next section's when another opens. The search box, New Page and the page-tab toggle sit above the page tabs. Command-F searches the page titles; Enter opens the first match and Escape clears it. A page that also changed on another computer shows Keep mine / Keep theirs above it. `--section FILE TITLE` opens one section the same way at that page. Section files must be regular files; discovery rejects symbolic links. |
| 52 | 52 | |
| 53 | Launched without a file, the app reopens the notebooks open when it last closed, at the one shown last, or shows "No notebooks open" with New Notebook and Open Existing. Settings (the open notebooks, the sidebar, recent fonts and the user name edits are stored under, which Settings… (โ,, Ctrl+, on Linux, or Options in the sidebar) changes and which starts as the account's full name) are JSON in `~/Library/Application Support/Snowbound/settings.json` or `$XDG_CONFIG_HOME/snowbound`; `--settings FILE` uses another file, `--notebook FOLDER` adds a notebook to them, and `--screenshot` never writes them. Open Existing takes a notebook folder, its `Open Notebook.onetoc2` or a section file, which opens in the notebook its folders make up. A notebook on a mounted SMB share opens through the embedded SMB client, signed in with the password the system keychain holds, and through the mount only when that fails. The notebook button left of the section tabs opens OneNote 2010's navigation bar: the open notebooks with their sections and section groups, the open section marked; dragging a row reorders a folder or, onto a group, moves into it, and right-clicking a row, a section tab or a page tab offers OneNote's commands (Rename, Delete to the notebook's recycle bin, New Section, New Section Group, Close This Notebook; Delete, New Page, Make Subpage, Promote Subpage). Page tabs drag to reorder. The `+` above the page tabs adds a page at the end of the section, titled with its date and time as OneNote titles one, and puts the caret in its title; until its body is typed in, drawn on or clicked, templates offer our recreations of OneNote 2010's art (Ivy first), its page colours and Informal Meeting Notes' content, written as ops (`corpus/notebook-management`). | |
| 53 | Launched without a file, the app reopens the notebooks open when it last closed, at the one shown last, or shows "No notebooks open" with New Notebook and Open Existing. Settings (the open notebooks, the sidebar, recent fonts and the user name edits are stored under, which Settings (โ,, Ctrl+, on Linux, or Options in the sidebar) changes and which starts as the account's full name) are JSON in `~/Library/Application Support/Snowbound/settings.json` or `$XDG_CONFIG_HOME/snowbound`; `--settings FILE` uses another file, `--notebook FOLDER` adds a notebook to them, and `--screenshot` never writes them. Open Existing takes a notebook folder, its `Open Notebook.onetoc2` or a section file, which opens in the notebook its folders make up. A notebook on a mounted SMB share opens through the embedded SMB client, signed in with the password the system keychain holds, and through the mount only when that fails. The notebook button left of the section tabs opens OneNote 2010's navigation bar: the open notebooks with their sections and section groups, the open section marked; dragging a row reorders a folder or, onto a group, moves into it, and right-clicking a row, a section tab or a page tab offers OneNote's commands (Rename, Delete to the notebook's recycle bin, New Section, New Section Group, Close This Notebook; Delete, New Page, Make Subpage, Promote Subpage). Page tabs drag to reorder. The `+` above the page tabs adds a page at the end of the section, titled with its date and time as OneNote titles one, and puts the caret in its title; until its body is typed in, drawn on or clicked, templates offer our recreations of OneNote 2010's art (Ivy first), its page colours and Informal Meeting Notes' content, written as ops (`corpus/notebook-management`). | |
| 54 | 54 | |
| 55 | 55 | As in OneNote 2010, Notebook Recycle Bin (File menu, a notebook's context menu, or the palette) shows the notebook's recycle bin as its own row of tabs, read-only under a bar: a page's context menu restores it to a section, keeping its identity, copies it out, or deletes it for good, a section's restores it to a folder, and Empty Recycle Bin deletes what it holds (`corpus/recycle-bin-view`). Pages another author changed since they were viewed are bold in the page list, and so are their section's tab and row and the groups and notebook holding them, until left or marked read (Mark as Read, Ctrl+Q off macOS; Mark Notebook as Read; Next Unread; Show Unread Changes in This Notebook); what was read is kept in the cache folder's `read.json`, as OneNote keeps it in its cache. Save As (โงโS) writes the page or section as a OneNote section, the notebook as a OneNote package, or any of them as a PDF; opening a `.onepkg` asks, as Unpack Notebook does, for its name, colour and folder (`corpus/notebook-package`). |
| 56 | 56 |