| 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,7 +342,7 @@ extension PagesViewController: UITableViewDragDelegate, UITableViewDropDelegate |
| 342 | if indentable(rows, at) { add("Make Subpage") { [weak self] in self?.indent(id, by: 1) } } | 342 | if indentable(rows, at) { add("Make Subpage") { [weak self] in self?.indent(id, by: 1) } } |
| 343 | if rows[at].level > 1 { add("Promote Subpage") { [weak self] in self?.indent(id, by: -1) } } | 343 | if rows[at].level > 1 { add("Promote Subpage") { [weak self] in self?.indent(id, by: -1) } } |
| 344 | if !moveActions(item).isEmpty { | 344 | if !moveActions(item).isEmpty { |
| 345 | add("Move to Section…") { [weak self] in | 345 | add("Move to Section") { [weak self] in |
| 346 | guard let self, let row = items.firstIndex(where: { $0.id == id }), | 346 | guard let self, let row = items.firstIndex(where: { $0.id == id }), |
| 347 | let cell = tableView.cellForRow(at: IndexPath(row: row, section: 0)) | 347 | let cell = tableView.cellForRow(at: IndexPath(row: row, section: 0)) |
| 348 | else { return } | 348 | else { return } |
| ... | @@ -571,7 +571,7 @@ extension NotebooksViewController: UICollectionViewDragDelegate, UICollectionVie | ... | @@ -571,7 +571,7 @@ extension NotebooksViewController: UICollectionViewDragDelegate, UICollectionVie |
| 571 | if at < order.count - 1 { add("Move Down") { [weak self] in self?.shift(path, of: notebook, by: 1) } } | 571 | if at < order.count - 1 { add("Move Down") { [weak self] in self?.shift(path, of: notebook, by: 1) } } |
| 572 | } | 572 | } |
| 573 | if !notebook.destinations(of: path).isEmpty { | 573 | if !notebook.destinations(of: path).isEmpty { |
| 574 | add("Move…") { [weak self, weak cell] in | 574 | add("Move") { [weak self, weak cell] in |
| 575 | guard let self, let cell else { return } | 575 | guard let self, let cell else { return } |
| 576 | sheet("Move", moveActions(path, of: notebook), from: cell, in: self) | 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,10 +94,10 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke |
| 94 | }) | 94 | }) |
| 95 | var actions: [UIMenuElement] = [ | 95 | var actions: [UIMenuElement] = [ |
| 96 | UIMenu(title: "New Notebook", options: .displayInline, children: places), | 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 | self?.connect() | 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 | self?.openFolder() | 101 | self?.openFolder() |
| 102 | }, | 102 | }, |
| 103 | ] | 103 | ] |
| ... | @@ -114,7 +114,7 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke | ... | @@ -114,7 +114,7 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke |
| 114 | let more = UIMenu(children: [ | 114 | let more = UIMenu(children: [ |
| 115 | UIDeferredMenuElement.uncached { [weak self] provide in | 115 | UIDeferredMenuElement.uncached { [weak self] provide in |
| 116 | var actions: [UIMenuElement] = [ | 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 | [weak self] _ in self.map(SyncViewController.present) | 118 | [weak self] _ in self.map(SyncViewController.present) |
| 119 | } | 119 | } |
| 120 | ] | 120 | ] |
| ... | @@ -125,7 +125,7 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke | ... | @@ -125,7 +125,7 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke |
| 125 | }) | 125 | }) |
| 126 | } | 126 | } |
| 127 | actions.append( | 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 | guard let self else { return } | 129 | guard let self else { return } |
| 130 | Author.ask(from: self) {} | 130 | Author.ask(from: self) {} |
| 131 | }) | 131 | }) |
| ... | @@ -385,7 +385,7 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke | ... | @@ -385,7 +385,7 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke |
| 385 | return [UIMenu(options: .displayInline, children: [details, now])] | 385 | return [UIMenu(options: .displayInline, children: [details, now])] |
| 386 | } ?? [] | 386 | } ?? [] |
| 387 | let actions = [ | 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 | [weak self] _ in self?.newSection(in: notebook) | 389 | [weak self] _ in self?.newSection(in: notebook) |
| 390 | }, | 390 | }, |
| 391 | UIAction(title: "Tags Summary", image: UIImage(systemName: "tag")) { [weak self] _ in | 391 | UIAction(title: "Tags Summary", image: UIImage(systemName: "tag")) { [weak self] _ in |
| ... | @@ -403,13 +403,13 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke | ... | @@ -403,13 +403,13 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke |
| 403 | return UIMenu(children: status + actions + [close]) | 403 | return UIMenu(children: status + actions + [close]) |
| 404 | } | 404 | } |
| 405 | let file = [ | 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 | self?.rename(folder, of: notebook) | 407 | self?.rename(folder, of: notebook) |
| 408 | }, | 408 | }, |
| 409 | UIAction(title: "Duplicate", image: UIImage(systemName: "plus.square.on.square")) { [weak self] _ in | 409 | UIAction(title: "Duplicate", image: UIImage(systemName: "plus.square.on.square")) { [weak self] _ in |
| 410 | self?.duplicate(folder, of: notebook) | 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 | self?.share(folder, from: notebook) | 413 | self?.share(folder, from: notebook) |
| 414 | }, | 414 | }, |
| 415 | UIAction(title: "Show in Files", image: UIImage(systemName: "folder")) { _ in | 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,7 +202,7 @@ final class PageViewController: UIViewController, PHPickerViewControllerDelegate |
| 202 | ]) | 202 | ]) |
| 203 | return (Prototype.reading ? [readingAction()] : []) + [ | 203 | return (Prototype.reading ? [readingAction()] : []) + [ |
| 204 | insert, | 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 | [weak self] _ in self?.showPaper() | 206 | [weak self] _ in self?.showPaper() |
| 207 | }, | 207 | }, |
| 208 | themeMenu(), | 208 | themeMenu(), |
| ... | @@ -211,10 +211,10 @@ final class PageViewController: UIViewController, PHPickerViewControllerDelegate | ... | @@ -211,10 +211,10 @@ final class PageViewController: UIViewController, PHPickerViewControllerDelegate |
| 211 | scene?.showTags(of: section.notebook, from: self) | 211 | scene?.showTags(of: section.notebook, from: self) |
| 212 | }, | 212 | }, |
| 213 | UIMenu(options: .displayInline, children: [ | 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 | self?.printPage(export: false) | 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 | self?.printPage(export: true) | 218 | self?.printPage(export: true) |
| 219 | }, | 219 | }, |
| 220 | ]), | 220 | ]), |
apps/ios/Snowbound/Prototypes.swift+2-2| ... | @@ -52,8 +52,8 @@ extension Prototype { | ... | @@ -52,8 +52,8 @@ extension Prototype { |
| 52 | open.title = "Open Existing Notebook" | 52 | open.title = "Open Existing Notebook" |
| 53 | welcome.secondaryButton = open | 53 | welcome.secondaryButton = open |
| 54 | var elsewhere = [ | 54 | var elsewhere = [ |
| 55 | UIAction(title: "Open Folder…", image: UIImage(systemName: "folder")) { _ in openFolder() }, | 55 | UIAction(title: "Open Folder", image: UIImage(systemName: "folder")) { _ in openFolder() }, |
| 56 | UIAction(title: "Connect to Server…", image: UIImage(systemName: "server.rack")) { _ in connect() }, | 56 | UIAction(title: "Connect to Server", image: UIImage(systemName: "server.rack")) { _ in connect() }, |
| 57 | ] | 57 | ] |
| 58 | if !ICloud.signedIn { | 58 | if !ICloud.signedIn { |
| 59 | elsewhere.append( | 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,7 +52,7 @@ keyboard, the toolbar and the macOS menu bar all run commands from it. |
| 52 | frame with the window's new size instead of stretching the last one. | 52 | frame with the window's new size instead of stretching the last one. |
| 53 | - A notebook on a mounted SMB share is opened through the embedded SMB client, | 53 | - A notebook on a mounted SMB share is opened through the embedded SMB client, |
| 54 | signed in with the password the keychain keeps for that mount (see | 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 | by address instead, and keeps a password it asks to remember as the Finder does, an | 56 | by address instead, and keeps a password it asks to remember as the Finder does, an |
| 57 | SMB internet password. | 57 | SMB internet password. |
| 58 | - The menu bar (`menubar.rs`) is laid out as OneNote for Mac's. Its items are | 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,7 +147,7 @@ keyboard, the toolbar and the macOS menu bar all run commands from it. |
| 147 | other writers) and takes OneNote's coordination bytes with byte-range | 147 | other writers) and takes OneNote's coordination bytes with byte-range |
| 148 | locks, so both apps can have a section open on one machine. | 148 | locks, so both apps can have a section open on one machine. |
| 149 | ReadDirectoryChangesW reports changes below a notebook, including other | 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 | client, its passwords in the Credential Manager. | 151 | client, its passwords in the Credential Manager. |
| 152 | - The common file dialogs, task dialogs, the date and time picker, and the | 152 | - The common file dialogs, task dialogs, the date and time picker, and the |
| 153 | Spell Checking API (from Windows 8) are the system's; audio plays and | 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,7 +192,7 @@ client with the account the system keeps for that mount. It falls back to the |
| 192 | mount only when it can't sign in that way. | 192 | mount only when it can't sign in that way. |
| 193 | 193 | ||
| 194 | A notebook can also be opened from its server's address, with no mount at all (File ▸ | 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 | speaks SMB1, the only version its Finder has. The notebook is kept by that address | 196 | speaks SMB1, the only version its Finder has. The notebook is kept by that address |
| 197 | (`smb://[domain;]user@server/share/folder`), never with a password; the password lives in | 197 | (`smb://[domain;]user@server/share/folder`), never with a password; the password lives in |
| 198 | the keychain (the Secret Service on Linux) when the user asks to remember it, and | 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,7 +47,7 @@ Versions is chosen. An open version is read-only under a yellow bar (RGB 255, 23 |
| 47 | "This is an earlier version of the page. It will be deleted over time. Click here to restore | 47 | "This is an earlier version of the page. It will be deleted over time. Click here to restore |
| 48 | or delete this version." What the version changed since the one before it is banded green | 48 | or delete this version." What the version changed since the one before it is banded green |
| 49 | (RGB 214, 255, 214). The bar's menu (`shots/02-version-menu.png`): Restore Version, Delete | 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 | History for This Notebook; Hide Page Versions. Share also has Hide Authors, Recent Edits and | 51 | History for This Notebook; Hide Page Versions. Share also has Hide Authors, Recent Edits and |
| 52 | Find by Author (`shots/14`–`16`). | 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,7 +6,7 @@ ends with Section Color, sixteen named colours and None (`section-color-menu.png |
| 6 | appends a revision to the section's own file setting its metadata's `0x14001cbe` | 6 | appends a revision to the section's own file setting its metadata's `0x14001cbe` |
| 7 | (COLORREF; `teal.one`), and None sets `0xFFFFFFFF` (`none.one`); the table of contents is | 7 | (COLORREF; `teal.one`), and None sets `0xFFFFFFFF` (`none.one`); the table of contents is |
| 8 | left byte for byte. Notebook Properties (`notebook-properties.png`, from the notebook's | 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 | appends a revision to the root `Open Notebook.onetoc2` setting its root's `0x14001cbe` | 10 | appends a revision to the root `Open Notebook.onetoc2` setting its root's `0x14001cbe` |
| 11 | (`before.onetoc2`, `after.onetoc2`). Its Display name is written to neither file: OneNote | 11 | (`before.onetoc2`, `after.onetoc2`). Its Display name is written to neither file: OneNote |
| 12 | keeps it in its local `OneNoteOfflineCache.onecache`, so the hierarchy's `nickname` | 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,7 +11,7 @@ cd snowbound-linux-x86_64 |
| 11 | ./bin/snowbound | 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 | the one holding the `.one` section files and `Open Notebook.onetoc2`. | 15 | the one holding the `.one` section files and `Open Notebook.onetoc2`. |
| 16 | Snowbound remembers the notebooks you open. `--notebook FOLDER` opens one | 16 | Snowbound remembers the notebooks you open. `--notebook FOLDER` opens one |
| 17 | straight away. | 17 | straight away. |
crates/snowbound/src/background.rs+1-1| ... | @@ -161,7 +161,7 @@ pub fn menu( | ... | @@ -161,7 +161,7 @@ pub fn menu( |
| 161 | } | 161 | } |
| 162 | match cells[index] { | 162 | match cells[index] { |
| 163 | Cell::Color(None) | Cell::Art(None) | Cell::Rules(None) => label(ui, "None"), | 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 | Cell::Color(Some(color)) => { | 165 | Cell::Color(Some(color)) => { |
| 166 | ui.leaf( | 166 | ui.leaf( |
| 167 | "swatch", | 167 | "swatch", |
crates/snowbound/src/commands.rs+20-20| ... | @@ -260,12 +260,12 @@ const NONE: &[Chord] = &[]; | ... | @@ -260,12 +260,12 @@ const NONE: &[Chord] = &[]; |
| 260 | 260 | ||
| 261 | /// OneNote 2010's chords off macOS; there, AppKit's and OneNote for Mac's. | 261 | /// OneNote 2010's chords off macOS; there, AppKit's and OneNote for Mac's. |
| 262 | pub const COMMANDS: &[Command] = &[ | 262 | pub const COMMANDS: &[Command] = &[ |
| 263 | row!(Id::Settings, "Settings…", &[cmd(',')], &[cmd(',')]), | 263 | row!(Id::Settings, "Settings", &[cmd(',')], &[cmd(',')]), |
| 264 | row!(Id::NewNotebook, "New Notebook…", NONE, NONE), | 264 | row!(Id::NewNotebook, "New Notebook", NONE, NONE), |
| 265 | row!(Id::OpenNotebook, "Open Notebook…", &[cmd('o')], &[cmd('o')]), | 265 | row!(Id::OpenNotebook, "Open Notebook", &[cmd('o')], &[cmd('o')]), |
| 266 | row!(Id::OpenFromServer, "Open Notebook from Server…", NONE, NONE), | 266 | row!(Id::OpenFromServer, "Open Notebook from Server", NONE, NONE), |
| 267 | row!(Id::OpenShared, "Open Shared Notebook…", NONE, NONE), | 267 | row!(Id::OpenShared, "Open Shared Notebook", NONE, NONE), |
| 268 | row!(Id::LiveShare, "Live Share…", NONE, NONE), | 268 | row!(Id::LiveShare, "Live Share", NONE, NONE), |
| 269 | row!(Id::CloseNotebook, "Close This Notebook", NONE, NONE), | 269 | row!(Id::CloseNotebook, "Close This Notebook", NONE, NONE), |
| 270 | row!(Id::NewSection, "New Section", &[cmd('t')], &[cmd('t')]), | 270 | row!(Id::NewSection, "New Section", &[cmd('t')], &[cmd('t')]), |
| 271 | row!(Id::NewSectionGroup, "New Section Group", NONE, NONE), | 271 | row!(Id::NewSectionGroup, "New Section Group", NONE, NONE), |
| ... | @@ -280,7 +280,7 @@ pub const COMMANDS: &[Command] = &[ | ... | @@ -280,7 +280,7 @@ pub const COMMANDS: &[Command] = &[ |
| 280 | row!(Id::CopyPageLink, "Copy Link to Page", NONE, NONE), | 280 | row!(Id::CopyPageLink, "Copy Link to Page", NONE, NONE), |
| 281 | row!( | 281 | row!( |
| 282 | Id::PasswordProtect, | 282 | Id::PasswordProtect, |
| 283 | "Password Protect This Section…", | 283 | "Password Protect This Section", |
| 284 | NONE, | 284 | NONE, |
| 285 | NONE | 285 | NONE |
| 286 | ), | 286 | ), |
| ... | @@ -302,7 +302,7 @@ pub const COMMANDS: &[Command] = &[ | ... | @@ -302,7 +302,7 @@ pub const COMMANDS: &[Command] = &[ |
| 302 | ), | 302 | ), |
| 303 | row!( | 303 | row!( |
| 304 | Id::SaveAs, | 304 | Id::SaveAs, |
| 305 | "Save As…", | 305 | "Save As", |
| 306 | &[cmd('s').shift()], | 306 | &[cmd('s').shift()], |
| 307 | &[cmd('s').shift()] | 307 | &[cmd('s').shift()] |
| 308 | ), | 308 | ), |
| ... | @@ -318,11 +318,11 @@ pub const COMMANDS: &[Command] = &[ | ... | @@ -318,11 +318,11 @@ pub const COMMANDS: &[Command] = &[ |
| 318 | NONE | 318 | NONE |
| 319 | ), | 319 | ), |
| 320 | row!(Id::NextUnread, "Next Unread", NONE, NONE), | 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 | // Go to takes OneNote's Ctrl+P, and Pause its Ctrl+Alt+P. | 322 | // Go to takes OneNote's Ctrl+P, and Pause its Ctrl+Alt+P. |
| 323 | row!( | 323 | row!( |
| 324 | Id::Print, | 324 | Id::Print, |
| 325 | "Print…", | 325 | "Print", |
| 326 | &[cmd('p').option().shift()], | 326 | &[cmd('p').option().shift()], |
| 327 | &[cmd('p').option().shift()] | 327 | &[cmd('p').option().shift()] |
| 328 | ), | 328 | ), |
| ... | @@ -351,10 +351,10 @@ pub const COMMANDS: &[Command] = &[ | ... | @@ -351,10 +351,10 @@ pub const COMMANDS: &[Command] = &[ |
| 351 | &[cmd('f').option()], | 351 | &[cmd('f').option()], |
| 352 | &[key('o').option()], | 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 | row!( | 355 | row!( |
| 356 | Id::CommandPalette, | 356 | Id::CommandPalette, |
| 357 | "Command Palette…", | 357 | "Command Palette", |
| 358 | &[cmd('p').shift()], | 358 | &[cmd('p').shift()], |
| 359 | &[cmd('p').shift()] | 359 | &[cmd('p').shift()] |
| 360 | ), | 360 | ), |
| ... | @@ -393,7 +393,7 @@ pub const COMMANDS: &[Command] = &[ | ... | @@ -393,7 +393,7 @@ pub const COMMANDS: &[Command] = &[ |
| 393 | row!(Id::MarkdownShortcuts, "Markdown Shortcuts", NONE, NONE), | 393 | row!(Id::MarkdownShortcuts, "Markdown Shortcuts", NONE, NONE), |
| 394 | row!( | 394 | row!( |
| 395 | Id::Spelling, | 395 | Id::Spelling, |
| 396 | "Spelling…", | 396 | "Spelling", |
| 397 | &[named(NamedKey::F7)], | 397 | &[named(NamedKey::F7)], |
| 398 | &[named(NamedKey::F7)] | 398 | &[named(NamedKey::F7)] |
| 399 | ), | 399 | ), |
| ... | @@ -406,10 +406,10 @@ pub const COMMANDS: &[Command] = &[ | ... | @@ -406,10 +406,10 @@ pub const COMMANDS: &[Command] = &[ |
| 406 | ), | 406 | ), |
| 407 | row!(Id::PageColor, "Page Color", NONE, NONE), | 407 | row!(Id::PageColor, "Page Color", NONE, NONE), |
| 408 | row!(Id::Table, "Table", NONE, NONE), | 408 | row!(Id::Table, "Table", NONE, NONE), |
| 409 | row!(Id::Picture, "Picture…", NONE, NONE), | 409 | row!(Id::Picture, "Picture", NONE, NONE), |
| 410 | row!(Id::ScreenClipping, "Screen Clipping", NONE, NONE), | 410 | row!(Id::ScreenClipping, "Screen Clipping", NONE, NONE), |
| 411 | row!(Id::Attachment, "Attach File…", NONE, NONE), | 411 | row!(Id::Attachment, "Attach File", NONE, NONE), |
| 412 | row!(Id::Link, "Link…", &[cmd('k')], &[cmd('k')]), | 412 | row!(Id::Link, "Link", &[cmd('k')], &[cmd('k')]), |
| 413 | row!(Id::InsertSpace, "Insert Space", NONE, NONE), | 413 | row!(Id::InsertSpace, "Insert Space", NONE, NONE), |
| 414 | row!( | 414 | row!( |
| 415 | Id::Equation, | 415 | Id::Equation, |
| ... | @@ -465,7 +465,7 @@ pub const COMMANDS: &[Command] = &[ | ... | @@ -465,7 +465,7 @@ pub const COMMANDS: &[Command] = &[ |
| 465 | NONE, | 465 | NONE, |
| 466 | NONE | 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 | row!( | 469 | row!( |
| 470 | Id::Transport(Transport::SeePlayback), | 470 | Id::Transport(Transport::SeePlayback), |
| 471 | "See Playback", | 471 | "See Playback", |
| ... | @@ -596,8 +596,8 @@ pub const COMMANDS: &[Command] = &[ | ... | @@ -596,8 +596,8 @@ pub const COMMANDS: &[Command] = &[ |
| 596 | row!(Id::Style(8), "Quote", NONE, NONE), | 596 | row!(Id::Style(8), "Quote", NONE, NONE), |
| 597 | row!(Id::Style(9), "Code", NONE, NONE), | 597 | row!(Id::Style(9), "Code", NONE, NONE), |
| 598 | row!(Id::Style(10), "Normal", NONE, NONE), | 598 | row!(Id::Style(10), "Normal", NONE, NONE), |
| 599 | row!(Id::Themes, "Customize Theme…", NONE, NONE), | 599 | row!(Id::Themes, "Customize Theme", NONE, NONE), |
| 600 | row!(Id::CustomizeTags, "Customize Tags…", NONE, NONE), | 600 | row!(Id::CustomizeTags, "Customize Tags", NONE, NONE), |
| 601 | row!( | 601 | row!( |
| 602 | Id::RemoveTags, | 602 | Id::RemoveTags, |
| 603 | "Remove Tag", | 603 | "Remove Tag", |
| ... | @@ -608,7 +608,7 @@ pub const COMMANDS: &[Command] = &[ | ... | @@ -608,7 +608,7 @@ pub const COMMANDS: &[Command] = &[ |
| 608 | row!(Id::ToDoList, "Make To-Do List", NONE, NONE), | 608 | row!(Id::ToDoList, "Make To-Do List", NONE, NONE), |
| 609 | row!(Id::BulletedList, "Make Bulleted List", NONE, NONE), | 609 | row!(Id::BulletedList, "Make Bulleted List", NONE, NONE), |
| 610 | row!(Id::Help, "Snowbound Help", NONE, NONE), | 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 | /// The most tags a list holds: MS-ONE's action types 0 to 99 number them. | 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,7 +105,7 @@ pub fn menu( |
| 105 | drawn("Delete Version", art::DELETE), | 105 | drawn("Delete Version", art::DELETE), |
| 106 | ui::popup::Item { | 106 | ui::popup::Item { |
| 107 | disabled: sections.is_empty(), | 107 | disabled: sections.is_empty(), |
| 108 | ..drawn("Copy Page To…", art::COPY) | 108 | ..drawn("Copy Page To", art::COPY) |
| 109 | }, | 109 | }, |
| 110 | ui::popup::Item { | 110 | ui::popup::Item { |
| 111 | separated: true, | 111 | separated: true, |
crates/snowbound/src/keys.rs+1-1| ... | @@ -78,7 +78,7 @@ fn label(chord: Chord) -> String { | ... | @@ -78,7 +78,7 @@ fn label(chord: Chord) -> String { |
| 78 | fn title(id: Command) -> String { | 78 | fn title(id: Command) -> String { |
| 79 | match id { | 79 | match id { |
| 80 | Command::Tag(place) => format!("Tag {}", place + 1), | 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,7 +303,7 @@ impl State { |
| 303 | item("Ignore"), | 303 | item("Ignore"), |
| 304 | Item { | 304 | Item { |
| 305 | separated: true, | 305 | separated: true, |
| 306 | ..drawn("Spelling…", art::SPELLING) | 306 | ..drawn("Spelling", art::SPELLING) |
| 307 | }, | 307 | }, |
| 308 | ], | 308 | ], |
| 309 | Some(correction) => { | 309 | Some(correction) => { |
| ... | @@ -326,7 +326,7 @@ impl State { | ... | @@ -326,7 +326,7 @@ impl State { |
| 326 | drawn("Add to Dictionary", art::ADD_TO_DICTIONARY), | 326 | drawn("Add to Dictionary", art::ADD_TO_DICTIONARY), |
| 327 | Item { | 327 | Item { |
| 328 | separated: true, | 328 | separated: true, |
| 329 | ..drawn("Spelling…", art::SPELLING) | 329 | ..drawn("Spelling", art::SPELLING) |
| 330 | }, | 330 | }, |
| 331 | ]); | 331 | ]); |
| 332 | items | 332 | items |
| ... | @@ -344,7 +344,7 @@ impl State { | ... | @@ -344,7 +344,7 @@ impl State { |
| 344 | .collect::<Vec<_>>(); | 344 | .collect::<Vec<_>>(); |
| 345 | items.extend(if context.attachment.is_some() { | 345 | items.extend(if context.attachment.is_some() { |
| 346 | // OneNote 2010's commands for the file itself; its clipboard holds text alone. | 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 | } else { | 348 | } else { |
| 349 | let mut items = vec![ | 349 | let mut items = vec![ |
| 350 | Item { | 350 | Item { |
| ... | @@ -362,7 +362,7 @@ impl State { | ... | @@ -362,7 +362,7 @@ impl State { |
| 362 | Some(_) => items.extend([ | 362 | Some(_) => items.extend([ |
| 363 | Item { | 363 | Item { |
| 364 | separated: true, | 364 | separated: true, |
| 365 | ..drawn("Edit Link…", art::LINK) | 365 | ..drawn("Edit Link", art::LINK) |
| 366 | }, | 366 | }, |
| 367 | drawn("Copy Link to Paragraph", art::COPY_LINK), | 367 | drawn("Copy Link to Paragraph", art::COPY_LINK), |
| 368 | Item { | 368 | Item { |
| ... | @@ -375,7 +375,7 @@ impl State { | ... | @@ -375,7 +375,7 @@ impl State { |
| 375 | None => items.extend([ | 375 | None => items.extend([ |
| 376 | Item { | 376 | Item { |
| 377 | separated: true, | 377 | separated: true, |
| 378 | ..drawn("Link…", art::LINK) | 378 | ..drawn("Link", art::LINK) |
| 379 | }, | 379 | }, |
| 380 | drawn("Copy Link to Paragraph", art::COPY_LINK), | 380 | drawn("Copy Link to Paragraph", art::COPY_LINK), |
| 381 | ]), | 381 | ]), |
| ... | @@ -414,7 +414,7 @@ impl State { | ... | @@ -414,7 +414,7 @@ impl State { |
| 414 | self.view.correct(&correction, &chosen_text)? | 414 | self.view.correct(&correction, &chosen_text)? |
| 415 | } | 415 | } |
| 416 | "Delete Repeated Word" => self.view.correct(&correction, "")?, | 416 | "Delete Repeated Word" => self.view.correct(&correction, "")?, |
| 417 | "Spelling…" => { | 417 | "Spelling" => { |
| 418 | self.open_spelling_pane(); | 418 | self.open_spelling_pane(); |
| 419 | return Ok(()); | 419 | return Ok(()); |
| 420 | } | 420 | } |
| ... | @@ -441,7 +441,7 @@ impl State { | ... | @@ -441,7 +441,7 @@ impl State { |
| 441 | } | 441 | } |
| 442 | let response = match chosen_text.as_str() { | 442 | let response = match chosen_text.as_str() { |
| 443 | "Open" => return self.open_attachment(&context.attachment.unwrap()), | 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 | "Cut" => self.view.copy(true)?, | 445 | "Cut" => self.view.copy(true)?, |
| 446 | "Copy" => self.view.copy(false)?, | 446 | "Copy" => self.view.copy(false)?, |
| 447 | "Paste" => { | 447 | "Paste" => { |
| ... | @@ -449,7 +449,7 @@ impl State { | ... | @@ -449,7 +449,7 @@ impl State { |
| 449 | .push(Command::Page(canvas::interaction::Request::Paste)); | 449 | .push(Command::Page(canvas::interaction::Request::Paste)); |
| 450 | return Ok(()); | 450 | return Ok(()); |
| 451 | } | 451 | } |
| 452 | "Edit Link…" | "Link…" => { | 452 | "Edit Link" | "Link" => { |
| 453 | self.open_link_dialog(); | 453 | self.open_link_dialog(); |
| 454 | return Ok(()); | 454 | return Ok(()); |
| 455 | } | 455 | } |
crates/snowbound/src/main.rs+15-13| ... | @@ -2413,7 +2413,7 @@ impl State { | ... | @@ -2413,7 +2413,7 @@ impl State { |
| 2413 | } else if ui::shell::split_button( | 2413 | } else if ui::shell::split_button( |
| 2414 | ui, | 2414 | ui, |
| 2415 | part, | 2415 | part, |
| 2416 | title(id), | 2416 | commands::command(id).title, |
| 2417 | icon, | 2417 | icon, |
| 2418 | bar, | 2418 | bar, |
| 2419 | None, | 2419 | None, |
| ... | @@ -2496,7 +2496,7 @@ impl State { | ... | @@ -2496,7 +2496,7 @@ impl State { |
| 2496 | } else if ui::shell::split_button( | 2496 | } else if ui::shell::split_button( |
| 2497 | ui, | 2497 | ui, |
| 2498 | part, | 2498 | part, |
| 2499 | title(id), | 2499 | commands::command(id).title, |
| 2500 | icon, | 2500 | icon, |
| 2501 | None, | 2501 | None, |
| 2502 | status.checked, | 2502 | status.checked, |
| ... | @@ -2798,7 +2798,7 @@ impl State { | ... | @@ -2798,7 +2798,7 @@ impl State { |
| 2798 | if ui::shell::split_button( | 2798 | if ui::shell::split_button( |
| 2799 | ui, | 2799 | ui, |
| 2800 | "pen", | 2800 | "pen", |
| 2801 | title(Cmd::Pen), | 2801 | commands::command(Cmd::Pen).title, |
| 2802 | art::PEN, | 2802 | art::PEN, |
| 2803 | bar, | 2803 | bar, |
| 2804 | status.checked, | 2804 | status.checked, |
| ... | @@ -4724,7 +4724,7 @@ fn conflict_bar(ui: &mut Ui, bar: Bar, sections: &[&str], steps: [bool; 2]) -> O | ... | @@ -4724,7 +4724,7 @@ fn conflict_bar(ui: &mut Ui, bar: Bar, sections: &[&str], steps: [bool; 2]) -> O |
| 4724 | ..Default::default() | 4724 | ..Default::default() |
| 4725 | }, | 4725 | }, |
| 4726 | ui::popup::Item { | 4726 | ui::popup::Item { |
| 4727 | text: "Copy Page To…", | 4727 | text: "Copy Page To", |
| 4728 | icon: Some(art::COPY), | 4728 | icon: Some(art::COPY), |
| 4729 | disabled: sections.is_empty(), | 4729 | disabled: sections.is_empty(), |
| 4730 | ..Default::default() | 4730 | ..Default::default() |
| ... | @@ -5349,7 +5349,9 @@ fn dropdown( | ... | @@ -5349,7 +5349,9 @@ fn dropdown( |
| 5349 | Head::Split(id) if status_of(id).enabled => { | 5349 | Head::Split(id) if status_of(id).enabled => { |
| 5350 | let icon = artwork(id).unwrap_or_default(); | 5350 | let icon = artwork(id).unwrap_or_default(); |
| 5351 | let on = status_of(id).checked; | 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 | choice = Some(Choice::Command(id)); | 5355 | choice = Some(Choice::Command(id)); |
| 5354 | } | 5356 | } |
| 5355 | tip(ui, id); | 5357 | tip(ui, id); |
| ... | @@ -5368,7 +5370,7 @@ fn dropdown( | ... | @@ -5368,7 +5370,7 @@ fn dropdown( |
| 5368 | Head::Split(id) => { | 5370 | Head::Split(id) => { |
| 5369 | let icon = artwork(id).unwrap_or_default(); | 5371 | let icon = artwork(id).unwrap_or_default(); |
| 5370 | let anchor = ui::shell::menu_button(ui, part, icon, None, menu); | 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 | anchor | 5374 | anchor |
| 5373 | } | 5375 | } |
| 5374 | Head::Tag(_, tag) => { | 5376 | Head::Tag(_, tag) => { |
| ... | @@ -5422,7 +5424,7 @@ fn dropdown( | ... | @@ -5422,7 +5424,7 @@ fn dropdown( |
| 5422 | let options: Vec<_> = entries | 5424 | let options: Vec<_> = entries |
| 5423 | .iter() | 5425 | .iter() |
| 5424 | .map(|entry| match *entry { | 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 | _ => String::new(), | 5428 | _ => String::new(), |
| 5427 | }) | 5429 | }) |
| 5428 | .collect(); | 5430 | .collect(); |
| ... | @@ -5723,12 +5725,12 @@ fn tag_tip(ui: &mut Ui, place: usize, tag: &canvas::editor::NoteTag) { | ... | @@ -5723,12 +5725,12 @@ fn tag_tip(ui: &mut Ui, place: usize, tag: &canvas::editor::NoteTag) { |
| 5723 | 5725 | ||
| 5724 | /// A tooltip naming command `id` and its chord on the box built last. | 5726 | /// A tooltip naming command `id` and its chord on the box built last. |
| 5725 | fn tip(ui: &mut Ui, id: commands::Id) { | 5727 | fn tip(ui: &mut Ui, id: commands::Id) { |
| 5726 | ui::popup::tooltip(ui, title(id), &commands::shortcut(id), None); | 5728 | ui::popup::tooltip( |
| 5727 | } | 5729 | ui, |
| 5728 | 5730 | commands::command(id).title, | |
| 5729 | /// Command `id`'s title without the menu's ellipsis, naming the command, not a dialog it opens. | 5731 | &commands::shortcut(id), |
| 5730 | fn title(id: commands::Id) -> &'static str { | 5732 | None, |
| 5731 | commands::command(id).title.trim_end_matches('…') | 5733 | ); |
| 5732 | } | 5734 | } |
| 5733 | 5735 | ||
| 5734 | /// Whether each toolbar group stands on a face of its own. | 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,7 +102,7 @@ const MENUS: &[Item] = &[ |
| 102 | C(Id::Search), | 102 | C(Id::Search), |
| 103 | C(Id::SearchResults), | 103 | C(Id::SearchResults), |
| 104 | S, | 104 | S, |
| 105 | Item::System("Start Dictation…", "startDictation:", None), | 105 | Item::System("Start Dictation", "startDictation:", None), |
| 106 | Item::System( | 106 | Item::System( |
| 107 | "Emoji & Symbols", | 107 | "Emoji & Symbols", |
| 108 | "orderFrontCharacterPalette:", | 108 | "orderFrontCharacterPalette:", |
crates/snowbound/src/menus.rs+9-9| ... | @@ -342,7 +342,7 @@ impl State { | ... | @@ -342,7 +342,7 @@ impl State { |
| 342 | item(Action::Copy, "Copy To", nowhere(), false), | 342 | item(Action::Copy, "Copy To", nowhere(), false), |
| 343 | ], | 343 | ], |
| 344 | Target::Section { path, .. } if crate::recycle::binned(path) => vec![ | 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 | item(Action::Move, "Restore To", nowhere(), false), | 346 | item(Action::Move, "Restore To", nowhere(), false), |
| 347 | item(Action::EmptyRecycleBin, "Empty Recycle Bin", false, true), | 347 | item(Action::EmptyRecycleBin, "Empty Recycle Bin", false, true), |
| 348 | item(Action::Colors, "Section Color", false, true), | 348 | item(Action::Colors, "Section Color", false, true), |
| ... | @@ -360,7 +360,7 @@ impl State { | ... | @@ -360,7 +360,7 @@ impl State { |
| 360 | item(Action::NewPage, "New Page", false, true), | 360 | item(Action::NewPage, "New Page", false, true), |
| 361 | // OneNote offers this beside New Page, which is a plain + here. | 361 | // OneNote offers this beside New Page, which is a plain + here. |
| 362 | item(Action::NewSubpage, "New Subpage", false, false), | 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 | // Levels and versions are known once the section is open. | 365 | // Levels and versions are known once the section is open. |
| 366 | let Some(session) = self.session.as_ref().filter(|_| self.open(library, path)) | 366 | let Some(session) = self.session.as_ref().filter(|_| self.open(library, path)) |
| ... | @@ -400,7 +400,7 @@ impl State { | ... | @@ -400,7 +400,7 @@ impl State { |
| 400 | let section = matches!(target, Target::Section { .. }); | 400 | let section = matches!(target, Target::Section { .. }); |
| 401 | let mut actions = vec![item(Action::Rename, "Rename", false, false)]; | 401 | let mut actions = vec![item(Action::Rename, "Rename", false, false)]; |
| 402 | if section { | 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 | actions.extend([ | 405 | actions.extend([ |
| 406 | item(Action::Delete, "Delete", false, false), | 406 | item(Action::Delete, "Delete", false, false), |
| ... | @@ -412,7 +412,7 @@ impl State { | ... | @@ -412,7 +412,7 @@ impl State { |
| 412 | actions.extend([ | 412 | actions.extend([ |
| 413 | item( | 413 | item( |
| 414 | Action::Password, | 414 | Action::Password, |
| 415 | "Password Protect This Section…", | 415 | "Password Protect This Section", |
| 416 | library.catalog().is_none(), | 416 | library.catalog().is_none(), |
| 417 | true, | 417 | true, |
| 418 | ), | 418 | ), |
| ... | @@ -423,7 +423,7 @@ impl State { | ... | @@ -423,7 +423,7 @@ impl State { |
| 423 | section_file(library, path).is_none(), | 423 | section_file(library, path).is_none(), |
| 424 | false, | 424 | false, |
| 425 | ), | 425 | ), |
| 426 | item(Action::Theme, "Theme…", false, false), | 426 | item(Action::Theme, "Theme", false, false), |
| 427 | ]); | 427 | ]); |
| 428 | } | 428 | } |
| 429 | actions | 429 | actions |
| ... | @@ -433,10 +433,10 @@ impl State { | ... | @@ -433,10 +433,10 @@ impl State { |
| 433 | let last = self.notebooks.len().saturating_sub(1); | 433 | let last = self.notebooks.len().saturating_sub(1); |
| 434 | let unread = self.unread_notebook(library); | 434 | let unread = self.unread_notebook(library); |
| 435 | vec![ | 435 | vec![ |
| 436 | item(Action::Rename, "Rename…", false, false), | 436 | item(Action::Rename, "Rename", false, false), |
| 437 | item( | 437 | item( |
| 438 | Action::SaveAs, | 438 | Action::SaveAs, |
| 439 | "Save As…", | 439 | "Save As", |
| 440 | library.catalog().is_none(), | 440 | library.catalog().is_none(), |
| 441 | false, | 441 | false, |
| 442 | ), | 442 | ), |
| ... | @@ -493,7 +493,7 @@ impl State { | ... | @@ -493,7 +493,7 @@ impl State { |
| 493 | library.folder().is_none(), | 493 | library.folder().is_none(), |
| 494 | true, | 494 | true, |
| 495 | ), | 495 | ), |
| 496 | item(Action::Theme, "Theme…", false, false), | 496 | item(Action::Theme, "Theme", false, false), |
| 497 | item( | 497 | item( |
| 498 | Action::RecycleBin, | 498 | Action::RecycleBin, |
| 499 | "Notebook Recycle Bin", | 499 | "Notebook Recycle Bin", |
| ... | @@ -502,7 +502,7 @@ impl State { | ... | @@ -502,7 +502,7 @@ impl State { |
| 502 | ), | 502 | ), |
| 503 | item( | 503 | item( |
| 504 | Action::Properties, | 504 | Action::Properties, |
| 505 | "Properties…", | 505 | "Properties", |
| 506 | library.catalog().is_none(), | 506 | library.catalog().is_none(), |
| 507 | false, | 507 | false, |
| 508 | ), | 508 | ), |
crates/snowbound/src/options.rs+1-1| ... | @@ -992,7 +992,7 @@ fn installed(state: &mut State, _: &mut Options) { | ... | @@ -992,7 +992,7 @@ fn installed(state: &mut State, _: &mut Options) { |
| 992 | let ui = &mut state.ui; | 992 | let ui = &mut state.ui; |
| 993 | if crate::desktop::uninstallable() { | 993 | if crate::desktop::uninstallable() { |
| 994 | path(ui, &crate::desktop::binary().unwrap_or_default()); | 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 | crate::desktop::uninstall(&state.proxy); | 996 | crate::desktop::uninstall(&state.proxy); |
| 997 | } | 997 | } |
| 998 | } else if crate::desktop::installable() { | 998 | } else if crate::desktop::installable() { |
crates/snowbound/src/print.rs+1-5| ... | @@ -257,11 +257,7 @@ impl State { | ... | @@ -257,11 +257,7 @@ impl State { |
| 257 | }); | 257 | }); |
| 258 | crate::buttons(ui); | 258 | crate::buttons(ui); |
| 259 | let cancel = ui::button(ui, "cancel", "Cancel").clicked; | 259 | let cancel = ui::button(ui, "cancel", "Cancel").clicked; |
| 260 | let action = if dialog.export { | 260 | let action = if dialog.export { "Export" } else { "Print" }; |
| 261 | "Export…" | ||
| 262 | } else { | ||
| 263 | "Print…" | ||
| 264 | }; | ||
| 265 | let go = ui::button(ui, "go", action).clicked || entered; | 261 | let go = ui::button(ui, "go", action).clicked || entered; |
| 266 | ui.close(); | 262 | ui.close(); |
| 267 | ui.close(); | 263 | ui.close(); |
crates/snowbound/src/protection.rs+5-5| ... | @@ -442,9 +442,9 @@ impl State { | ... | @@ -442,9 +442,9 @@ impl State { |
| 442 | }; | 442 | }; |
| 443 | text(ui, "status", &status, false); | 443 | text(ui, "status", &status, false); |
| 444 | buttons = if protected { | 444 | buttons = if protected { |
| 445 | &["Change Password…", "Remove Password…", "Lock All", "Close"] | 445 | &["Change Password", "Remove Password", "Lock All", "Close"] |
| 446 | } else { | 446 | } else { |
| 447 | &["Set Password…", "Lock All", "Close"] | 447 | &["Set Password", "Lock All", "Close"] |
| 448 | }; | 448 | }; |
| 449 | } | 449 | } |
| 450 | Dialog::Set([value, confirm], refused) => { | 450 | Dialog::Set([value, confirm], refused) => { |
| ... | @@ -527,9 +527,9 @@ impl State { | ... | @@ -527,9 +527,9 @@ impl State { |
| 527 | self.ui.close_popup(id()); | 527 | self.ui.close_popup(id()); |
| 528 | return self.lock_all(); | 528 | return self.lock_all(); |
| 529 | } | 529 | } |
| 530 | (Some("Set Password…"), _) => Some(Dialog::Set(Default::default(), None)), | 530 | (Some("Set Password"), _) => Some(Dialog::Set(Default::default(), None)), |
| 531 | (Some("Change Password…"), _) => Some(Dialog::Change(Default::default(), None)), | 531 | (Some("Change Password"), _) => Some(Dialog::Change(Default::default(), None)), |
| 532 | (Some("Remove Password…"), _) => Some(Dialog::Remove(Zeroizing::default(), false)), | 532 | (Some("Remove Password"), _) => Some(Dialog::Remove(Zeroizing::default(), false)), |
| 533 | (Some(_), Dialog::Unlock(value, _)) => match library.unlock(&path, &value) { | 533 | (Some(_), Dialog::Unlock(value, _)) => match library.unlock(&path, &value) { |
| 534 | Ok(()) => { | 534 | Ok(()) => { |
| 535 | self.commands | 535 | self.commands |
crates/snowbound/src/recording.rs+1-1| ... | @@ -495,7 +495,7 @@ impl State { | ... | @@ -495,7 +495,7 @@ impl State { |
| 495 | let title = | 495 | let title = |
| 496 | crate::commands::command(crate::commands::Id::Transport(Transport::SeekTo)) | 496 | crate::commands::command(crate::commands::Id::Transport(Transport::SeekTo)) |
| 497 | .title; | 497 | .title; |
| 498 | node.set_label(title.trim_end_matches('…')); | 498 | node.set_label(title); |
| 499 | node.set_value(clock_text); | 499 | node.set_value(clock_text); |
| 500 | } | 500 | } |
| 501 | if clicked { | 501 | if clicked { |
crates/snowbound/src/save_as.rs+1-1| ... | @@ -135,7 +135,7 @@ impl State { | ... | @@ -135,7 +135,7 @@ impl State { |
| 135 | } | 135 | } |
| 136 | crate::buttons(ui); | 136 | crate::buttons(ui); |
| 137 | let cancel = ui::button(ui, "cancel", "Cancel").clicked; | 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 | ui.close(); | 139 | ui.close(); |
| 140 | ui.close(); | 140 | ui.close(); |
| 141 | if !go && !cancel { | 141 | if !go && !cancel { |
crates/snowbound/src/sidebar.rs+3-3| ... | @@ -279,7 +279,7 @@ fn sidebar( | ... | @@ -279,7 +279,7 @@ fn sidebar( |
| 279 | ( | 279 | ( |
| 280 | "shared", | 280 | "shared", |
| 281 | Leading::Icon(art::LINK), | 281 | Leading::Icon(art::LINK), |
| 282 | "Open Shared…", | 282 | "Open Shared", |
| 283 | Action::OpenShared, | 283 | Action::OpenShared, |
| 284 | ), | 284 | ), |
| 285 | ( | 285 | ( |
| ... | @@ -1267,13 +1267,13 @@ impl crate::State { | ... | @@ -1267,13 +1267,13 @@ impl crate::State { |
| 1267 | ( | 1267 | ( |
| 1268 | "shared", | 1268 | "shared", |
| 1269 | Leading::Icon(art::LINK), | 1269 | Leading::Icon(art::LINK), |
| 1270 | "Open Shared Notebook…", | 1270 | "Open Shared Notebook", |
| 1271 | crate::Command::OpenShared, | 1271 | crate::Command::OpenShared, |
| 1272 | ), | 1272 | ), |
| 1273 | ( | 1273 | ( |
| 1274 | "server", | 1274 | "server", |
| 1275 | Leading::Icon(art::SERVER), | 1275 | Leading::Icon(art::SERVER), |
| 1276 | "Open Notebook from Server…", | 1276 | "Open Notebook from Server", |
| 1277 | crate::Command::OpenFromServer(None), | 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,7 +101,7 @@ pub fn gallery(ui: &mut Ui, id: Id, anchor: Anchor, symbols: &[char]) -> Option< |
| 101 | "more", | 101 | "more", |
| 102 | Spec { | 102 | Spec { |
| 103 | size: [fill(), fill()], | 103 | size: [fill(), fill()], |
| 104 | text: Some("More Symbols…"), | 104 | text: Some("More Symbols"), |
| 105 | icon: Some(art::SYMBOL), | 105 | icon: Some(art::SYMBOL), |
| 106 | gap: 6.0, | 106 | gap: 6.0, |
| 107 | ..Spec::default() | 107 | ..Spec::default() |
crates/snowbound/src/sync.rs+1-1| ... | @@ -739,7 +739,7 @@ fn build(ui: &mut Ui, facts: &Facts) -> Picked { | ... | @@ -739,7 +739,7 @@ fn build(ui: &mut Ui, facts: &Facts) -> Picked { |
| 739 | &format!("Using the system’s connection. Snowbound couldn’t sign in: {notice}"), | 739 | &format!("Using the system’s connection. Snowbound couldn’t sign in: {notice}"), |
| 740 | theme.text_dim, | 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 | ui.close(); | 744 | ui.close(); |
| 745 | } | 745 | } |
crates/snowbound/src/tags.rs+3-3| ... | @@ -515,11 +515,11 @@ impl State { | ... | @@ -515,11 +515,11 @@ impl State { |
| 515 | let new = button( | 515 | let new = button( |
| 516 | ui, | 516 | ui, |
| 517 | "new", | 517 | "new", |
| 518 | "New Tag…", | 518 | "New Tag", |
| 519 | list.tags.len() < commands::TAGS && list.editor.is_none(), | 519 | list.tags.len() < commands::TAGS && list.editor.is_none(), |
| 520 | ); | 520 | ); |
| 521 | modify = modify.or_else(|| { | 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 | .then_some(list.selected) | 523 | .then_some(list.selected) |
| 524 | .flatten() | 524 | .flatten() |
| 525 | }); | 525 | }); |
| ... | @@ -1035,7 +1035,7 @@ fn gallery(ui: &mut Ui, id: Id, anchor: Anchor, snowbound: bool) -> Option<Pick> | ... | @@ -1035,7 +1035,7 @@ fn gallery(ui: &mut Ui, id: Id, anchor: Anchor, snowbound: bool) -> Option<Pick> |
| 1035 | ..Spec::default() | 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 | picked = Some(Pick::Picture); | 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,7 +807,7 @@ pub(crate) fn gallery( |
| 807 | "customize", | 807 | "customize", |
| 808 | Spec { | 808 | Spec { |
| 809 | size: [fill(), fill()], | 809 | size: [fill(), fill()], |
| 810 | text: Some("Customize…"), | 810 | text: Some("Customize"), |
| 811 | ..Spec::default() | 811 | ..Spec::default() |
| 812 | }, | 812 | }, |
| 813 | ); | 813 | ); |
crates/snowbound/src/unpack.rs+1-1| ... | @@ -154,7 +154,7 @@ impl State { | ... | @@ -154,7 +154,7 @@ impl State { |
| 154 | ..Spec::default() | 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 | let root = dialog.folder.join(dialog.name.trim()); | 158 | let root = dialog.folder.join(dialog.name.trim()); |
| 159 | let full = format!("Full path: {}", root.display()); | 159 | let full = format!("Full path: {}", root.display()); |
| 160 | ui.leaf( | 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,7 +452,7 @@ fn a_version_copied_into_its_section_is_listed_and_found() { |
| 452 | let notebook = | 452 | let notebook = |
| 453 | Path::new(env!("CARGO_MANIFEST_DIR")).join("../../corpus/page-versions/candidate-restore"); | 453 | Path::new(env!("CARGO_MANIFEST_DIR")).join("../../corpus/page-versions/candidate-restore"); |
| 454 | let mut steps = version_menu(); | 454 | let mut steps = version_menu(); |
| 455 | // Copy Page To…, then the section itself. | 455 | // Copy Page To, then the section itself. |
| 456 | steps.extend(["key Down", "key Down", "key Down", "key Enter", "settle"].map(String::from)); | 456 | steps.extend(["key Down", "key Down", "key Down", "key Enter", "settle"].map(String::from)); |
| 457 | steps.extend(["key Down", "key Enter", "accessibility copied"].map(String::from)); | 457 | steps.extend(["key Down", "key Enter", "accessibility copied"].map(String::from)); |
| 458 | steps.extend(search("Second author", "found")); | 458 | steps.extend(search("Second author", "found")); |
| ... | @@ -674,7 +674,7 @@ fn open_shared_notebook_is_offered_with_notebooks_open() { | ... | @@ -674,7 +674,7 @@ fn open_shared_notebook_is_offered_with_notebooks_open() { |
| 674 | let [sidebar, dialog] = replay_with(&scratch, Some(&notebook), &steps, &variables) | 674 | let [sidebar, dialog] = replay_with(&scratch, Some(&notebook), &steps, &variables) |
| 675 | .try_into() | 675 | .try_into() |
| 676 | .unwrap(); | 676 | .unwrap(); |
| 677 | assert!(sidebar.contains(r#""Open Shared…""#), "{sidebar}"); | 677 | assert!(sidebar.contains(r#""Open Shared""#), "{sidebar}"); |
| 678 | assert!( | 678 | assert!( |
| 679 | dialog.contains(r#"Dialog "Open Shared Notebook""#), | 679 | dialog.contains(r#"Dialog "Open Shared Notebook""#), |
| 680 | "{dialog}" | 680 | "{dialog}" |
tools/RELEASE.md+2-2| ... | @@ -230,7 +230,7 @@ frames from the symbol table. | ... | @@ -230,7 +230,7 @@ frames from the symbol table. |
| 230 | 230 | ||
| 231 | `update.rs` runs one thread. With automatic checks on (Options, General) and a | 231 | `update.rs` runs one thread. With automatic checks on (Options, General) and a |
| 232 | published build, it checks shortly after launch and then daily, skipping while | 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 | palette elsewhere) checks at once and reports what it found. A check reads | 234 | palette elsewhere) checks at once and reports what it found. A check reads |
| 235 | `latest.json`, then the named build's `build.json` and signature, then | 235 | `latest.json`, then the named build's `build.json` and signature, then |
| 236 | `history.json` and the `build.json` of each build since its own, and | 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,7 +241,7 @@ the app unpacked into `.Snowbound.app.update` next to the bundle on macOS, the |
| 241 | executable into `.snowbound.update` next to it on Linux (`.snowbound.exe.update` on | 241 | executable into `.snowbound.update` next to it on Linux (`.snowbound.exe.update` on |
| 242 | Windows). The sync status icon gets a dot, and its popup says what the update | 242 | Windows). The sync status icon gets a dot, and its popup says what the update |
| 243 | changes, unfolding to the titles, and offers Build Folder and Restart to | 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 | Restart to Update quits the app and starts the old executable with | 246 | Restart to Update quits the app and starts the old executable with |
| 247 | `--finish-update`, which waits for the app to exit, renames the old bundle (or | 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,7 +50,7 @@ snowbound title bar · toolbar · section frame · page tabs · the page as a |
| 50 | 50 | ||
| 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. | 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 | 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`). | 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 |