mirror of
https://github.com/zaphar/sheetsui.git
synced 2025-07-26 23:09:50 -04:00
Compare commits
No commits in common. "31037752a88d0315b912bcf5bf61853acfb7037f" and "b68894076378cf1c02bb0c5a1c9e6a8cecf8a94c" have entirely different histories.
31037752a8
...
b688940763
@ -36,7 +36,3 @@ sheetui path/to/file.xlsx # edit/view a spreadsheet
|
|||||||
```
|
```
|
||||||
|
|
||||||
<img src="./assets/screenshot.png" />
|
<img src="./assets/screenshot.png" />
|
||||||
|
|
||||||
## Reference
|
|
||||||
|
|
||||||
* [Documentation](./docs/index.md)
|
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
# The sheetui user documentation
|
|
||||||
|
|
||||||
## Running sheetui
|
|
||||||
|
|
||||||
`sheetui --help` will print out help for the command line tags.
|
|
||||||
|
|
||||||
Currently this will print out:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
Usage: sheetui [OPTIONS] <WORKBOOK>
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
<WORKBOOK>
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-l, --locale-name <LOCALE_NAME> [default: en]
|
|
||||||
-t, --timezone-name <TIMEZONE_NAME> [default: America/New_York]
|
|
||||||
--log-input <LOG_INPUT>
|
|
||||||
-h, --help Print help
|
|
||||||
-V, --version Print version
|
|
||||||
```
|
|
||||||
|
|
||||||
## User Interface
|
|
||||||
|
|
||||||
The sheetui user interface is loosely inspired by vim. It is a modal interface that is entirely keyboard driven. At nearly any time you can type `Alt-h` to get some context sensitive help.
|
|
||||||
|
|
||||||
### Navigation Mode
|
|
||||||
|
|
||||||
The interface will start out in navigation mode. You can navigate around the table and between the sheets using the following keybinds:
|
|
||||||
|
|
||||||
**Cell Navigation**
|
|
||||||
|
|
||||||
* `h`, ⬆️, and `TAB` will move one cell to the left.
|
|
||||||
* `l` and, ➡️ will move one cell to the right.
|
|
||||||
* `j`, ⬇️, and `Enter` will move one cell down.
|
|
||||||
* `k` ⬆️, will move one cell up.
|
|
||||||
|
|
||||||
**Sheet Navigation**
|
|
||||||
|
|
||||||
* `Ctrl-n` moves to the next sheet
|
|
||||||
* `Ctrl-p` moves to the prev sheet
|
|
||||||
|
|
||||||
Sheet navigation moving will loop around when you reach the ends.
|
|
||||||
|
|
||||||
**Numeric prefixes**
|
|
||||||
|
|
||||||
You can prefix each of the keybinds above with a numeric prefix to do them that many times. So typing `123h` will move to the left 123 times. Hitting `Esc` will clear the numeric prefix if you want to cancel it.
|
|
||||||
|
|
||||||
**Modifying the Sheet or Cells**
|
|
||||||
|
|
||||||
* `e` or `i` will enter CellEdit mode for the current cell.
|
|
||||||
* `Ctrl-h` will shorten the width of the column you are on.
|
|
||||||
* `Ctrl-l` will lengthen the width of the column you are on.
|
|
||||||
|
|
||||||
**Other Keybindings**
|
|
||||||
|
|
||||||
* `Ctrl-s` will save the sheet.
|
|
||||||
* `q` will exit the application.
|
|
||||||
* `:` will enter CommandMode.
|
|
||||||
|
|
||||||
<aside>Note that for `q` this will not currently prompt you if the sheet is not saved.</aside>
|
|
||||||
|
|
||||||
### CellEdit Mode
|
|
||||||
|
|
||||||
You enter CellEdit mode by hitting `e` or `i` while in navigation mode. Type what you want into the cell.
|
|
||||||
|
|
||||||
Starting with:
|
|
||||||
|
|
||||||
* `=` will treat what you type as a formula.
|
|
||||||
* `$` will treat it as us currency.
|
|
||||||
|
|
||||||
Typing a number will treat the contents as a number. While typing non-numeric text will treat it as text content. <aside>We do not yet support modifying the type of a cell after the fact. We may add this in the future.</aside>
|
|
||||||
|
|
||||||
For the most part this should work the same way you expect a spreadsheet to work.
|
|
||||||
|
|
||||||
* `Enter` will update the cell contents.
|
|
||||||
* `Esc` will cancel editing the cell and leave it unedited.
|
|
||||||
|
|
||||||
You can find the functions we support documented here: [ironcalc docs](https://docs.ironcalc.com/functions/lookup-and-reference.html)
|
|
||||||
|
|
||||||
### Command Mode
|
|
||||||
|
|
||||||
You enter command mode by typing `:` while in navigation mode. You can then type a command and hit `Enter` to execute it or `Esc` to cancel.
|
|
||||||
|
|
||||||
The currently supported commands are:
|
|
||||||
|
|
||||||
* `write [path]` save the current spreadsheet. If the path is provided it will save it to that path. If omitted it will save to the path you are currently editing. `w` is a shorthand alias for this command.
|
|
||||||
* `insert-rows [number]` Inserts a row into the sheet at your current row. If the number is provided then inserts that many rows. If omitted then just inserts one.
|
|
||||||
* `insert-cols [number]` Just line `insert-rows` but for columns.
|
|
||||||
* `rename-sheet [idx] <name>` rename a sheet. If the idx is provide then renames that sheet. If omitted then it renames the current sheet.
|
|
||||||
* `new-sheet [name]` Creates a new sheet. If the name is provided then uses that. If omitted then uses a default sheet name.
|
|
||||||
* `select-sheet <name>` Select a sheet by name.
|
|
||||||
* `edit <path>` Edit a new spreadsheet at the current path. `e` is a shorthand alias for this command.
|
|
||||||
* `quit` Quits the application. `q` is a shorthand alias for this command.
|
|
||||||
|
|
||||||
<aside>Note that in the case of `quit` and `edit` that we do not currently prompt you if the current spreadsheet has not been saved yet. So your changes will be discarded if you have not saved first.</aside>
|
|
@ -108,7 +108,7 @@ fn try_consume_new_sheet<'cmd, 'i: 'cmd>(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
if input.remaining() > 0 && !is_ws(&mut input) {
|
if input.remaining() > 0 && !is_ws(&mut input) {
|
||||||
return Err("Invalid command: Did you mean to type `new-sheet <arg>`?");
|
return Err("Invalid command: Did you mean to type `write <arg>`?");
|
||||||
}
|
}
|
||||||
let arg = input.span(0..).trim();
|
let arg = input.span(0..).trim();
|
||||||
return Ok(Some(Cmd::NewSheet(if arg.is_empty() {
|
return Ok(Some(Cmd::NewSheet(if arg.is_empty() {
|
||||||
@ -129,11 +129,11 @@ fn try_consume_select_sheet<'cmd, 'i: 'cmd>(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
if input.remaining() > 0 && !is_ws(&mut input) {
|
if input.remaining() > 0 && !is_ws(&mut input) {
|
||||||
return Err("Invalid command: Did you mean to type `select-sheet <sheet-name>`?");
|
return Err("Invalid command: Did you mean to type `write <sheet-name>`?");
|
||||||
}
|
}
|
||||||
let arg = input.span(0..).trim();
|
let arg = input.span(0..).trim();
|
||||||
if arg.is_empty() {
|
if arg.is_empty() {
|
||||||
return Err("Invalid command: Did you forget the sheet name? `select-sheet <sheet-name>`?");
|
return Err("Invalid command: Did you forget the sheet name? `write <sheet-name>`?");
|
||||||
}
|
}
|
||||||
return Ok(Some(Cmd::SelectSheet(arg)));
|
return Ok(Some(Cmd::SelectSheet(arg)));
|
||||||
}
|
}
|
||||||
@ -189,7 +189,7 @@ fn try_consume_insert_column<'cmd, 'i: 'cmd>(
|
|||||||
if let Ok(count) = arg.parse() {
|
if let Ok(count) = arg.parse() {
|
||||||
count
|
count
|
||||||
} else {
|
} else {
|
||||||
return Err("You must pass in a non negative number for the column count");
|
return Err("You must pass in a non negative number for the row count");
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
@ -385,16 +385,16 @@ impl<'ws> Workspace<'ws> {
|
|||||||
self.enter_dialog_mode(self.render_help_text());
|
self.enter_dialog_mode(self.render_help_text());
|
||||||
}
|
}
|
||||||
KeyCode::Char('n') if key.modifiers == KeyModifiers::CONTROL => {
|
KeyCode::Char('n') if key.modifiers == KeyModifiers::CONTROL => {
|
||||||
self.run_with_prefix(|ws: &mut Workspace<'_>| -> Result<()> {
|
for _ in 1..=self.state.get_n_prefix() {
|
||||||
ws.book.select_next_sheet();
|
self.book.select_next_sheet();
|
||||||
Ok(())
|
}
|
||||||
})?;
|
self.state.reset_n_prefix();
|
||||||
}
|
}
|
||||||
KeyCode::Char('p') if key.modifiers == KeyModifiers::CONTROL => {
|
KeyCode::Char('p') if key.modifiers == KeyModifiers::CONTROL => {
|
||||||
self.run_with_prefix(|ws: &mut Workspace<'_>| -> Result<()> {
|
for _ in 1..=self.state.get_n_prefix() {
|
||||||
ws.book.select_prev_sheet();
|
self.book.select_prev_sheet();
|
||||||
Ok(())
|
}
|
||||||
})?;
|
self.state.reset_n_prefix();
|
||||||
}
|
}
|
||||||
KeyCode::Char('s')
|
KeyCode::Char('s')
|
||||||
if key.modifiers == KeyModifiers::HYPER
|
if key.modifiers == KeyModifiers::HYPER
|
||||||
@ -403,22 +403,42 @@ impl<'ws> Workspace<'ws> {
|
|||||||
self.save_file()?;
|
self.save_file()?;
|
||||||
}
|
}
|
||||||
KeyCode::Char('l') if key.modifiers == KeyModifiers::CONTROL => {
|
KeyCode::Char('l') if key.modifiers == KeyModifiers::CONTROL => {
|
||||||
self.run_with_prefix(|ws: &mut Workspace<'_>| -> Result<()> {
|
for _ in 1..=self.state.get_n_prefix() {
|
||||||
let Address { row: _, col } = &ws.book.location;
|
let Address { row: _, col } = &self.book.location;
|
||||||
ws.book
|
self.book
|
||||||
.set_col_size(*col, ws.book.get_col_size(*col)? + 1)?;
|
.set_col_size(*col, self.book.get_col_size(*col)? + 1)?;
|
||||||
Ok(())
|
}
|
||||||
})?;
|
self.state.reset_n_prefix();
|
||||||
}
|
}
|
||||||
KeyCode::Char('h') if key.modifiers == KeyModifiers::CONTROL => {
|
KeyCode::Char('h') if key.modifiers == KeyModifiers::CONTROL => {
|
||||||
self.run_with_prefix(|ws: &mut Workspace<'_>| -> Result<()> {
|
for _ in 1..=self.state.get_n_prefix() {
|
||||||
let Address { row: _, col } = &ws.book.location;
|
let Address { row: _, col } = &self.book.location;
|
||||||
let curr_size = ws.book.get_col_size(*col)?;
|
let curr_size = self.book.get_col_size(*col)?;
|
||||||
if curr_size > 1 {
|
if curr_size > 1 {
|
||||||
ws.book.set_col_size(*col, curr_size - 1)?;
|
self.book.set_col_size(*col, curr_size - 1)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
}
|
||||||
})?;
|
self.state.reset_n_prefix();
|
||||||
|
}
|
||||||
|
KeyCode::Char('r') if key.modifiers == KeyModifiers::CONTROL => {
|
||||||
|
for _ in 1..=self.state.get_n_prefix() {
|
||||||
|
let (row_count, _) = self.book.get_size()?;
|
||||||
|
self.book.update_entry(
|
||||||
|
&Address {
|
||||||
|
row: row_count + 1,
|
||||||
|
col: 1,
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
)?;
|
||||||
|
let (row, _) = self.book.get_size()?;
|
||||||
|
let mut loc = self.book.location.clone();
|
||||||
|
if loc.row < row as usize {
|
||||||
|
loc.row = row as usize;
|
||||||
|
self.book.move_to(&loc)?;
|
||||||
|
}
|
||||||
|
self.handle_movement_change();
|
||||||
|
}
|
||||||
|
self.state.reset_n_prefix();
|
||||||
}
|
}
|
||||||
KeyCode::Char('q') => {
|
KeyCode::Char('q') => {
|
||||||
return Ok(Some(ExitCode::SUCCESS));
|
return Ok(Some(ExitCode::SUCCESS));
|
||||||
|
@ -35,10 +35,8 @@ impl<'ws> Workspace<'ws> {
|
|||||||
Box::new(|rect: Rect, buf: &mut Buffer, ws: &mut Self| {
|
Box::new(|rect: Rect, buf: &mut Buffer, ws: &mut Self| {
|
||||||
let [text_rect, info_rect] = Layout::horizontal(vec![Constraint::Fill(1),Constraint::Fill(1)]).areas(rect);
|
let [text_rect, info_rect] = Layout::horizontal(vec![Constraint::Fill(1),Constraint::Fill(1)]).areas(rect);
|
||||||
ws.text_area.render(text_rect, buf);
|
ws.text_area.render(text_rect, buf);
|
||||||
let hint = Paragraph::new(vec![
|
let hint = Paragraph::new(vec![Line::from(""),Line::from("ALT-h to toggle help dialog").centered()]);
|
||||||
Line::from(""),
|
// TODO(zaphar): Show a small getting-started text?
|
||||||
Line::from("ALT-h to toggle help dialog").centered()
|
|
||||||
]);
|
|
||||||
hint.render(info_rect, buf);
|
hint.render(info_rect, buf);
|
||||||
}),
|
}),
|
||||||
Box::new(move |rect: Rect, buf: &mut Buffer, ws: &mut Self| {
|
Box::new(move |rect: Rect, buf: &mut Buffer, ws: &mut Self| {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user