mirror of
https://github.com/narrrl/narl-workspace.git
synced 2026-11-03 16:41:27 +01:00
special: keys go to a tile, and Esc only ever leaves
Inside the special workspace the agenda and the calendar could only be reached with Alt-h/j/k/l, which a desktop that binds Alt to its own windows never lets through, and the same keys meant two things: t on the agenda put the whole workspace away, t elsewhere went to it. Three esc buttons, one per tile, all closed everything. Now b, t and C only ever go to their tile, from anywhere. Tab and Shift-Tab walk the tiles, as they walk the spaces elsewhere; l past the board's last list goes on into the agenda, h in the agenda back to the board, j past its end into the calendar. S toggles the workspace, and q and Esc leave it from whichever tile — after leaving fullscreen first. The tile buttons are gone; each tile's head shows its key instead, lit on the one with focus, and the bar has a special pill beside the spaces. A document opened from the workspace — a day of the calendar, an entry of the agenda, today's entry, a card's notes — comes back to the tile it was opened from on q. The board tile shows the board of the space being browsed, or else the last one shown, so coming from the diary, which has none, no longer leaves it empty. Card and list actions follow the board on screen, not the browsed space.
This commit is contained in:
1 parent
0245402ead
commit
176aad8c6f
15 files changed
+182
-105
No files matched your search
@@ -137,7 +137,7 @@ it; in the document pane it is that document. In the browse pane:
|
|||||||
| `R` | rename what is highlighted — a document, a folder or a file — starting from its name (`:name!` renames the space) |
|
| `R` | rename what is highlighted — a document, a folder or a file — starting from its name (`:name!` renames the space) |
|
||||||
| `m` / `p` | pick nodes up — `m` again puts one back, `M` takes the whole folder — then drop them all into the folder you walk to |
|
| `m` / `p` | pick nodes up — `m` again puts one back, `M` takes the whole folder — then drop them all into the folder you walk to |
|
||||||
| `Ctrl`/`Shift`-click | pick a row up / every row from the cursor to it; drag picked-up rows onto a folder, a crumb or a space |
|
| `Ctrl`/`Shift`-click | pick a row up / every row from the cursor to it; drag picked-up rows onto a folder, a crumb or a space |
|
||||||
| `S` | the special workspace: the board, the agenda and the calendar side by side; `Alt-h`/`j`/`k`/`l` move between them, `f` gives one the screen |
|
| `S` | the special workspace: the board, the agenda and the calendar side by side; `Tab` or `Alt-h`/`j`/`k`/`l` move between them, `f` gives one the screen, `Esc` leaves |
|
||||||
| `b` | the board of this space, in the special workspace; a digit there shows another space's |
|
| `b` | the board of this space, in the special workspace; a digit there shows another space's |
|
||||||
| `t` | the agenda: what is late, due this week, or in hand, on every board, in the special workspace |
|
| `t` | the agenda: what is late, due this week, or in hand, on every board, in the special workspace |
|
||||||
| `T` | today's diary entry, started if there is none |
|
| `T` | today's diary entry, started if there is none |
|
||||||
@@ -244,7 +244,7 @@ start as `sftp_host_key` in the data directory.
|
|||||||
|
|
||||||
`b`, the board door on the rail, or `:board` swaps the tree for the special
|
`b`, the board door on the rail, or `:board` swaps the tree for the special
|
||||||
workspace — the board of the space being browsed, with the agenda and the
|
workspace — the board of the space being browsed, with the agenda and the
|
||||||
calendar beside it — and swaps it back. Every space has one except the
|
calendar beside it — and `Esc` swaps it back. Every space has one except the
|
||||||
diary — a journal has no backlog — and `:set board` / `:set noboard` changes
|
diary — a journal has no backlog — and `:set board` / `:set noboard` changes
|
||||||
that for the space you are in (the dimmed board door on the rail does the
|
that for the space you are in (the dimmed board door on the rail does the
|
||||||
same). A new board starts with `backlog`, `doing` and `done`, and the columns mean
|
same). A new board starts with `backlog`, `doing` and `done`, and the columns mean
|
||||||
|
|||||||
@@ -4,30 +4,38 @@
|
|||||||
|
|
||||||
The board, the agenda and the [calendar](Diary.md#the-calendar) share one
|
The board, the agenda and the [calendar](Diary.md#the-calendar) share one
|
||||||
workspace of their own, as the desktop's special workspace holds what is
|
workspace of their own, as the desktop's special workspace holds what is
|
||||||
looked at in passing. `S` (or `:special`) puts it up and away again. It is
|
looked at in passing. `S` (or `:special`, or the `special` pill in the bar)
|
||||||
three tiles: the board on the left, the agenda over the calendar on the right.
|
puts it up and away again. It is three tiles: the board on the left, the
|
||||||
|
agenda over the calendar on the right.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
| key | what it does |
|
| key | what it does |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `S` | put the special workspace up, or away |
|
| `S` | put the special workspace up, or away |
|
||||||
| `b`, `t`, `C` | straight to the board, the agenda or the calendar; again on the one with focus puts the workspace away |
|
| `b`, `t`, `C` | to the board, the agenda or the calendar, from anywhere |
|
||||||
| `Alt-h` / `Alt-j` / `Alt-k` / `Alt-l` | focus the tile that way |
|
| `Tab` / `Shift-Tab` | the next / previous tile |
|
||||||
|
| `Alt-h` / `Alt-j` / `Alt-k` / `Alt-l` | the tile that way |
|
||||||
|
| `l` past the last list, `h` in the agenda, `j` past its end | on into the tile beside |
|
||||||
| `f` | the focused tile fills the screen; again or `Esc` to leave |
|
| `f` | the focused tile fills the screen; again or `Esc` to leave |
|
||||||
| `1` … `9` | switch space, and the board with it |
|
| `1` … `9` | switch space, and the board with it |
|
||||||
| `q`, `Esc` | back to the tree |
|
| `q`, `Esc` | leave, back to where you were |
|
||||||
|
|
||||||
The keys go to the tile with focus, which wears the light border; the other
|
The keys go to the tile with focus, which wears the light border and its key
|
||||||
tiles keep their cursors, dimmed. Everything in it is read again each time it
|
lit in its head; the other tiles keep their cursors, dimmed. `b`, `t` and `C`
|
||||||
comes up.
|
only ever go to their tile, and `Esc` only ever leaves — or, with a tile
|
||||||
|
filling the screen, leaves that first. A document opened from the workspace —
|
||||||
|
a card's notes, a day of the calendar, an entry of the agenda, `T` — comes
|
||||||
|
back to the tile it was opened from on `q`. Everything in it is read again
|
||||||
|
each time it comes up.
|
||||||
|
|
||||||
## The board
|
## The board
|
||||||
|
|
||||||
Every space except the diary has a board. `b`, the board button on the rail, or
|
Every space except the diary has a board. `b`, the board button on the rail, or
|
||||||
`:board` shows it in the special workspace, and `q` goes back to the tree. The
|
`:board` shows it in the special workspace, and `Esc` goes back to the tree.
|
||||||
head of the board names every space that has one, with the digit that goes
|
It is the board of the space being browsed, or, in a space without one such as
|
||||||
there.
|
the diary, the last board shown. The head of the board names every space that
|
||||||
|
has one, with the digit that goes there.
|
||||||
|
|
||||||
A new board has three lists: `backlog`, `doing` and `done`. The lists mean
|
A new board has three lists: `backlog`, `doing` and `done`. The lists mean
|
||||||
something by where they stand, not by their names:
|
something by where they stand, not by their names:
|
||||||
@@ -55,7 +63,7 @@ a week fold away until `z` shows them.
|
|||||||
| `z` | show done cards older than a week |
|
| `z` | show done cards older than a week |
|
||||||
| `F` | a letter over every card; type it to jump there |
|
| `F` | a letter over every card; type it to jump there |
|
||||||
| `x`, `dd` | delete the card |
|
| `x`, `dd` | delete the card |
|
||||||
| `q`, `Esc` | put the special workspace away |
|
| `q`, `Esc` | leave the special workspace |
|
||||||
|
|
||||||
### Capturing a card
|
### Capturing a card
|
||||||
|
|
||||||
@@ -121,14 +129,15 @@ special workspace beside the board.
|
|||||||
|
|
||||||
| key | what it does |
|
| key | what it does |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `j` / `k` | down / up, on into the diary entries |
|
| `j` / `k` | down / up, on into the diary entries, then into the calendar |
|
||||||
|
| `h` | to the board |
|
||||||
| `Enter` | to the card on its own board, in the tile beside, or open the entry |
|
| `Enter` | to the card on its own board, in the tile beside, or open the entry |
|
||||||
| `Space` | finish the card where it stands |
|
| `Space` | finish the card where it stands |
|
||||||
| `D` | due date |
|
| `D` | due date |
|
||||||
| `H` / `L` | to the list left / right on its board |
|
| `H` / `L` | to the list left / right on its board |
|
||||||
| `:` | the command line with the agenda kept up, so `:repeat` acts on the card |
|
| `:` | the command line with the agenda kept up, so `:repeat` acts on the card |
|
||||||
| `T` | today's diary entry |
|
| `T` | today's diary entry |
|
||||||
| `t`, `q`, `Esc` | put the special workspace away |
|
| `q`, `Esc` | leave the special workspace |
|
||||||
|
|
||||||
The agenda opens by itself once a day when something is late or due. "Today"
|
The agenda opens by itself once a day when something is late or due. "Today"
|
||||||
is the day in `WORKSPACE_TIMEZONE`, the same as for the mails, so a browser in
|
is the day in `WORKSPACE_TIMEZONE`, the same as for the mails, so a browser in
|
||||||
|
|||||||
+10
-9
@@ -42,14 +42,14 @@ arguments and `↑` / `↓` recall earlier lines.
|
|||||||
| `m / p` | pick nodes up, then drop them all into the folder you walk to |
|
| `m / p` | pick nodes up, then drop them all into the folder you walk to |
|
||||||
| `M` | pick up the whole folder — again puts it all back |
|
| `M` | pick up the whole folder — again puts it all back |
|
||||||
| `Ctrl / Shift-click` | pick a row up · every row up to it — drag them onto a folder |
|
| `Ctrl / Shift-click` | pick a row up · every row up to it — drag them onto a folder |
|
||||||
| `S` | the special workspace: the board, the agenda and the calendar side by side — again puts it away |
|
| `S` | the special workspace: the board, the agenda and the calendar side by side — S again, q or Esc leave it |
|
||||||
| `Alt-h/j/k/l · f` | in the special workspace: to the board, the agenda or the calendar — f gives one the screen |
|
| `Tab · Alt-h/j/k/l` | in the special workspace: the next tile, or the one that way — l past the last list and j past the agenda go on too |
|
||||||
| `t` | agenda — late, due this week, in hand, on every board — in the special workspace |
|
| `t` | agenda — late, due this week, in hand, on every board — in the special workspace |
|
||||||
| `T` | today's diary entry, started if there is none — from templates/day when the diary has one |
|
| `T` | today's diary entry, started if there is none — from templates/day when the diary has one |
|
||||||
| `C` | the diary by month, in the special workspace: h/l a day, j/k a week, H/L a month, . today, Enter opens or starts the day |
|
| `C` | the diary by month, in the special workspace: h/l a day, j/k a week, H/L a month, . today, Enter opens or starts the day |
|
||||||
| `[d / ]d` | the diary entry before / after the one open, by date |
|
| `[d / ]d` | the diary entry before / after the one open, by date |
|
||||||
| `Ctrl-P` | find anything, in every space — Enter goes to the result lit |
|
| `Ctrl-P` | find anything, in every space — Enter goes to the result lit |
|
||||||
| `b` | this space's board, in the special workspace — 1 … 9 there shows another space's |
|
| `b` | the board, in the special workspace: this space's, or the last one shown — 1 … 9 there shows another's |
|
||||||
| `c` | capture a card into this space's inbox, without leaving here |
|
| `c` | capture a card into this space's inbox, without leaving here |
|
||||||
| `Esc · q` | close the document, and its tile when there are several |
|
| `Esc · q` | close the document, and its tile when there are several |
|
||||||
| `/` | full-text search, inside the current space and its text files — Tab or Ctrl-N/P walk the results, Enter opens one |
|
| `/` | full-text search, inside the current space and its text files — Tab or Ctrl-N/P walk the results, Enter opens one |
|
||||||
@@ -70,7 +70,7 @@ The first list is the inbox, the last list is done.
|
|||||||
|
|
||||||
| key | what it does |
|
| key | what it does |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `h / l` | to the list left / right |
|
| `h / l` | to the list left / right — right of the last is the agenda |
|
||||||
| `j / k` | down / up the cards |
|
| `j / k` | down / up the cards |
|
||||||
| `gg / G` | first / last card of the list |
|
| `gg / G` | first / last card of the list |
|
||||||
| `J / K` | move the card down / up its list |
|
| `J / K` | move the card down / up its list |
|
||||||
@@ -84,20 +84,21 @@ The first list is the inbox, the last list is done.
|
|||||||
| `F` | a letter over every card in view — type it to put the cursor there |
|
| `F` | a letter over every card in view — type it to put the cursor there |
|
||||||
| `r` | read the board again, for changes made elsewhere |
|
| `r` | read the board again, for changes made elsewhere |
|
||||||
| `x · dd` | delete the card (asks y/N) |
|
| `x · dd` | delete the card (asks y/N) |
|
||||||
| `q · Esc` | put the special workspace away |
|
| `q · Esc` | leave the special workspace |
|
||||||
|
|
||||||
### In the agenda
|
### In the agenda
|
||||||
|
|
||||||
| key | what it does |
|
| key | what it does |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `j / k` | down / up the cards, then what was written on this day in earlier years |
|
| `j / k` | down / up the cards, then what was written on this day in earlier years, then into the calendar |
|
||||||
|
| `h` | to the board |
|
||||||
| `Enter` | to the card, on its own board beside — or to the entry |
|
| `Enter` | to the card, on its own board beside — or to the entry |
|
||||||
| `Space` | done, where it stands |
|
| `Space` | done, where it stands |
|
||||||
| `D` | due date: fri, tm, +3d … — :due on the line |
|
| `D` | due date: fri, tm, +3d … — :due on the line |
|
||||||
| `:` | the line, with the agenda kept up — :repeat weekly acts on the card |
|
| `:` | the line, with the agenda kept up — :repeat weekly acts on the card |
|
||||||
| `H / L` | to the list left / right on its board |
|
| `H / L` | to the list left / right on its board |
|
||||||
| `T` | today's diary entry |
|
| `T` | today's diary entry |
|
||||||
| `t · q · Esc` | put the special workspace away |
|
| `q · Esc` | leave the special workspace — a document opened from it comes back here on q |
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
@@ -123,8 +124,8 @@ do: `:w!`, `:space! <name>`, `:import!`.
|
|||||||
|
|
||||||
| command | what it does |
|
| command | what it does |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `:board` | this space's board, in the special workspace — again puts it away |
|
| `:board` | this space's board, in the special workspace |
|
||||||
| `:special` | the special workspace: the board, the agenda and the calendar side by side — S toggles it, Alt-h/j/k/l moves between them, 1 … 9 picks the board |
|
| `:special` | the special workspace: the board, the agenda and the calendar side by side — S puts it up and away, Tab walks its tiles, 1 … 9 picks the board |
|
||||||
| `:card, :c <title>` | add a card — on the board to the list under the cursor, elsewhere to this space's inbox |
|
| `:card, :c <title>` | add a card — on the board to the list under the cursor, elsewhere to this space's inbox |
|
||||||
| `:card!` | add a card linked to the open document — Enter on it opens the document |
|
| `:card!` | add a card linked to the open document — Enter on it opens the document |
|
||||||
| `:due <when\|->` | due date of the card — on the board, or highlighted in the agenda: today, tm, fri, +3d, +2w, 10-31, 2026-10-31 — - clears |
|
| `:due <when\|->` | due date of the card — on the board, or highlighted in the agenda: today, tm, fri, +3d, +2w, 10-31, 2026-10-31 — - clears |
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ agenda.
|
|||||||
| `H` / `L` | a month back / forward |
|
| `H` / `L` | a month back / forward |
|
||||||
| `.` | today |
|
| `.` | today |
|
||||||
| `Enter` | open the day's entry, or start one dated that day |
|
| `Enter` | open the day's entry, or start one dated that day |
|
||||||
| `C`, `Esc` | put the special workspace away |
|
| `q`, `Esc` | leave the special workspace; an entry opened here comes back on `q` |
|
||||||
|
|
||||||
A dot marks a day with an entry. A number says how many when there are more
|
A dot marks a day with an entry. A number says how many when there are more
|
||||||
than one.
|
than one.
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ The rail of buttons on the right does the same as the keys, for a mouse.
|
|||||||
add a name and press `Enter`. `l` or `Enter` on the folder goes into it, and
|
add a name and press `Enter`. `l` or `Enter` on the folder goes into it, and
|
||||||
`h` comes back up.
|
`h` comes back up.
|
||||||
6. Press `c`, type `try the board`, and press `Enter`. That is a card in the
|
6. Press `c`, type `try the board`, and press `Enter`. That is a card in the
|
||||||
inbox of this space's board. Press `b` to see the board, and `b` again to go
|
inbox of this space's board. Press `b` to see the board, and `Esc` to go
|
||||||
back. It shares a special workspace with the agenda and the calendar: `S`
|
back. It shares a special workspace with the agenda and the calendar: `S`
|
||||||
puts it up, and `Alt-h`/`Alt-l` move between them.
|
puts it up and away, and `Tab` walks between them.
|
||||||
7. Press `T`. That is today's diary entry, started for you.
|
7. Press `T`. That is today's diary entry, started for you.
|
||||||
8. Press `?` for the list of every key and command. `h` / `l` walk its columns, `/` narrows it down, and `Esc` closes it.
|
8. Press `?` for the list of every key and command. `h` / `l` walk its columns, `/` narrows it down, and `Esc` closes it.
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -13,7 +13,7 @@ The keys are the desktop's. On a phone, every one of these has a button; see
|
|||||||
The first time you open the workspace each day, the **agenda** opens by itself
|
The first time you open the workspace each day, the **agenda** opens by itself
|
||||||
when anything is late or due. Any other time, press `t`. It opens in the
|
when anything is late or due. Any other time, press `t`. It opens in the
|
||||||
**special workspace**, beside the board and the calendar; `S` puts that up and
|
**special workspace**, beside the board and the calendar; `S` puts that up and
|
||||||
away from anywhere, and `Alt-h`/`Alt-l` move between its tiles.
|
away from anywhere, and `Tab` walks its tiles.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ Walk it with `j` and `k`. On a card:
|
|||||||
- `H` and `L` move it to the list left or right on its board;
|
- `H` and `L` move it to the list left or right on its board;
|
||||||
- `Enter` goes to the card on its own board, in the tile beside.
|
- `Enter` goes to the card on its own board, in the tile beside.
|
||||||
|
|
||||||
`Esc` or `t` puts the special workspace away. The bar keeps showing the `late` and `today`
|
`Esc` leaves the special workspace. The bar keeps showing the `late` and `today`
|
||||||
counts while either is above zero. More in [Boards and the agenda](Boards-and-Agenda.md).
|
counts while either is above zero. More in [Boards and the agenda](Boards-and-Agenda.md).
|
||||||
|
|
||||||
## At work: spaces, documents and tiles
|
## At work: spaces, documents and tiles
|
||||||
|
|||||||
+28
-13
@@ -295,7 +295,8 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The agenda's keys: j/k down the list, Enter to the card, Space finishes it.
|
* The agenda's keys: j/k down the list, Enter to the card, Space finishes it.
|
||||||
* Past the cards the cursor walks on into what was written on this day.
|
* Past the cards the cursor walks on into what was written on this day, and
|
||||||
|
* past that into the calendar under it; `h` is the board to its left.
|
||||||
*/
|
*/
|
||||||
function agendaKey(event: KeyboardEvent): boolean {
|
function agendaKey(event: KeyboardEvent): boolean {
|
||||||
const cards = workspace.agendaCards
|
const cards = workspace.agendaCards
|
||||||
@@ -303,19 +304,20 @@
|
|||||||
const past = workspace.agenda.on_this_day[workspace.agendaCursor - cards.length]
|
const past = workspace.agenda.on_this_day[workspace.agendaCursor - cards.length]
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case 'j':
|
case 'j':
|
||||||
case 'ArrowDown':
|
case 'ArrowDown': {
|
||||||
workspace.agendaCursor = Math.min(workspace.agendaCursor + 1, Math.max(cards.length + workspace.agenda.on_this_day.length - 1, 0))
|
const last = Math.max(cards.length + workspace.agenda.on_this_day.length - 1, 0)
|
||||||
|
if (workspace.agendaCursor >= last) workspace.specialFocus = 'calendar'
|
||||||
|
else workspace.agendaCursor += 1
|
||||||
return true
|
return true
|
||||||
|
}
|
||||||
case 'k':
|
case 'k':
|
||||||
case 'ArrowUp':
|
case 'ArrowUp':
|
||||||
workspace.agendaCursor = Math.max(workspace.agendaCursor - 1, 0)
|
workspace.agendaCursor = Math.max(workspace.agendaCursor - 1, 0)
|
||||||
return true
|
return true
|
||||||
case 'Enter':
|
case 'Enter':
|
||||||
if (card) void workspace.guard(() => workspace.goToCard(card))
|
if (card) void workspace.guard(() => workspace.goToCard(card))
|
||||||
else if (past) {
|
// Opened from the special workspace, so `q` on it comes back here.
|
||||||
workspace.closeSpecial()
|
else if (past) void workspace.guard(() => workspace.openPath(past.path))
|
||||||
void workspace.guard(() => workspace.openPath(past.path))
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
case ' ':
|
case ' ':
|
||||||
if (card) void workspace.guard(() => workspace.finishCard(card))
|
if (card) void workspace.guard(() => workspace.finishCard(card))
|
||||||
@@ -337,10 +339,12 @@
|
|||||||
if (card) void workspace.guard(() => workspace.sendAgendaCard(card, event.key === 'H' ? -1 : 1))
|
if (card) void workspace.guard(() => workspace.sendAgendaCard(card, event.key === 'H' ? -1 : 1))
|
||||||
return true
|
return true
|
||||||
case 'T':
|
case 'T':
|
||||||
workspace.closeSpecial()
|
|
||||||
void runCommand('today')
|
void runCommand('today')
|
||||||
return true
|
return true
|
||||||
case 't':
|
case 'h':
|
||||||
|
case 'ArrowLeft':
|
||||||
|
workspace.specialFocus = 'board'
|
||||||
|
return true
|
||||||
case 'q':
|
case 'q':
|
||||||
workspace.closeSpecial()
|
workspace.closeSpecial()
|
||||||
return true
|
return true
|
||||||
@@ -369,7 +373,6 @@
|
|||||||
case 'Enter':
|
case 'Enter':
|
||||||
void workspace.guard(() => workspace.openCalendarDay()).then(() => hooks.focusEditor())
|
void workspace.guard(() => workspace.openCalendarDay()).then(() => hooks.focusEditor())
|
||||||
return true
|
return true
|
||||||
case 'C':
|
|
||||||
case 'q':
|
case 'q':
|
||||||
workspace.closeSpecial()
|
workspace.closeSpecial()
|
||||||
return true
|
return true
|
||||||
@@ -638,7 +641,9 @@
|
|||||||
return true
|
return true
|
||||||
case 'l':
|
case 'l':
|
||||||
case 'ArrowRight':
|
case 'ArrowRight':
|
||||||
workspace.moveList(1)
|
// Right of the last list is the agenda, the tile beside the board.
|
||||||
|
if (workspace.listCursor >= (workspace.board?.lists.length ?? 0) - 1) workspace.specialFocus = 'agenda'
|
||||||
|
else workspace.moveList(1)
|
||||||
return true
|
return true
|
||||||
case 'j':
|
case 'j':
|
||||||
case 'ArrowDown':
|
case 'ArrowDown':
|
||||||
@@ -854,6 +859,13 @@
|
|||||||
const previous = pending
|
const previous = pending
|
||||||
pending = ''
|
pending = ''
|
||||||
|
|
||||||
|
// In the special workspace Tab walks its tiles, as it walks the spaces
|
||||||
|
// everywhere else; the digits still switch space, and the board with it.
|
||||||
|
if (key === 'Tab' && workspace.view === 'special') {
|
||||||
|
event.preventDefault()
|
||||||
|
workspace.cycleSpecial(event.shiftKey ? -1 : 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
if (spaceKey(event)) {
|
if (spaceKey(event)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
return
|
return
|
||||||
@@ -888,7 +900,8 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// `S`, as the desktop's special workspace is bound: the board, the
|
// `S`, as the desktop's special workspace is bound: the board, the
|
||||||
// agenda and the calendar, put up and away again.
|
// agenda and the calendar, put up and away again. `b`, `t` and `C` only
|
||||||
|
// ever go to their tile.
|
||||||
if (key === 'S') {
|
if (key === 'S') {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
void runCommand('special')
|
void runCommand('special')
|
||||||
@@ -909,7 +922,6 @@
|
|||||||
}
|
}
|
||||||
if (key === 'b') {
|
if (key === 'b') {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
workspace.closeSpecial()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (previous === 'g' && key === 'g') {
|
if (previous === 'g' && key === 'g') {
|
||||||
@@ -1307,6 +1319,9 @@
|
|||||||
.special > .agenda { grid-area: agenda; }
|
.special > .agenda { grid-area: agenda; }
|
||||||
.special > .calendar { grid-area: calendar; }
|
.special > .calendar { grid-area: calendar; }
|
||||||
.special .fill { height: 100%; }
|
.special .fill { height: 100%; }
|
||||||
|
/* The focused tile's key is lit, as its border is. */
|
||||||
|
.special > .window.focused :global(.tile-key) { background: var(--light); border-color: var(--light); color: var(--ink); }
|
||||||
|
|
||||||
/* One cursor is lit at a time: the other tiles keep theirs, dimmed. */
|
/* One cursor is lit at a time: the other tiles keep theirs, dimmed. */
|
||||||
.special > .window:not(.focused) :global(:is(.card.active, .row.at, .day.at)) {
|
.special > .window:not(.focused) :global(:is(.card.active, .row.at, .day.at)) {
|
||||||
background: var(--surface-alt);
|
background: var(--surface-alt);
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
/** An entry from an earlier year, in place in the diary. */
|
/** An entry from an earlier year, in place in the diary. */
|
||||||
function open(entry: DiaryEntry) {
|
function open(entry: DiaryEntry) {
|
||||||
workspace.closeSpecial()
|
|
||||||
void workspace.guard(() => workspace.openPath(entry.path))
|
void workspace.guard(() => workspace.openPath(entry.path))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@
|
|||||||
<div class="sheet" bind:this={sheet}>
|
<div class="sheet" bind:this={sheet}>
|
||||||
<header>
|
<header>
|
||||||
<span class="label rule"><span class="marker"></span><span class="title">agenda</span> {total} open</span>
|
<span class="label rule"><span class="marker"></span><span class="title">agenda</span> {total} open</span>
|
||||||
<button onclick={() => workspace.closeSpecial()}>esc</button>
|
<span class="kbd tile-key" title="the key that comes to this tile">t</span>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{#if workspace.agenda.diary_today === false}
|
{#if workspace.agenda.diary_today === false}
|
||||||
@@ -125,7 +124,9 @@
|
|||||||
<span class="gap"></span>
|
<span class="gap"></span>
|
||||||
<span class="kbd">T</span> diary
|
<span class="kbd">T</span> diary
|
||||||
<span class="gap"></span>
|
<span class="gap"></span>
|
||||||
<span class="kbd">t</span><span class="kbd">esc</span> close
|
<span class="kbd">tab</span> tile
|
||||||
|
<span class="gap"></span>
|
||||||
|
<span class="kbd">esc</span> leave
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,15 @@
|
|||||||
<button class="pill add" title="new space (:space! <name>)" onclick={() => hooks.openCommandLine(':space! ')}>
|
<button class="pill add" title="new space (:space! <name>)" onclick={() => hooks.openCommandLine(':space! ')}>
|
||||||
+
|
+
|
||||||
</button>
|
</button>
|
||||||
|
<!-- The special workspace, beside the others as the desktop bar shows its own. -->
|
||||||
|
<button
|
||||||
|
class="pill special"
|
||||||
|
aria-current={workspace.view === 'special'}
|
||||||
|
title="the special workspace: board, agenda, calendar (S)"
|
||||||
|
onclick={() => workspace.guard(() => workspace.toggleSpecial())}
|
||||||
|
>
|
||||||
|
<span class="slug">special</span>
|
||||||
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<span class="divider path"></span>
|
<span class="divider path"></span>
|
||||||
@@ -114,6 +123,7 @@
|
|||||||
.pill:hover .n { color: var(--light-dim); }
|
.pill:hover .n { color: var(--light-dim); }
|
||||||
.pill[aria-current='true'] .n { color: var(--ink); opacity: 0.55; }
|
.pill[aria-current='true'] .n { color: var(--ink); opacity: 0.55; }
|
||||||
.pill.add { color: var(--muted); }
|
.pill.add { color: var(--muted); }
|
||||||
|
.pill.special { margin-left: 6px; }
|
||||||
.pill.add:hover { color: var(--light); }
|
.pill.add:hover { color: var(--light); }
|
||||||
|
|
||||||
/* A crumb is a link along the path; a hairline grows under it on hover. */
|
/* A crumb is a link along the path; a hairline grows under it on hover. */
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
<div class="board">
|
<div class="board">
|
||||||
<div class="head label rule">
|
<div class="head label rule">
|
||||||
<span class="marker"></span>
|
<span class="marker"></span>
|
||||||
<span class="name">{workspace.space?.slug ?? ''}</span>
|
<span class="name">{workspace.boardSpace?.slug ?? ''}</span>
|
||||||
<span>board</span>
|
<span>board</span>
|
||||||
{#if workspace.board}<span class="count">{total} {total === 1 ? 'card' : 'cards'}</span>{/if}
|
{#if workspace.board}<span class="count">{total} {total === 1 ? 'card' : 'cards'}</span>{/if}
|
||||||
<!-- Every board is a digit away: the digit switches space, and the board
|
<!-- Every board is a digit away: the digit switches space, and the board
|
||||||
@@ -65,12 +65,13 @@
|
|||||||
{#if boards.length > 1}
|
{#if boards.length > 1}
|
||||||
<span class="boards">
|
<span class="boards">
|
||||||
{#each boards as { space, digit } (space.id)}
|
{#each boards as { space, digit } (space.id)}
|
||||||
<button class="other" class:here={space.id === workspace.spaceId} onclick={() => workspace.guard(() => workspace.switchSpace(digit === 0 ? 9 : digit - 1))}>
|
<button class="other" class:here={space.id === workspace.boardSpaceId} onclick={() => workspace.guard(() => workspace.switchSpace(digit === 0 ? 9 : digit - 1))}>
|
||||||
<span class="n">{digit}</span>{space.slug}
|
<span class="n">{digit}</span>{space.slug}
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
<span class="kbd tile-key" title="the key that comes to this tile">b</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="columns" bind:this={columns}>
|
<div class="columns" bind:this={columns}>
|
||||||
@@ -132,8 +133,8 @@
|
|||||||
</section>
|
</section>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="empty muted">
|
<div class="empty muted">
|
||||||
{#if !workspace.space?.has_board}
|
{#if !workspace.boardSpace}
|
||||||
{workspace.space?.slug ?? 'this space'} has no board — :set board turns one on{#if boards.length}, or a digit goes to one that has{/if}.
|
no space has a board — :set board gives the one you are in one.
|
||||||
{:else}
|
{:else}
|
||||||
no lists yet — :list <name> makes a column. then o adds a card to it.
|
no lists yet — :list <name> makes a column. then o adds a card to it.
|
||||||
{/if}
|
{/if}
|
||||||
@@ -152,6 +153,8 @@
|
|||||||
<span><span class="kbd">R</span> rename</span>
|
<span><span class="kbd">R</span> rename</span>
|
||||||
<span><span class="kbd">dd</span> delete</span>
|
<span><span class="kbd">dd</span> delete</span>
|
||||||
<span><span class="kbd">1</span>…<span class="kbd">9</span> board</span>
|
<span><span class="kbd">1</span>…<span class="kbd">9</span> board</span>
|
||||||
|
<span><span class="kbd">tab</span> tile</span>
|
||||||
|
<span><span class="kbd">esc</span> leave</span>
|
||||||
<span class="rule-hint">first list is the inbox · last list is done</span>
|
<span class="rule-hint">first list is the inbox · last list is done</span>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
@@ -172,6 +175,8 @@
|
|||||||
.head .marker { background: var(--light); }
|
.head .marker { background: var(--light); }
|
||||||
.head .name { color: var(--light); }
|
.head .name { color: var(--light); }
|
||||||
.boards { display: flex; gap: 4px; margin-left: 8px; }
|
.boards { display: flex; gap: 4px; margin-left: 8px; }
|
||||||
|
/* After the rule, at the right end, where the other tiles have theirs. */
|
||||||
|
.head .tile-key { order: 1; margin-left: 12px; }
|
||||||
.other {
|
.other {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
<span class="label rule"><span class="marker"></span><span class="title">{title}</span> {workspace.calendarEntries.length} entries</span>
|
<span class="label rule"><span class="marker"></span><span class="title">{title}</span> {workspace.calendarEntries.length} entries</span>
|
||||||
<button onclick={() => workspace.guard(() => workspace.moveCalendar(0, -1))} aria-label="previous month">‹</button>
|
<button onclick={() => workspace.guard(() => workspace.moveCalendar(0, -1))} aria-label="previous month">‹</button>
|
||||||
<button onclick={() => workspace.guard(() => workspace.moveCalendar(0, 1))} aria-label="next month">›</button>
|
<button onclick={() => workspace.guard(() => workspace.moveCalendar(0, 1))} aria-label="next month">›</button>
|
||||||
<button onclick={() => workspace.closeSpecial()}>esc</button>
|
<span class="kbd tile-key" title="the key that comes to this tile">C</span>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<ol class="grid">
|
<ol class="grid">
|
||||||
@@ -89,7 +89,9 @@
|
|||||||
<span class="gap"></span>
|
<span class="gap"></span>
|
||||||
<span class="kbd">↵</span> open
|
<span class="kbd">↵</span> open
|
||||||
<span class="gap"></span>
|
<span class="gap"></span>
|
||||||
<span class="kbd">C</span><span class="kbd">esc</span> close
|
<span class="kbd">tab</span> tile
|
||||||
|
<span class="gap"></span>
|
||||||
|
<span class="kbd">esc</span> leave
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -72,14 +72,14 @@
|
|||||||
['m / p', 'pick nodes up, then drop them all into the folder you walk to'],
|
['m / p', 'pick nodes up, then drop them all into the folder you walk to'],
|
||||||
['M', 'pick up the whole folder — again puts it all back'],
|
['M', 'pick up the whole folder — again puts it all back'],
|
||||||
['Ctrl / Shift-click', 'pick a row up · every row up to it — drag them onto a folder'],
|
['Ctrl / Shift-click', 'pick a row up · every row up to it — drag them onto a folder'],
|
||||||
['S', 'the special workspace: the board, the agenda and the calendar side by side — again puts it away'],
|
['S', 'the special workspace: the board, the agenda and the calendar side by side — S again, q or Esc leave it'],
|
||||||
['Alt-h/j/k/l · f', 'in the special workspace: to the board, the agenda or the calendar — f gives one the screen'],
|
['Tab · Alt-h/j/k/l', 'in the special workspace: the next tile, or the one that way — l past the last list and j past the agenda go on too'],
|
||||||
['t', 'agenda — late, due this week, in hand, on every board — in the special workspace'],
|
['t', 'agenda — late, due this week, in hand, on every board — in the special workspace'],
|
||||||
['T', "today's diary entry, started if there is none — from templates/day when the diary has one"],
|
['T', "today's diary entry, started if there is none — from templates/day when the diary has one"],
|
||||||
['C', 'the diary by month, in the special workspace: h/l a day, j/k a week, H/L a month, . today, Enter opens or starts the day'],
|
['C', 'the diary by month, in the special workspace: h/l a day, j/k a week, H/L a month, . today, Enter opens or starts the day'],
|
||||||
['[d / ]d', 'the diary entry before / after the one open, by date'],
|
['[d / ]d', 'the diary entry before / after the one open, by date'],
|
||||||
['Ctrl-P', 'find anything, in every space — Enter goes to the result lit'],
|
['Ctrl-P', 'find anything, in every space — Enter goes to the result lit'],
|
||||||
['b', "this space's board, in the special workspace — 1 … 9 there shows another space's"],
|
['b', "the board, in the special workspace: this space's, or the last one shown — 1 … 9 there shows another's"],
|
||||||
['c', "capture a card into this space's inbox, without leaving here"],
|
['c', "capture a card into this space's inbox, without leaving here"],
|
||||||
['Esc · q', 'close the document, and its tile when there are several'],
|
['Esc · q', 'close the document, and its tile when there are several'],
|
||||||
['/', 'full-text search, inside the current space and its text files — Tab or Ctrl-N/P walk the results, Enter opens one'],
|
['/', 'full-text search, inside the current space and its text files — Tab or Ctrl-N/P walk the results, Enter opens one'],
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
footnotes on the list keys. */
|
footnotes on the list keys. */
|
||||||
const boardKeys: [string, string][] = [
|
const boardKeys: [string, string][] = [
|
||||||
['rule', 'the first list is the inbox, the last list is done'],
|
['rule', 'the first list is the inbox, the last list is done'],
|
||||||
['h / l', 'to the list left / right'],
|
['h / l', 'to the list left / right — right of the last is the agenda'],
|
||||||
['j / k', 'down / up the cards'],
|
['j / k', 'down / up the cards'],
|
||||||
['gg / G', 'first / last card of the list'],
|
['gg / G', 'first / last card of the list'],
|
||||||
['J / K', 'move the card down / up its list'],
|
['J / K', 'move the card down / up its list'],
|
||||||
@@ -114,18 +114,19 @@
|
|||||||
['F', 'a letter over every card in view — type it to put the cursor there'],
|
['F', 'a letter over every card in view — type it to put the cursor there'],
|
||||||
['r', 'read the board again, for changes made elsewhere'],
|
['r', 'read the board again, for changes made elsewhere'],
|
||||||
['x · dd', 'delete the card (asks y/N)'],
|
['x · dd', 'delete the card (asks y/N)'],
|
||||||
['q · Esc', 'put the special workspace away'],
|
['q · Esc', 'leave the special workspace'],
|
||||||
]
|
]
|
||||||
|
|
||||||
const agendaKeys: [string, string][] = [
|
const agendaKeys: [string, string][] = [
|
||||||
['j / k', 'down / up the cards, then what was written on this day in earlier years'],
|
['j / k', 'down / up the cards, then what was written on this day in earlier years, then into the calendar'],
|
||||||
|
['h', 'to the board'],
|
||||||
['Enter', 'to the card, on its own board beside — or to the entry'],
|
['Enter', 'to the card, on its own board beside — or to the entry'],
|
||||||
['Space', 'done, where it stands'],
|
['Space', 'done, where it stands'],
|
||||||
['D', 'due date: fri, tm, +3d … — :due on the line'],
|
['D', 'due date: fri, tm, +3d … — :due on the line'],
|
||||||
[':', 'the line, with the agenda kept up — :repeat weekly acts on the card'],
|
[':', 'the line, with the agenda kept up — :repeat weekly acts on the card'],
|
||||||
['H / L', 'to the list left / right on its board'],
|
['H / L', 'to the list left / right on its board'],
|
||||||
['T', "today's diary entry"],
|
['T', "today's diary entry"],
|
||||||
['t · q · Esc', 'put the special workspace away'],
|
['q · Esc', 'leave the special workspace — a document opened from it comes back here on q'],
|
||||||
]
|
]
|
||||||
|
|
||||||
const titles: Record<Group, string> = {
|
const titles: Record<Group, string> = {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
{ icon: 'board', label: 'Board', keys: ['b'], run: () => (workspace.specialFocus = 'board'), on: workspace.specialFocus === 'board' },
|
{ icon: 'board', label: 'Board', keys: ['b'], run: () => (workspace.specialFocus = 'board'), on: workspace.specialFocus === 'board' },
|
||||||
{ icon: 'agenda', label: 'Agenda — late, due, in hand', keys: ['t'], run: () => (workspace.specialFocus = 'agenda'), on: workspace.specialFocus === 'agenda' },
|
{ icon: 'agenda', label: 'Agenda — late, due, in hand', keys: ['t'], run: () => (workspace.specialFocus = 'agenda'), on: workspace.specialFocus === 'agenda' },
|
||||||
{ icon: 'calendar', label: 'Calendar', keys: ['C'], run: () => (workspace.specialFocus = 'calendar'), on: workspace.specialFocus === 'calendar' },
|
{ icon: 'calendar', label: 'Calendar', keys: ['C'], run: () => (workspace.specialFocus = 'calendar'), on: workspace.specialFocus === 'calendar' },
|
||||||
{ icon: 'list', label: 'Back to the tree', keys: ['S', 'q'], run: () => workspace.closeSpecial() },
|
{ icon: 'list', label: 'Back to the tree', keys: ['Esc', 'S'], run: () => workspace.closeSpecial() },
|
||||||
]
|
]
|
||||||
if (workspace.specialFocus !== 'board') return [tiles, [cmd, help]]
|
if (workspace.specialFocus !== 'board') return [tiles, [cmd, help]]
|
||||||
// The card verbs are doors too, so a phone — which has no H, L or
|
// The card verbs are doors too, so a phone — which has no H, L or
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ export const commands: CommandSpec[] = [
|
|||||||
run: async () => {
|
run: async () => {
|
||||||
// A document `o` made and nothing was written in is not kept.
|
// A document `o` made and nothing was written in is not kept.
|
||||||
if (await workspace.abandonIfBlank()) {
|
if (await workspace.abandonIfBlank()) {
|
||||||
if (workspace.returnTo === 'board') await workspace.guard(() => workspace.returnToBoard())
|
if (workspace.returnTo === 'special') await workspace.guard(() => workspace.returnToSpecial())
|
||||||
return hooks.focusList()
|
return hooks.focusList()
|
||||||
}
|
}
|
||||||
if (workspace.dirty) {
|
if (workspace.dirty) {
|
||||||
@@ -340,7 +340,7 @@ export const commands: CommandSpec[] = [
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (workspace.editing) workspace.editing = false
|
if (workspace.editing) workspace.editing = false
|
||||||
else if (workspace.returnTo === 'board') return void workspace.guard(() => workspace.returnToBoard())
|
else if (workspace.returnTo === 'special') return void workspace.guard(() => workspace.returnToSpecial())
|
||||||
else if (workspace.tiles.length > 1) return focusTile(closeFocused())
|
else if (workspace.tiles.length > 1) return focusTile(closeFocused())
|
||||||
else workspace.closeTile()
|
else workspace.closeTile()
|
||||||
hooks.focusList()
|
hooks.focusList()
|
||||||
@@ -429,13 +429,13 @@ export const commands: CommandSpec[] = [
|
|||||||
{
|
{
|
||||||
name: 'board',
|
name: 'board',
|
||||||
group: 'board',
|
group: 'board',
|
||||||
help: "this space's board, in the special workspace — again puts it away",
|
help: "this space's board, in the special workspace",
|
||||||
run: () => workspace.guard(() => workspace.toggleSpecial('board')),
|
run: () => workspace.guard(() => workspace.showSpecial('board')),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'special',
|
name: 'special',
|
||||||
group: 'board',
|
group: 'board',
|
||||||
help: 'the special workspace: the board, the agenda and the calendar side by side — S toggles it, Alt-h/j/k/l moves between them, 1 … 9 picks the board',
|
help: 'the special workspace: the board, the agenda and the calendar side by side — S puts it up and away, Tab walks its tiles, 1 … 9 picks the board',
|
||||||
run: () => workspace.guard(() => workspace.toggleSpecial()),
|
run: () => workspace.guard(() => workspace.toggleSpecial()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -599,7 +599,7 @@ export const commands: CommandSpec[] = [
|
|||||||
help: 'the diary by month — h/l a day, j/k a week, H/L a month, Enter opens the day or starts it',
|
help: 'the diary by month — h/l a day, j/k a week, H/L a month, Enter opens the day or starts it',
|
||||||
run: () =>
|
run: () =>
|
||||||
workspace.guard(async () => {
|
workspace.guard(async () => {
|
||||||
if (workspace.view === 'special') return workspace.toggleSpecial('calendar')
|
if (workspace.view === 'special') return void (workspace.specialFocus = 'calendar')
|
||||||
await workspace.openCalendar()
|
await workspace.openCalendar()
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -611,7 +611,7 @@ export const commands: CommandSpec[] = [
|
|||||||
run: () =>
|
run: () =>
|
||||||
workspace.guard(async () => {
|
workspace.guard(async () => {
|
||||||
if (workspace.view !== 'special') workspace.agendaCursor = 0
|
if (workspace.view !== 'special') workspace.agendaCursor = 0
|
||||||
await workspace.toggleSpecial('agenda')
|
await workspace.showSpecial('agenda')
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+71
-38
@@ -76,7 +76,7 @@ export class Tile {
|
|||||||
* document goes back to the board — on the same card — rather than leaving
|
* document goes back to the board — on the same card — rather than leaving
|
||||||
* you in a tree you never asked for.
|
* you in a tree you never asked for.
|
||||||
*/
|
*/
|
||||||
returnTo = $state<'board' | null>(null)
|
returnTo = $state<'special' | null>(null)
|
||||||
/*
|
/*
|
||||||
* The document `o` just made. If it is left with neither a name nor a word
|
* The document `o` just made. If it is left with neither a name nor a word
|
||||||
* in it, it is removed on the way out rather than left as an empty row.
|
* in it, it is removed on the way out rather than left as an empty row.
|
||||||
@@ -170,7 +170,7 @@ class Workspace {
|
|||||||
get returnTo() {
|
get returnTo() {
|
||||||
return this.tile.returnTo
|
return this.tile.returnTo
|
||||||
}
|
}
|
||||||
set returnTo(to: 'board' | null) {
|
set returnTo(to: 'special' | null) {
|
||||||
this.tile.returnTo = to
|
this.tile.returnTo = to
|
||||||
}
|
}
|
||||||
get vimMode() {
|
get vimMode() {
|
||||||
@@ -299,6 +299,12 @@ class Workspace {
|
|||||||
overlay = $state<'none' | 'help' | 'media' | 'overview' | 'actions' | 'verify' | 'outline' | 'trash' | 'history' | 'backlinks'>('none')
|
overlay = $state<'none' | 'help' | 'media' | 'overview' | 'actions' | 'verify' | 'outline' | 'trash' | 'history' | 'backlinks'>('none')
|
||||||
/** Which of the special workspace's tiles the keys go to. */
|
/** Which of the special workspace's tiles the keys go to. */
|
||||||
specialFocus = $state<SpecialTile>('board')
|
specialFocus = $state<SpecialTile>('board')
|
||||||
|
/**
|
||||||
|
* The space whose board the special workspace shows: the one being browsed
|
||||||
|
* when it has a board, or else the last one shown, so that coming from the
|
||||||
|
* diary does not leave the board's tile empty.
|
||||||
|
*/
|
||||||
|
boardSpaceId = $state<number | null>(null)
|
||||||
/** `gO`: the headings of what is on screen, to jump between from the keyboard. */
|
/** `gO`: the headings of what is on screen, to jump between from the keyboard. */
|
||||||
outline = $state.raw<Heading[]>([])
|
outline = $state.raw<Heading[]>([])
|
||||||
outlineCursor = $state(0)
|
outlineCursor = $state(0)
|
||||||
@@ -821,14 +827,12 @@ class Workspace {
|
|||||||
this.cursor = Math.min(spot.cursor, Math.max(this.nodes.length - 1, 0))
|
this.cursor = Math.min(spot.cursor, Math.max(this.nodes.length - 1, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The board belongs to the space, so switching space with the special
|
// Switching space with the special workspace up shows the new space's
|
||||||
// workspace up shows the new space's board — or says it has none, rather
|
// board; a space without one leaves the last board where it is.
|
||||||
// than leaving the previous board on screen.
|
if (this.view === 'special' && this.space?.has_board) {
|
||||||
if (this.view === 'special') {
|
|
||||||
this.listCursor = 0
|
this.listCursor = 0
|
||||||
this.cardCursor = 0
|
this.cardCursor = 0
|
||||||
if (this.space?.has_board) await this.reloadBoard(id, undefined)
|
await this.reloadBoard(id, undefined)
|
||||||
else this.rawBoard = null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -910,7 +914,7 @@ class Workspace {
|
|||||||
if (isContainer(node)) this.closeSpecial()
|
if (isContainer(node)) this.closeSpecial()
|
||||||
else {
|
else {
|
||||||
this.view = 'tree'
|
this.view = 'tree'
|
||||||
this.returnTo = 'board'
|
this.returnTo = 'special'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isContainer(node)) {
|
if (isContainer(node)) {
|
||||||
@@ -1222,6 +1226,7 @@ class Workspace {
|
|||||||
for (const tile of this.tiles) tile.clear()
|
for (const tile of this.tiles) tile.clear()
|
||||||
this.prune()
|
this.prune()
|
||||||
this.rawBoard = null
|
this.rawBoard = null
|
||||||
|
this.boardSpaceId = null
|
||||||
this.view = 'tree'
|
this.view = 'tree'
|
||||||
this.spaces = await api.listSpaces()
|
this.spaces = await api.listSpaces()
|
||||||
const next = this.spaces.find((space) => space.role === 'diary') ?? this.spaces[0]
|
const next = this.spaces.find((space) => space.role === 'diary') ?? this.spaces[0]
|
||||||
@@ -1351,7 +1356,7 @@ class Workspace {
|
|||||||
async openCalendarDay() {
|
async openCalendarDay() {
|
||||||
const day = this.calendarDay
|
const day = this.calendarDay
|
||||||
const entry = this.calendarEntries.find((e) => e.day === day)
|
const entry = this.calendarEntries.find((e) => e.day === day)
|
||||||
this.closeSpecial()
|
// Opened from the special workspace, so `q` on the entry comes back here.
|
||||||
if (entry) return this.openPath(entry.path)
|
if (entry) return this.openPath(entry.path)
|
||||||
const home = this.spaces.find((space) => space.role === 'diary')
|
const home = this.spaces.find((space) => space.role === 'diary')
|
||||||
if (!home) return this.say('there is no diary to write in', 'error')
|
if (!home) return this.say('there is no diary to write in', 'error')
|
||||||
@@ -1384,7 +1389,7 @@ class Workspace {
|
|||||||
if (!done) return
|
if (!done) return
|
||||||
const moved = await api.moveCard(card.id, done.id, done.cards.length)
|
const moved = await api.moveCard(card.id, done.id, done.cards.length)
|
||||||
await this.loadAgenda()
|
await this.loadAgenda()
|
||||||
if (this.view === 'special' && this.spaceId === card.space_id) await this.reloadBoard()
|
if (this.view === 'special' && this.boardSpaceId === card.space_id) await this.reloadBoard()
|
||||||
this.say(doneSaid(moved))
|
this.say(doneSaid(moved))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1397,7 +1402,7 @@ class Workspace {
|
|||||||
// It may have changed group; the cursor goes with it.
|
// It may have changed group; the cursor goes with it.
|
||||||
const at = this.agendaCards.findIndex((c) => c.id === card.id)
|
const at = this.agendaCards.findIndex((c) => c.id === card.id)
|
||||||
if (at >= 0) this.agendaCursor = at
|
if (at >= 0) this.agendaCursor = at
|
||||||
if (this.view === 'special' && this.spaceId === card.space_id) await this.reloadBoard()
|
if (this.view === 'special' && this.boardSpaceId === card.space_id) await this.reloadBoard()
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1414,7 +1419,7 @@ class Workspace {
|
|||||||
await this.loadAgenda()
|
await this.loadAgenda()
|
||||||
const at = this.agendaCards.findIndex((c) => c.id === card.id)
|
const at = this.agendaCards.findIndex((c) => c.id === card.id)
|
||||||
if (at >= 0) this.agendaCursor = at
|
if (at >= 0) this.agendaCursor = at
|
||||||
if (this.view === 'special' && this.spaceId === card.space_id) await this.reloadBoard()
|
if (this.view === 'special' && this.boardSpaceId === card.space_id) await this.reloadBoard()
|
||||||
this.say(from + delta === lists.length - 1 ? doneSaid(moved) : `"${card.title}" → ${target.name}`)
|
this.say(from + delta === lists.length - 1 ? doneSaid(moved) : `"${card.title}" → ${target.name}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1489,13 +1494,18 @@ class Workspace {
|
|||||||
*/
|
*/
|
||||||
async openSpecial(focus: SpecialTile = this.specialFocus) {
|
async openSpecial(focus: SpecialTile = this.specialFocus) {
|
||||||
const space = this.space
|
const space = this.space
|
||||||
if (space?.has_board) await this.reloadBoard(space.id)
|
const shown = this.boardSpace?.has_board ? this.boardSpace : this.spaces.find((s) => s.has_board)
|
||||||
else this.rawBoard = null
|
const board = space?.has_board ? space : shown
|
||||||
|
if (board) await this.reloadBoard(board.id, board.id === this.boardSpaceId ? this.card?.id : undefined)
|
||||||
|
else {
|
||||||
|
this.rawBoard = null
|
||||||
|
this.boardSpaceId = null
|
||||||
|
}
|
||||||
if (!this.calendarDay) this.calendarDay = formatDay(Math.floor(Date.now() / 1000))
|
if (!this.calendarDay) this.calendarDay = formatDay(Math.floor(Date.now() / 1000))
|
||||||
await Promise.all([this.loadAgenda(), this.loadCalendar()])
|
await Promise.all([this.loadAgenda(), this.loadCalendar()])
|
||||||
this.view = 'special'
|
this.view = 'special'
|
||||||
this.specialFocus = focus
|
this.specialFocus = focus
|
||||||
if (focus === 'board' && space && !space.has_board) this.say(`${space.slug} has no board — :set board turns one on`, 'error')
|
if (focus === 'board' && !board) this.say('no space has a board — :set board turns one on', 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
closeSpecial() {
|
closeSpecial() {
|
||||||
@@ -1503,24 +1513,40 @@ class Workspace {
|
|||||||
this.returnTo = null
|
this.returnTo = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** `S`: the special workspace up, on the tile it was left on, or away again. */
|
||||||
|
async toggleSpecial() {
|
||||||
|
if (this.view === 'special') this.closeSpecial()
|
||||||
|
else await this.openSpecial()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `b`, `t`, `C` and `S`: the special workspace on that tile. A key whose
|
* `b`, `t` and `C`: to that tile of the special workspace, putting it up
|
||||||
* tile already has focus puts the workspace away again, as the desktop
|
* first when it is not. They only ever go somewhere; leaving is `S`,
|
||||||
* toggles its scratchpad; any other moves the focus there.
|
* `q` or `Esc`, from whichever tile.
|
||||||
*/
|
*/
|
||||||
async toggleSpecial(focus?: SpecialTile) {
|
async showSpecial(focus: SpecialTile) {
|
||||||
if (this.view !== 'special') return this.openSpecial(focus)
|
if (this.view === 'special') this.specialFocus = focus
|
||||||
if (focus === undefined || focus === this.specialFocus) return this.closeSpecial()
|
else await this.openSpecial(focus)
|
||||||
this.specialFocus = focus
|
}
|
||||||
|
|
||||||
|
/** `Tab` / `Shift-Tab` in the special workspace: the next tile round, or the one before. */
|
||||||
|
cycleSpecial(by: 1 | -1) {
|
||||||
|
const order: SpecialTile[] = ['board', 'agenda', 'calendar']
|
||||||
|
this.specialFocus = order[(order.indexOf(this.specialFocus) + by + order.length) % order.length]
|
||||||
}
|
}
|
||||||
|
|
||||||
async openBoard() {
|
async openBoard() {
|
||||||
await this.openSpecial('board')
|
await this.openSpecial('board')
|
||||||
}
|
}
|
||||||
|
|
||||||
async reloadBoard(spaceId = this.spaceId, keep = this.card?.id) {
|
get boardSpace(): NodeSummary | undefined {
|
||||||
|
return this.spaces.find((space) => space.id === this.boardSpaceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
async reloadBoard(spaceId = this.boardSpaceId ?? this.spaceId, keep = this.card?.id) {
|
||||||
if (spaceId === null) return
|
if (spaceId === null) return
|
||||||
this.rawBoard = await api.board(spaceId)
|
this.rawBoard = await api.board(spaceId)
|
||||||
|
this.boardSpaceId = spaceId
|
||||||
void this.loadAgenda()
|
void this.loadAgenda()
|
||||||
// Follow the card that was just moved rather than whatever now sits under
|
// Follow the card that was just moved rather than whatever now sits under
|
||||||
// the cursor, so J J J walks one card down instead of shuffling three.
|
// the cursor, so J J J walks one card down instead of shuffling three.
|
||||||
@@ -1576,7 +1602,7 @@ class Workspace {
|
|||||||
// The neighbour's own position, not its index: older done cards may be
|
// The neighbour's own position, not its index: older done cards may be
|
||||||
// hidden above it.
|
// hidden above it.
|
||||||
await api.moveCard(card.id, list.id, list.cards[to].position)
|
await api.moveCard(card.id, list.id, list.cards[to].position)
|
||||||
await this.reloadBoard(this.spaceId, card.id)
|
await this.reloadBoard(this.boardSpaceId, card.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** `H`/`L`: the same card, one list over — the move a board is really for. */
|
/** `H`/`L`: the same card, one list over — the move a board is really for. */
|
||||||
@@ -1594,7 +1620,7 @@ class Workspace {
|
|||||||
const target = lists[index]
|
const target = lists[index]
|
||||||
if (!card || !target || index === this.listCursor) return
|
if (!card || !target || index === this.listCursor) return
|
||||||
const moved = await api.moveCard(card.id, target.id, target.cards.length)
|
const moved = await api.moveCard(card.id, target.id, target.cards.length)
|
||||||
await this.reloadBoard(this.spaceId, card.id)
|
await this.reloadBoard(this.boardSpaceId, card.id)
|
||||||
this.say(index === lists.length - 1 ? doneSaid(moved) : `"${card.title}" → ${target.name}`)
|
this.say(index === lists.length - 1 ? doneSaid(moved) : `"${card.title}" → ${target.name}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1609,7 +1635,7 @@ class Workspace {
|
|||||||
const list = this.list
|
const list = this.list
|
||||||
if (!list) return this.say('no list to put it on', 'error')
|
if (!list) return this.say('no list to put it on', 'error')
|
||||||
const card = await api.createCard(list.id, { title, node_id: nodeId })
|
const card = await api.createCard(list.id, { title, node_id: nodeId })
|
||||||
await this.reloadBoard(this.spaceId, card.id)
|
await this.reloadBoard(this.boardSpaceId, card.id)
|
||||||
return this.say(nodeId === null ? `carded "${card.title}"` : `carded "${card.title}" → #${nodeId}`)
|
return this.say(nodeId === null ? `carded "${card.title}"` : `carded "${card.title}" → #${nodeId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1626,7 +1652,7 @@ class Workspace {
|
|||||||
const card = this.card
|
const card = this.card
|
||||||
if (!card) return this.say('no card selected', 'error')
|
if (!card) return this.say('no card selected', 'error')
|
||||||
const updated = await api.updateCard(card.id, patch)
|
const updated = await api.updateCard(card.id, patch)
|
||||||
await this.reloadBoard(this.spaceId, card.id)
|
await this.reloadBoard(this.boardSpaceId, card.id)
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1649,13 +1675,13 @@ class Workspace {
|
|||||||
const card = this.card
|
const card = this.card
|
||||||
if (!card) return this.say('no card selected', 'error')
|
if (!card) return this.say('no card selected', 'error')
|
||||||
await api.deleteCard(card.id)
|
await api.deleteCard(card.id)
|
||||||
await this.reloadBoard(this.spaceId, undefined)
|
await this.reloadBoard(this.boardSpaceId, undefined)
|
||||||
this.say(`deleted "${card.title}"`)
|
this.say(`deleted "${card.title}"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
async createList(name: string) {
|
async createList(name: string) {
|
||||||
if (this.spaceId === null) return
|
if (this.boardSpaceId === null) return
|
||||||
this.rawBoard = await api.createList(this.spaceId, name)
|
this.rawBoard = await api.createList(this.boardSpaceId, name)
|
||||||
// It goes in front of the done list, so it is not simply the last one.
|
// It goes in front of the done list, so it is not simply the last one.
|
||||||
const names = this.rawBoard.lists.map((list) => list.name)
|
const names = this.rawBoard.lists.map((list) => list.name)
|
||||||
this.listCursor = Math.max(names.lastIndexOf(name), 0)
|
this.listCursor = Math.max(names.lastIndexOf(name), 0)
|
||||||
@@ -1686,7 +1712,7 @@ class Workspace {
|
|||||||
*/
|
*/
|
||||||
async openCard() {
|
async openCard() {
|
||||||
const card = this.card
|
const card = this.card
|
||||||
const space = this.spaceId
|
const space = this.boardSpaceId
|
||||||
if (!card || space === null) return
|
if (!card || space === null) return
|
||||||
let nodeId = card.node_id
|
let nodeId = card.node_id
|
||||||
if (nodeId !== null) {
|
if (nodeId !== null) {
|
||||||
@@ -1718,16 +1744,19 @@ class Workspace {
|
|||||||
await this.openNode(nodeId, card.node_id === null)
|
await this.openNode(nodeId, card.node_id === null)
|
||||||
this.prune()
|
this.prune()
|
||||||
if (card.node_id === null) this.enterInsert = true
|
if (card.node_id === null) this.enterInsert = true
|
||||||
this.returnTo = 'board'
|
this.returnTo = 'special'
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Back to the board a card's document was opened from. */
|
/**
|
||||||
async returnToBoard() {
|
* Back to the special workspace a document was opened from — a card's
|
||||||
|
* notes, a day of the calendar, an entry of the agenda — on the tile it
|
||||||
|
* was opened from.
|
||||||
|
*/
|
||||||
|
async returnToSpecial() {
|
||||||
this.returnTo = null
|
this.returnTo = null
|
||||||
this.closeTile()
|
this.closeTile()
|
||||||
this.pane = 'list'
|
this.pane = 'list'
|
||||||
await this.reloadBoard()
|
await this.openSpecial(this.specialFocus)
|
||||||
this.view = 'special'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Turn a space's board on or off. The diary ships with it off. */
|
/** Turn a space's board on or off. The diary ships with it off. */
|
||||||
@@ -1737,7 +1766,11 @@ class Workspace {
|
|||||||
await api.updateNode(space.id, { has_board: on })
|
await api.updateNode(space.id, { has_board: on })
|
||||||
this.spaces = await api.listSpaces()
|
this.spaces = await api.listSpaces()
|
||||||
if (on) await this.openBoard()
|
if (on) await this.openBoard()
|
||||||
else this.rawBoard = null
|
else if (this.boardSpaceId === space.id) {
|
||||||
|
this.rawBoard = null
|
||||||
|
this.boardSpaceId = null
|
||||||
|
if (this.view === 'special') await this.openSpecial()
|
||||||
|
}
|
||||||
this.say(on ? `${space.slug} has a board` : `${space.slug} has no board any more`)
|
this.say(on ? `${space.slug} has a board` : `${space.slug} has no board any more`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user