Let error messages stay until read; show the remote form on the welcome screen
Error messages now stay for 8 seconds or until clicked, and keep each hint on its own line. The welcome screen shows how to start gites with a repository on another machine. The spec follows these changes and the new ssh options and socket folder.
This commit is contained in:
@@ -35,7 +35,7 @@ one character, and no local folder with exactly that name exists. Every other te
|
||||
local folder. So `C:\code\x` and `C:/code/x` stay local, `./a:b` stays local, and a
|
||||
folder in the current folder named `notes:2024` stays local when it exists.
|
||||
|
||||
A path on the server that starts with `~/` is handed to the server's shell with the
|
||||
A path on the server that is `~` or starts with `~/` is handed to the server's shell with the
|
||||
`~` outside the quotes, so the shell expands it to the home folder there.
|
||||
|
||||
## Running git
|
||||
@@ -49,6 +49,7 @@ A path on the server that starts with `~/` is handed to the server's shell with
|
||||
|
||||
```
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=10
|
||||
-o ServerAliveInterval=5 -o ServerAliveCountMax=2
|
||||
-o ControlMaster=auto -o ControlPath=<socket> -o ControlPersist=10m
|
||||
user@myserver "git -C '/srv/app' 'log' '--format=…' …"
|
||||
```
|
||||
@@ -57,12 +58,18 @@ A path on the server that starts with `~/` is handed to the server's shell with
|
||||
has no terminal to ask in. Keys and the ssh agent work.
|
||||
- `ConnectTimeout=10`: a server that does not answer gives an error after 10 seconds
|
||||
instead of a window that waits for ever.
|
||||
- `ServerAliveInterval=5`, `ServerAliveCountMax=2`: when a server stops answering
|
||||
on a connection that is already open (also a shared one), ssh notices it in about
|
||||
10 seconds and gives an error instead of waiting for ever.
|
||||
- `ControlMaster`, `ControlPath`, `ControlPersist`: the first command opens one
|
||||
connection and leaves it open; later commands go through it and cost about one
|
||||
network round trip each instead of a new login. The connection closes 10 minutes
|
||||
after the last command. The socket is `gites-%C` in the user's runtime folder
|
||||
(`/run/user/<id>` on Linux), or in `/tmp` where there is none; `%C` is a hash ssh
|
||||
makes from the host, port and user, so the path stays short.
|
||||
(`/run/user/<id>` on Linux). Where there is none (always on macOS), it is in a
|
||||
`gites` folder in the user's cache folder, made so that only the user can open it;
|
||||
never in `/tmp`. If neither folder can be found or made, these three options are
|
||||
left out and every command opens a new connection. `%C` is a hash ssh makes from
|
||||
the host, port and user, so the path stays short.
|
||||
- On Windows, ssh cannot share connections, so these three options are left out and
|
||||
every command opens a new connection.
|
||||
- Quoting: every word of the git command is put in single quotes, and a `'` inside a
|
||||
@@ -107,6 +114,12 @@ If the connection drops while a repository is open, the next command shows its e
|
||||
as a message at the bottom of the window and the graph that is already on screen
|
||||
stays. The next reload connects again by itself.
|
||||
|
||||
A message that reports an error stays for 8 seconds, or until it is clicked, and each
|
||||
hint line above stays on its own line. Other messages go after 1.8 seconds, as before.
|
||||
|
||||
The commands that run git run away from the thread that draws the window, so the
|
||||
window does not freeze while it waits for a slow server.
|
||||
|
||||
## Tests
|
||||
|
||||
- Reading locations: `host:/path`, `user@host:/path`, `host:~/x`, `C:\x`, `C:/x`, a
|
||||
|
||||
+19
-7
@@ -70,7 +70,7 @@ watch(
|
||||
if (!settingsLoaded) return;
|
||||
clearTimeout(settingsTimer);
|
||||
settingsTimer = window.setTimeout(() => {
|
||||
invoke("save_settings", { value: { ...settings } }).catch((e) => showToast(`could not save settings: ${e}`));
|
||||
invoke("save_settings", { value: { ...settings } }).catch((e) => showToast(`could not save settings: ${e}`, { error: true }));
|
||||
}, 300);
|
||||
},
|
||||
{ deep: true },
|
||||
@@ -117,7 +117,7 @@ async function load() {
|
||||
if (data) await applyRepo(data);
|
||||
} catch (e) {
|
||||
// A repository already on screen stays, for example when a connection drops.
|
||||
if (repo.value) showToast(`could not reload: ${e}`);
|
||||
if (repo.value) showToast(`could not reload: ${e}`, { error: true });
|
||||
else error.value = String(e);
|
||||
} finally {
|
||||
loading = false;
|
||||
@@ -131,7 +131,7 @@ async function openRepo(path: string) {
|
||||
await applyRepo(await invoke<RepoData>("open_repo", { path }));
|
||||
} catch (e) {
|
||||
// The repository that was open stays open.
|
||||
showToast(`could not open ${path}: ${e}`);
|
||||
showToast(`could not open ${path}: ${e}`, { error: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ watch(
|
||||
invoke("save_filters", {
|
||||
repo: repoRoot,
|
||||
value: { hidden: filters.hidden, rules: filters.rules, hideTags: filters.hideTags, collapseAll: collapseAll.value },
|
||||
}).catch((e) => showToast(`could not save filters: ${e}`));
|
||||
}).catch((e) => showToast(`could not save filters: ${e}`, { error: true }));
|
||||
}, 300);
|
||||
},
|
||||
{ deep: true },
|
||||
@@ -366,10 +366,17 @@ async function copyText(text: string) {
|
||||
// ---- actions -------------------------------------------------------------
|
||||
|
||||
let toastTimer: number | undefined;
|
||||
function showToast(text: string) {
|
||||
/** Shows a short message. An error message stays longer, 8 seconds, so there is time to
|
||||
* read it; a click on any message closes it. */
|
||||
function showToast(text: string, { error = false } = {}) {
|
||||
toast.value = text;
|
||||
clearTimeout(toastTimer);
|
||||
toastTimer = window.setTimeout(() => (toast.value = null), 1800);
|
||||
toastTimer = window.setTimeout(() => (toast.value = null), error ? 8000 : 1800);
|
||||
}
|
||||
|
||||
function closeToast() {
|
||||
clearTimeout(toastTimer);
|
||||
toast.value = null;
|
||||
}
|
||||
|
||||
async function copyHash() {
|
||||
@@ -653,7 +660,7 @@ onUnmounted(() => {
|
||||
</footer>
|
||||
|
||||
<transition name="fade">
|
||||
<div v-if="toast" class="toast">{{ toast }}</div>
|
||||
<div v-if="toast" class="toast" @click="closeToast">{{ toast }}</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
@@ -760,6 +767,11 @@ onUnmounted(() => {
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 6px 24px #0008;
|
||||
/* Keeps each line of a message on its own line, such as the hint under an ssh error. */
|
||||
white-space: pre-line;
|
||||
max-width: min(640px, calc(100vw - 32px));
|
||||
overflow-wrap: anywhere;
|
||||
cursor: pointer;
|
||||
}
|
||||
.fade-enter-active, .fade-leave-active { transition: opacity 0.2s; }
|
||||
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
||||
|
||||
@@ -56,7 +56,10 @@ const name = (p: string) => p.split(/[\\/]/).filter(Boolean).pop() ?? p;
|
||||
/>
|
||||
<button v-else class="browse" @click="startTyping">Open on another machine…</button>
|
||||
</div>
|
||||
<p class="hint">You can also start gites with a path: <code>gites ~/code/my-project</code></p>
|
||||
<p class="hint">
|
||||
You can also start gites with a path: <code>gites ~/code/my-project</code>,
|
||||
or with a repository on another machine: <code>gites myserver:/srv/app</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user