[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
go.mod | 8 ++++----
go.sum | 16 ++++++++--------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/go.mod b/go.mod
index 1401fd89d..c6c01056c 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
github.com/alexflint/go-filemutex v1.3.0
github.com/aws/aws-sdk-go-v2 v1.36.1
github.com/aws/aws-sdk-go-v2/config v1.29.6
- github.com/aws/aws-sdk-go-v2/service/s3 v1.75.1
+ github.com/aws/aws-sdk-go-v2/service/s3 v1.76.1
github.com/aws/smithy-go v1.22.2
github.com/creack/pty v1.1.21
github.com/emirpasic/gods v1.18.1
@@ -53,11 +53,11 @@ require (
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect
- github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.30 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.32 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.4 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.11 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.13 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 // indirect
diff --git a/go.sum b/go.sum
index b2d8e319a..0e897eb3c 100644
--- a/go.sum
+++ b/go.sum
@@ -32,18 +32,18 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 h1:m1GeXHVMJsRsUAqG6H
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32/go.mod h1:IitoQxGfaKdVLNg0hD8/DXmAqNy0H4K2H2Sf91ti8sI=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.30 h1:yQSv0NQ4CRHoki6AcV/Ldoa4/QCMJauZkF23qznBCPQ=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.30/go.mod h1:jH3z32wDrsducaYX26xnl41ksYFWqjHphIciwIANZkc=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.32 h1:OIHj/nAhVzIXGzbAE+4XmZ8FPvro3THr6NlqErJc3wY=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.32/go.mod h1:LiBEsDo34OJXqdDlRGsilhlIiXR7DL+6Cx2f4p1EgzI=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.4 h1:iwk7v5+lUtA0cIQcQM6EyCXtQJZ9MGIWWaf0JKud5UE=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.5.4/go.mod h1:o9mSr0x1NwImSmP9q38aTUhjYwcDm277YUURBjXcC2I=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.0 h1:kT2WeWcFySdYpPgyqJMSUE7781Qucjtn6wBvrgm9P+M=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.0/go.mod h1:WYH1ABybY7JK9TITPnk6ZlP7gQB8psI4c9qDmMsnLSA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 h1:SYVGSFQHlchIcy6e7x12bsrxClCXSP5et8cqVhL8cuw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13/go.mod h1:kizuDaLX37bG5WZaoxGPQR/LNFXpxp0vsUnqfkWXfNE=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.11 h1:P8qJcYGVDswlMkVFhMi7SJmlf0jNA0JRbvE/q2PuXD8=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.11/go.mod h1:9yp5x5vYwyhnZZ9cKLBxZmrJTGv99C9iVmG7AKeUvdc=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.75.1 h1:hbTWOPUgAnPpk5+G1jZjYnq4eKCAePwRJEqLN1Tj7Bg=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.75.1/go.mod h1:Mo2xdnRzOyZQkGHEbhOgooG0eIV+GqS/g8LU4B5iftI=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.13 h1:OBsrtam3rk8NfBEq7OLOMm5HtQ9Yyw32X4UQMya/wjw=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.13/go.mod h1:3U4gFA5pmoCOja7aq4nSaIAGbaOHv2Yl2ug018cmC+Q=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.76.1 h1:d4ZG8mELlLeUWFBMCqPtRfEP3J6aQgg/KTC9jLSlkMs=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.76.1/go.mod h1:uZoEIR6PzGOZEjgAZE4hfYfsqK2zOHhq68JLKEvvXj4=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 h1:/eE3DogBjYlvlbhd2ssWyeuovWunHLxfgw3s/OJa4GQ=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15/go.mod h1:2PCJYpi7EKeA5SkStAmZlF6fi0uUABuhtF8ILHjGc3Y=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 h1:M/zwXiL2iXUrHputuXgmO94TVNmcenPHxgLXLutodKE=
From cbb1a49fb4ed40401216a02f27a47068a5a2ef23 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 14 Feb 2025 13:34:28 -0800
Subject: [PATCH 05/47] Bump golang.org/x/term from 0.28.0 to 0.29.0 (#1973)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[//]: # (dependabot-start)
⚠️ **Dependabot is rebasing this PR** ⚠️
Rebasing might not happen immediately, so don't worry if this takes some
time.
Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.
---
[//]: # (dependabot-end)
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.28.0 to
0.29.0.
Commits
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
From 5b36d0494689c17ade37fe36afbbda9331fd20b3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 14 Feb 2025 16:06:11 -0800
Subject: [PATCH 06/47] Bump golang.org/x/mod from 0.22.0 to 0.23.0 (#1969)
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.22.0 to
0.23.0.
Commits
52289f1
modfile: fix trailing empty lines in require blocks
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
go.mod | 2 +-
go.sum | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/go.mod b/go.mod
index c6c01056c..7f0b1a77b 100644
--- a/go.mod
+++ b/go.mod
@@ -33,7 +33,7 @@ require (
github.com/ubuntu/gowsl v0.0.0-20240906163211-049fd49bd93b
github.com/wavetermdev/htmltoken v0.2.0
golang.org/x/crypto v0.33.0
- golang.org/x/mod v0.22.0
+ golang.org/x/mod v0.23.0
golang.org/x/sys v0.30.0
golang.org/x/term v0.29.0
google.golang.org/api v0.220.0
diff --git a/go.sum b/go.sum
index 0e897eb3c..7ac247164 100644
--- a/go.sum
+++ b/go.sum
@@ -202,8 +202,8 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
-golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
-golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
+golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
From eff01f0b987d2d73061c85968145865b8a3a515f Mon Sep 17 00:00:00 2001
From: Sylvie Crowe <107814465+oneirocosm@users.noreply.github.com>
Date: Fri, 14 Feb 2025 16:09:13 -0800
Subject: [PATCH 07/47] feat: add an overlay to show general preview errs
(#1974)
This adds an overlay much like the connection error and copy error ones,
but it is for general use in the various preview widgets.
---
frontend/app/view/preview/preview.tsx | 80 ++++++++++++++++++++++++++-
frontend/types/custom.d.ts | 14 +++++
2 files changed, 93 insertions(+), 1 deletion(-)
diff --git a/frontend/app/view/preview/preview.tsx b/frontend/app/view/preview/preview.tsx
index b0fd1d48f..908fd71c6 100644
--- a/frontend/app/view/preview/preview.tsx
+++ b/frontend/app/view/preview/preview.tsx
@@ -3,6 +3,7 @@
import { BlockNodeModel } from "@/app/block/blocktypes";
import { Button } from "@/app/element/button";
+import { CopyButton } from "@/app/element/copybutton";
import { CenteredDiv } from "@/app/element/quickelems";
import { TypeAheadModal } from "@/app/modals/typeaheadmodal";
import { ContextMenuModel } from "@/app/store/contextmenu";
@@ -39,9 +40,10 @@ import {
} from "@/util/util";
import { Monaco } from "@monaco-editor/react";
import clsx from "clsx";
-import { Atom, atom, Getter, PrimitiveAtom, useAtomValue, useSetAtom, WritableAtom } from "jotai";
+import { Atom, atom, Getter, PrimitiveAtom, useAtom, useAtomValue, useSetAtom, WritableAtom } from "jotai";
import { loadable } from "jotai/utils";
import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api";
+import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
import { createRef, memo, useCallback, useEffect, useMemo, useState } from "react";
import { TransformComponent, TransformWrapper, useControls } from "react-zoom-pan-pinch";
import { CSVView } from "./csvview";
@@ -161,6 +163,7 @@ export class PreviewModel implements ViewModel {
fileContent: WritableAtom, [string], void>;
newFileContent: PrimitiveAtom;
connectionError: PrimitiveAtom;
+ errorMsgAtom: PrimitiveAtom;
openFileModal: PrimitiveAtom;
openFileModalDelay: PrimitiveAtom;
@@ -195,6 +198,7 @@ export class PreviewModel implements ViewModel {
this.filterOutNowsh = atom(true);
this.monacoRef = createRef();
this.connectionError = atom("");
+ this.errorMsgAtom = atom(null) as PrimitiveAtom;
this.viewIcon = atom((get) => {
const blockData = get(this.blockAtom);
if (blockData?.meta?.icon) {
@@ -1123,6 +1127,7 @@ function PreviewView({
model: PreviewModel;
}) {
const connStatus = useAtomValue(model.connStatus);
+ const [errorMsg, setErrorMsg] = useAtom(model.errorMsgAtom);
if (connStatus?.status != "connected") {
return null;
}
@@ -1143,6 +1148,7 @@ function PreviewView({
<>
{/* */}
+ );
+};
+
+export default AnsiLine;
diff --git a/frontend/tailwindsetup.css b/frontend/tailwindsetup.css
index 578567a92..acf1c74b0 100644
--- a/frontend/tailwindsetup.css
+++ b/frontend/tailwindsetup.css
@@ -41,4 +41,22 @@
--text-default: 14px;
--radius: 8px;
+
+ /* ANSI Colors (Default Dark Palette) */
+ --ansi-black: #757575;
+ --ansi-red: #cc685c;
+ --ansi-green: #76c266;
+ --ansi-yellow: #cbca9b;
+ --ansi-blue: #85aacb;
+ --ansi-magenta: #cc72ca;
+ --ansi-cyan: #74a7cb;
+ --ansi-white: #c1c1c1;
+ --ansi-brightblack: #727272;
+ --ansi-brightred: #cc9d97;
+ --ansi-brightgreen: #a3dd97;
+ --ansi-brightyellow: #cbcaaa;
+ --ansi-brightblue: #9ab6cb;
+ --ansi-brightmagenta: #cc8ecb;
+ --ansi-brightcyan: #b7b8cb;
+ --ansi-brightwhite: #f0f0f0;
}
From 217ab4a2e3dc1173531d68a6dcd7382224072f1d Mon Sep 17 00:00:00 2001
From: Mike Sawka
Date: Wed, 19 Feb 2025 15:18:42 -0800
Subject: [PATCH 29/47] new telemetry docs (#1999)
---
docs/docs/telemetry-old.mdx | 130 +++++++++++++++++++++++++++++++
docs/docs/telemetry.mdx | 147 +++++++++++++++++++-----------------
2 files changed, 209 insertions(+), 68 deletions(-)
create mode 100644 docs/docs/telemetry-old.mdx
diff --git a/docs/docs/telemetry-old.mdx b/docs/docs/telemetry-old.mdx
new file mode 100644
index 000000000..dba263dac
--- /dev/null
+++ b/docs/docs/telemetry-old.mdx
@@ -0,0 +1,130 @@
+---
+id: "telemetry-old"
+title: "Legacy Telemetry"
+sidebar_class_name: hidden
+---
+
+Wave Terminal collects telemetry data to help us track feature use, direct future product efforts, and generate aggregate metrics on Wave's popularity and usage. We do not collect or store any PII (personal identifiable information) and all metric data is only associated with and aggregated using your randomly generated _ClientId_. You may opt out of collection at any time.
+
+If you would like to turn telemetry on or off, the first opportunity is a button on the initial welcome page. After this, it can be turned off by adding `"telemetry:enabled": false` to the `config/settings.json` file. It can alternatively be turned on by adding `"telemetry:enabled": true` to the `config/settings.json` file.
+
+:::info
+
+You can also change your telemetry setting by running the wsh command:
+
+```
+wsh setconfig telemetry:enabled=true
+```
+
+:::
+
+---
+
+## Sending Telemetry
+
+Provided that telemetry is enabled, it is sent 10 seconds after Waveterm is first booted and then again every 4 hours thereafter. It can also be sent in response to a few special cases listed below. When telemetry is sent, it is grouped into individual days as determined by your time zone. Any data from a previous day is marked as `Uploaded` so it will not need to be sent again.
+
+### Sending Once Telemetry is Enabled
+
+As soon as telemetry is enabled, a telemetry update is sent regardless of how long it has been since the last send. This does not reset the usual timer for telemetry sends.
+
+### Notifying that Telemetry is Disabled
+
+As soon as telemetry is disabled, Waveterm sends a special update that notifies us of this change. See [When Telemetry is Turned Off](#when-telemetry-is-turned-off) for more info. The timer still runs in the background but no data is sent.
+
+### When Waveterm is Closed
+
+Provided that telemetry is enabled, it will be sent when Waveterm is closed.
+
+---
+
+## Telemetry Data
+
+When telemetry is active, we collect the following data. It is stored in the `telemetry.TelemetryData` type in the source code.
+
+| Name | Description |
+| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| ActiveMinutes | The number of minutes that the user has actively used Waveterm on a given day. This requires the terminal window to be in focus while the user is actively interacting with it. |
+| FgMinutes | The number of minutes that Waveterm has been in the foreground on a given day. This requires the terminal window to be in focus regardless of user interaction. |
+| OpenMinutes | The number of minutes that Waveterm has been open on a given day. This only requires that the terminal is open, even if the window is out of focus. |
+| NumBlocks | The number of existing blocks open on a given day |
+| NumTabs | The number of existing tabs open on a given day. |
+| NewTab | The number of new tabs created on a given day |
+| NumWindows | The number of existing windows open on a given day. |
+| NumWS | The number of existing workspaces on a given day. |
+| NumWSNamed | The number of named workspaces on a give day. |
+| NewTab | The number of new tabs opened on a given day. |
+| NumStartup | The number of times waveterm has been started on a given day. |
+| NumShutdown | The number of times waveterm has been shut down on a given day. |
+| SetTabTheme | The number of times the tab theme is changed from the context menu |
+| NumMagnify | The number of times any block is magnified |
+| NumPanics | The number of backend (golang) panics caught in the current day |
+| NumAIReqs | The number of AI requests made in the current day |
+| NumSSHConn | The number of distinct SSH connections that have been made to distinct hosts |
+| NumWSLConns | The number of distinct WSL connections that have been made to distinct distros |
+| Renderers | The number of new block views of each type are open on a given day. |
+| WshCmds | The number of wsh commands of each type run on a given day |
+| Blocks | The number of blocks of different view types open on a given day |
+| Conn | The number of successful remote connections made (and errors) on a given day |
+
+## Associated Data
+
+In addition to the telemetry data collected, the following is also reported. It is stored in the `telemetry.ActivityType` type in the source code.
+
+| Name | Description |
+| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| Day | The date the telemetry is associated with. It does not include the time. |
+| Uploaded | A boolean that indicates if the telemetry for this day is finalized. It is false during the day the telemetry is associated with, but gets set true at the first telemetry upload after that. Once it is true, the data for that particular day will not be sent up with the telemetry any more. |
+| TzName | The code for the timezone the user's OS is reporting (e.g. PST, GMT, JST) |
+| TzOffset | The offset for the timezone the user's OS is reporting (e.g. -08:00, +00:00, +09:00) |
+| ClientVersion | Which version of Waveterm is installed. |
+| ClientArch | This includes the user's operating system (e.g. linux or darwin) and architecture (e.g. x86_64 or arm64). It does not include data for any Connections at this time. |
+| BuildTime | This serves as a more accurate version number that keeps track of when we built the version. It has no bearing on when that version was installed by you. |
+| OSRelease | This lists the version of the operating system the user has installed. |
+| Displays | Display resolutions (added in v0.9.3 to help us understand what screen resolutions to optimize for) |
+
+## Telemetry Metadata
+
+Lastly, some data is sent along with the telemetry that describes how to classify it. It is stored in the `wcloud.TelemetryInputType` in the source code.
+
+| Name | Description |
+| ----------------- | --------------------------------------------------------------------------------------------------------------------------- |
+| UserId | Currently Unused. This is an anonymous UUID intended for use in future features. |
+| ClientId | This is an anonymous UUID created when Waveterm is first launched. It is used for telemetry and sending prompts to Open AI. |
+| AppType | This is used to differentiate the current version of waveterm from the legacy app. |
+| AutoUpdateEnabled | Whether or not auto update is turned on. |
+| AutoUpdateChannel | The type of auto update in use. This specifically refers to whether a latest or beta channel is selected. |
+| CurDay | The current day (in your time zone) when telemetry is sent. It does not include the time of day. |
+
+## Geo Data
+
+We do not store IP addresses in our telemetry table. However, CloudFlare passes us Geo-Location headers. We store these two header values:
+
+| Name | Description |
+| ------------ | ----------------------------------------------------------------- |
+| CFCountry | 2-letter country code (e.g. "US", "FR", or "JP") |
+| CFRegionCode | region code (often a provence, region, or state within a country) |
+
+---
+
+## When Telemetry is Turned Off
+
+When a user disables telemetry, Waveterm sends a notification that their anonymous _ClientId_ has had its telemetry disabled. This is done with the `wcloud.NoTelemetryInputType` type in the source code. Beyond that, no further information is sent unless telemetry is turned on again. If it is turned on again, the previous 30 days of telemetry will be sent.
+
+---
+
+## A Note on IP Addresses
+
+Telemetry is uploaded via https, which means your IP address is known to the telemetry server. We **do not** store your IP address in our telemetry table and **do not** associate it with your _ClientId_.
+
+---
+
+## Previously Collected Telemetry Data
+
+While we believe the data we collect with telemetry is fairly minimal, we cannot make that decision for every user. If you ever change your mind about what has been collected previously, you may request that your data be deleted by emailing us at [support@waveterm.dev](mailto:support@waveterm.dev). If you do, we will need your _ClientId_ to remove it.
+
+---
+
+## Privacy Policy
+
+For a summary of the above, you can take a look at our [Privacy Policy](https://www.waveterm.dev/privacy).
diff --git a/docs/docs/telemetry.mdx b/docs/docs/telemetry.mdx
index 9b33c4ba7..81f6789bb 100644
--- a/docs/docs/telemetry.mdx
+++ b/docs/docs/telemetry.mdx
@@ -1,16 +1,32 @@
---
sidebar_position: 100
+title: Telemetry
id: "telemetry"
-title: "Telemetry"
---
-Wave Terminal collects telemetry data to help us track feature use, direct future product efforts, and generate aggregate metrics on Wave's popularity and usage. We do not collect or store any PII (personal identifiable information) and all metric data is only associated with and aggregated using your randomly generated _ClientId_. You may opt out of collection at any time.
+## tl;dr
+
+Wave Terminal collects telemetry data to help us track feature use, direct future product efforts, and generate aggregate metrics on Wave's popularity and usage. We do NOT collect personal information (PII), keystrokes, file contents, AI prompts, IP addresses, hostnames, or commands. We attach all information to an anonymous, randomly generated _ClientId_ (UUID). You may opt out of collection at any time.
+
+Here’s a quick summary of what is collected:
+
+- Basic App/System Info – OS, architecture, app version, update settings
+- Usage Metrics – App start/shutdown, active minutes, foreground time, tab/block counts/usage
+- Feature Interactions – When you create tabs, run commands, change settings, etc.
+- Display Info – Monitor resolution, number of displays
+- Connection Events – SSH/WSL connection attempts (but NOT hostnames/IPs)
+- AI Commands – Only which AI backend is used (e.g., OpenAI, Claude) – no text or prompts sent
+- Error Reports – Crash/panic events with minimal debugging info, but no stack traces or detailed errors
+
+Telemetry can be disabled at any time in settings. If not disabled it is sent on startup, on shutdown, and every 4-hours.
+
+## How to Disable Telemetry
If you would like to turn telemetry on or off, the first opportunity is a button on the initial welcome page. After this, it can be turned off by adding `"telemetry:enabled": false` to the `config/settings.json` file. It can alternatively be turned on by adding `"telemetry:enabled": true` to the `config/settings.json` file.
-:::info
+:::tip
-You can also change your telemetry setting by running the wsh command:
+You can also change your telemetry setting (true/false) by running the wsh command:
```
wsh setconfig telemetry:enabled=true
@@ -18,7 +34,11 @@ wsh setconfig telemetry:enabled=true
:::
----
+:::info
+
+This document outlines the new telemetry system as of v0.11.1. The previous telemetry documentation is still relevant and can be found in our [Legacy Telemetry Documentation](./telemetry-old.mdx), but in general, the new telemetry is a superset of the old.
+
+:::
## Sending Telemetry
@@ -36,74 +56,65 @@ As soon as telemetry is disabled, Waveterm sends a special update that notifies
Provided that telemetry is enabled, it will be sent when Waveterm is closed.
----
+## Event Types
-## Telemetry Data
+Below is a list of the event types collected in the new telemetry system. More events are likely to be added in the future.
-When telemetry is active, we collect the following data. It is stored in the `telemetry.TelemetryData` type in the source code.
+| Event Name | Description |
+| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `app:startup` | Logged every time you start the app. Contains basic app information like architecture, version, buildtime, etc. |
+| `app:shutdown` | Logged on every shutdown |
+| `app:activity` | Logged once per hour of app activity |
+| `app:display` | Logged on startup and contains information about size of displays |
+| `app:counts` | Logged once per hour when app is active, contains basic counts like number of windows, tabs, workspaces, blocks, etc. |
+| `action:magnify` | Logged each time a block is magnified |
+| `action:settabtheme` | Logged each time a tab theme is changed |
+| `action:runaicmd` | Logged each time an AI request is made (no prompt information or text is sent), only sends "ai:backendtype" to know what type of AI backend is being used (OpenAI, Claude, Gemini, etc.) |
+| `action:createtab` | Logged when a new tab is created |
+| `action:createblock` | Logged when a new block is created (contains the block view type) |
+| `wsh:run` | Logged when a wsh command is executed (contains the command type) |
+| `debug:panic` | Logged when a backend (Go) panic happens. Contains a debugging string that can be used to find which panic was hit in our source code. No data is sent |
+| `conn:connect` | Logged each time a backend ssh/wsl connection connects (logs the conneciton type, no hostname or IP is sent) |
+| `conn:connecterror` | Logged when you try to connect but it fails (logs the connection type, no hostname or IP is set, and no detailed error information is sent) |
-| Name | Description |
-| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| ActiveMinutes | The number of minutes that the user has actively used Waveterm on a given day. This requires the terminal window to be in focus while the user is actively interacting with it. |
-| FgMinutes | The number of minutes that Waveterm has been in the foreground on a given day. This requires the terminal window to be in focus regardless of user interaction. |
-| OpenMinutes | The number of minutes that Waveterm has been open on a given day. This only requires that the terminal is open, even if the window is out of focus. |
-| NumBlocks | The number of existing blocks open on a given day |
-| NumTabs | The number of existing tabs open on a given day. |
-| NewTab | The number of new tabs created on a given day |
-| NumWindows | The number of existing windows open on a given day. |
-| NumWS | The number of existing workspaces on a given day. |
-| NumWSNamed | The number of named workspaces on a give day. |
-| NewTab | The number of new tabs opened on a given day. |
-| NumStartup | The number of times waveterm has been started on a given day. |
-| NumShutdown | The number of times waveterm has been shut down on a given day. |
-| SetTabTheme | The number of times the tab theme is changed from the context menu |
-| NumMagnify | The number of times any block is magnified |
-| NumPanics | The number of backend (golang) panics caught in the current day |
-| NumAIReqs | The number of AI requests made in the current day |
-| NumSSHConn | The number of distinct SSH connections that have been made to distinct hosts |
-| NumWSLConns | The number of distinct WSL connections that have been made to distinct distros |
-| Renderers | The number of new block views of each type are open on a given day. |
-| WshCmds | The number of wsh commands of each type run on a given day |
-| Blocks | The number of blocks of different view types open on a given day |
-| Conn | The number of successful remote connections made (and errors) on a given day |
+## Event Properties
-## Associated Data
+Each event may contain the following properties that are relevant to the particular events.
-In addition to the telemetry data collected, the following is also reported. It is stored in the `telemetry.ActivityType` type in the source code.
-
-| Name | Description |
-| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| Day | The date the telemetry is associated with. It does not include the time. |
-| Uploaded | A boolean that indicates if the telemetry for this day is finalized. It is false during the day the telemetry is associated with, but gets set true at the first telemetry upload after that. Once it is true, the data for that particular day will not be sent up with the telemetry any more. |
-| TzName | The code for the timezone the user's OS is reporting (e.g. PST, GMT, JST) |
-| TzOffset | The offset for the timezone the user's OS is reporting (e.g. -08:00, +00:00, +09:00) |
-| ClientVersion | Which version of Waveterm is installed. |
-| ClientArch | This includes the user's operating system (e.g. linux or darwin) and architecture (e.g. x86_64 or arm64). It does not include data for any Connections at this time. |
-| BuildTime | This serves as a more accurate version number that keeps track of when we built the version. It has no bearing on when that version was installed by you. |
-| OSRelease | This lists the version of the operating system the user has installed. |
-| Displays | Display resolutions (added in v0.9.3 to help us understand what screen resolutions to optimize for) |
-
-## Telemetry Metadata
-
-Lastly, some data is sent along with the telemetry that describes how to classify it. It is stored in the `wcloud.TelemetryInputType` in the source code.
-
-| Name | Description |
-| ----------------- | --------------------------------------------------------------------------------------------------------------------------- |
-| UserId | Currently Unused. This is an anonymous UUID intended for use in future features. |
-| ClientId | This is an anonymous UUID created when Waveterm is first launched. It is used for telemetry and sending prompts to Open AI. |
-| AppType | This is used to differentiate the current version of waveterm from the legacy app. |
-| AutoUpdateEnabled | Whether or not auto update is turned on. |
-| AutoUpdateChannel | The type of auto update in use. This specifically refers to whether a latest or beta channel is selected. |
-| CurDay | The current day (in your time zone) when telemetry is sent. It does not include the time of day. |
-
-## Geo Data
-
-We do not store IP addresses in our telemetry table. However, CloudFlare passes us Geo-Location headers. We store these two header values:
-
-| Name | Description |
-| ------------ | ----------------------------------------------------------------- |
-| CFCountry | 2-letter country code (e.g. "US", "FR", or "JP") |
-| CFRegionCode | region code (often a provence, region, or state within a country) |
+| Property | Description |
+| ------------------------ | ------------------------------------------------------------------------------------------------------ |
+| `client:arch` | Wave architecture (darwin, windows, linux) and x64 vs arm64 |
+| `client:version` | The Wave version (e.g. v0.11.1) |
+| `client:initial_version` | Initial installed wave version |
+| `client:buildtime` | The buildtime (more exact wave version) |
+| `client:osrelease` | A string representing the version of the OS you're running -- different for darwin, windows, and linux |
+| `client:isdev` | True/False if using the dev build |
+| `autoupdate:channel` | What auto-update channel you're on (latest vs beta) |
+| `autoupdate:enabled` | True/False if auto-updated is enabled |
+| `loc:countrycode` | Two character country code (e.g. US, CN, FR, JP) |
+| `loc:regioncode` | Two character region code (usually the State or Province within a country) |
+| `activity:activeminutes` | For app:activity, a number between 0-60 of how many minutes were active within the hour |
+| `activity:fgminutes` | For app:activity, a number between 0-60 of how many minutes Wave was the foreground application |
+| `activity:openminutes` | For app:activity, a number between 0-60 of how many minutes Wave was open |
+| `action:initiator` | For certain actions logs if the action was initiated by the UI or the backend |
+| `debug:panictype` | The string that identifies the panic location within our Go code |
+| `block:view` | Type of block, e.g. "preview", "waveai", "term", "sysinfo", etc. |
+| `ai:backendtype` | AI backend type (e.g. OpenAI, Gemini, Anthropic, etc.) |
+| `wsh:cmd` | The wsh command that was run, e.g. "view", "edit", "run", "editconfig" etc. |
+| `wsh:haderror` | True/False whether the wsh command returned an error |
+| `conn:conntype` | Type of connnection (ssh / wsl) |
+| `display:height` | Height of the main display in px |
+| `display:width` | Width of the main display in px |
+| `display:dpr` | DPR of the main display |
+| `display:count` | How many total displays |
+| `display:all` | JSON for all the displays attached (same attributes as above) |
+| `count:blocks` | Total number of blocks |
+| `count:tabs` | Total number of tabs |
+| `count:windows` | Total number of windows |
+| `count:workspaces` | Total number of workspaces |
+| `count:sshconn` | Total number of SSH connections |
+| `count:wslconn` | Total number of WSL connections |
+| `count:views` | Counts of the types of blocks (views) |
---
From 2df1c2e7bdf149b9b9c37e2a2b5d9bee2614c77d Mon Sep 17 00:00:00 2001
From: Mike Sawka
Date: Wed, 19 Feb 2025 15:44:16 -0800
Subject: [PATCH 30/47] fix multi-input paste (#2000)
a fix for #1862. so now when pasting information into a terminal when
multi-input is active, the data will be sent to all terminals in the
tab.
---
frontend/app/view/term/termwrap.ts | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/frontend/app/view/term/termwrap.ts b/frontend/app/view/term/termwrap.ts
index 370312b09..19e632a54 100644
--- a/frontend/app/view/term/termwrap.ts
+++ b/frontend/app/view/term/termwrap.ts
@@ -152,6 +152,7 @@ export class TermWrap {
sendDataHandler: (data: string) => void;
onSearchResultsDidChange?: (result: { resultIndex: number; resultCount: number }) => void;
private toDispose: TermTypes.IDisposable[] = [];
+ pasteActive: boolean = false;
constructor(
blockId: string,
@@ -217,6 +218,19 @@ export class TermWrap {
this.handleResize_debounced = debounce(50, this.handleResize.bind(this));
this.terminal.open(this.connectElem);
this.handleResize();
+ let pasteEventHandler = () => {
+ this.pasteActive = true;
+ setTimeout(() => {
+ this.pasteActive = false;
+ }, 30);
+ };
+ pasteEventHandler = pasteEventHandler.bind(this);
+ this.connectElem.addEventListener("paste", pasteEventHandler, true);
+ this.toDispose.push({
+ dispose: () => {
+ this.connectElem.removeEventListener("paste", pasteEventHandler, true);
+ },
+ });
}
async initTerminal() {
@@ -263,6 +277,12 @@ export class TermWrap {
if (!this.loaded) {
return;
}
+ if (this.pasteActive) {
+ this.pasteActive = false;
+ if (this.multiInputCallback) {
+ this.multiInputCallback(data);
+ }
+ }
this.sendDataHandler?.(data);
}
From 1f430201bd8da1d7401b8cbeaf03172cdd235a37 Mon Sep 17 00:00:00 2001
From: Sylvie Crowe <107814465+oneirocosm@users.noreply.github.com>
Date: Wed, 19 Feb 2025 16:45:43 -0800
Subject: [PATCH 31/47] Stat File Error Overlay and Clear Errors on Connection
Change (#1997)
This adds two small features:
- if a backend file stat operation fails, an error overlay is shown on
the frontend
- for all frontend error overlays, if the connection changes, the error
is cleared
---
frontend/app/view/preview/preview.tsx | 33 +++++++++++++++++++--------
1 file changed, 23 insertions(+), 10 deletions(-)
diff --git a/frontend/app/view/preview/preview.tsx b/frontend/app/view/preview/preview.tsx
index 27e09c323..c188d4e70 100644
--- a/frontend/app/view/preview/preview.tsx
+++ b/frontend/app/view/preview/preview.tsx
@@ -388,13 +388,20 @@ export class PreviewModel implements ViewModel {
if (fileName == null) {
return null;
}
- const statFile = await RpcApi.FileInfoCommand(TabRpcClient, {
- info: {
- path,
- },
- });
- console.log("stat file", statFile);
- return statFile;
+ try {
+ const statFile = await RpcApi.FileInfoCommand(TabRpcClient, {
+ info: {
+ path,
+ },
+ });
+ return statFile;
+ } catch (e) {
+ const errorStatus: ErrorMsg = {
+ status: "File Read Failed",
+ text: `${e}`,
+ };
+ globalStore.set(this.errorMsgAtom, errorStatus);
+ }
});
this.fileMimeType = atom>(async (get) => {
const fileInfo = await get(this.statFile);
@@ -410,13 +417,13 @@ export class PreviewModel implements ViewModel {
if (fileName == null) {
return null;
}
- let file: FileData;
try {
- file = await RpcApi.FileReadCommand(TabRpcClient, {
+ const file = await RpcApi.FileReadCommand(TabRpcClient, {
info: {
path,
},
});
+ return file;
} catch (e) {
const errorStatus: ErrorMsg = {
status: "File Read Failed",
@@ -424,7 +431,6 @@ export class PreviewModel implements ViewModel {
};
globalStore.set(this.errorMsgAtom, errorStatus);
}
- return file;
});
this.fileContentSaved = atom(null) as PrimitiveAtom;
@@ -1064,6 +1070,12 @@ function PreviewView({
}) {
const connStatus = useAtomValue(model.connStatus);
const [errorMsg, setErrorMsg] = useAtom(model.errorMsgAtom);
+ const connection = useAtomValue(model.connectionImmediate);
+
+ useEffect(() => {
+ setErrorMsg(null);
+ }, [connection]);
+
if (connStatus?.status != "connected") {
return null;
}
@@ -1089,6 +1101,7 @@ function PreviewView({
const fetchSuggestionsFn = async (query, ctx) => {
return await fetchSuggestions(model, query, ctx);
};
+
return (
<>
{/* */}
From b0e3b6d77791af93a0c56ab92ae803ae453ebee8 Mon Sep 17 00:00:00 2001
From: Evan Simkowitz
Date: Thu, 20 Feb 2025 10:17:32 -0800
Subject: [PATCH 32/47] Fix move & copy for prefix filesystems (#1998)
Also makes recursive the default for copy, adds better error for move
without recursive
---
frontend/app/store/wshclientapi.ts | 2 +-
.../app/view/preview/directorypreview.tsx | 305 +++++++-----------
frontend/app/view/preview/preview.tsx | 28 +-
go.mod | 2 +-
pkg/remote/fileshare/fileshare.go | 33 +-
pkg/remote/fileshare/fstype/fstype.go | 17 +-
pkg/remote/fileshare/fsutil/fsutil.go | 200 +++++-------
pkg/remote/fileshare/pathtree/pathtree.go | 5 +-
pkg/remote/fileshare/s3fs/s3fs.go | 130 ++++----
pkg/remote/fileshare/wavefs/wavefs.go | 12 +-
pkg/remote/fileshare/wshfs/wshfs.go | 4 +-
pkg/util/tarcopy/tarcopy.go | 2 -
pkg/wshrpc/wshclient/wshclient.go | 6 +-
pkg/wshrpc/wshremote/wshremote.go | 58 ++--
pkg/wshrpc/wshrpctypes.go | 4 +-
15 files changed, 375 insertions(+), 433 deletions(-)
diff --git a/frontend/app/store/wshclientapi.ts b/frontend/app/store/wshclientapi.ts
index 9ca02a5dd..fba651911 100644
--- a/frontend/app/store/wshclientapi.ts
+++ b/frontend/app/store/wshclientapi.ts
@@ -293,7 +293,7 @@ class RpcApiType {
}
// command "remotefilecopy" [call]
- RemoteFileCopyCommand(client: WshClient, data: CommandFileCopyData, opts?: RpcOpts): Promise {
+ RemoteFileCopyCommand(client: WshClient, data: CommandFileCopyData, opts?: RpcOpts): Promise {
return client.wshRpcCall("remotefilecopy", data, opts);
}
diff --git a/frontend/app/view/preview/directorypreview.tsx b/frontend/app/view/preview/directorypreview.tsx
index 2a29c1e5e..7ce987d06 100644
--- a/frontend/app/view/preview/directorypreview.tsx
+++ b/frontend/app/view/preview/directorypreview.tsx
@@ -2,9 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import { Button } from "@/app/element/button";
-import { CopyButton } from "@/app/element/copybutton";
import { Input } from "@/app/element/input";
-import { useDimensionsWithCallbackRef } from "@/app/hook/useDimensions";
import { ContextMenuModel } from "@/app/store/contextmenu";
import { atoms, getApi, globalStore } from "@/app/store/global";
import { RpcApi } from "@/app/store/wshclientapi";
@@ -39,16 +37,13 @@ import "./directorypreview.scss";
const PageJumpSize = 20;
-type FileCopyStatus = {
- copyData: CommandFileCopyData;
- copyError: string;
- allowRetry: boolean;
- isDir: boolean;
-};
+const recursiveError = "recursive flag must be set for directory operations";
+const overwriteError = "set overwrite flag to delete the existing file";
+const mergeError = "set overwrite flag to delete the existing contents or set merge flag to merge the contents";
declare module "@tanstack/react-table" {
interface TableMeta {
- updateName: (path: string) => void;
+ updateName: (path: string, isDir: boolean) => void;
newFile: () => void;
newDirectory: () => void;
}
@@ -216,6 +211,7 @@ function DirectoryTable({
newDirectory,
}: DirectoryTableProps) {
const fullConfig = useAtomValue(atoms.fullConfigAtom);
+ const setErrorMsg = useSetAtom(model.errorMsgAtom);
const getIconFromMimeType = useCallback(
(mimeType: string): string => {
while (mimeType.length > 0) {
@@ -291,7 +287,7 @@ function DirectoryTable({
const setEntryManagerProps = useSetAtom(entryManagerOverlayPropsAtom);
- const updateName = useCallback((path: string) => {
+ const updateName = useCallback((path: string, isDir: boolean) => {
const fileName = path.split("/").at(-1);
setEntryManagerProps({
entryManagerType: EntryManagerType.EditName,
@@ -302,24 +298,47 @@ function DirectoryTable({
const lastInstance = path.lastIndexOf(fileName);
newPath = path.substring(0, lastInstance) + newName;
console.log(`replacing ${fileName} with ${newName}: ${path}`);
- fireAndForget(async () => {
- try {
- await RpcApi.FileMoveCommand(TabRpcClient, {
- srcuri: await model.formatRemoteUri(path, globalStore.get),
- desturi: await model.formatRemoteUri(newPath, globalStore.get),
- opts: {
- recursive: true,
- },
- });
- } catch (e) {
- const errorStatus: ErrorMsg = {
- status: "Rename Failed",
- text: `${e}`,
- };
- globalStore.set(model.errorMsgAtom, errorStatus);
- }
- model.refreshCallback();
- });
+ const handleRename = (recursive: boolean) =>
+ fireAndForget(async () => {
+ try {
+ let srcuri = await model.formatRemoteUri(path, globalStore.get);
+ if (isDir) {
+ srcuri += "/";
+ }
+ await RpcApi.FileMoveCommand(TabRpcClient, {
+ srcuri,
+ desturi: await model.formatRemoteUri(newPath, globalStore.get),
+ opts: {
+ recursive,
+ },
+ });
+ } catch (e) {
+ const errorText = `${e}`;
+ console.warn(`Rename failed: ${errorText}`);
+ let errorMsg: ErrorMsg;
+ if (errorText.includes(recursiveError)) {
+ errorMsg = {
+ status: "Confirm Rename Directory",
+ text: "Renaming a directory requires the recursive flag. Proceed?",
+ level: "warning",
+ buttons: [
+ {
+ text: "Rename Recursively",
+ onClick: () => handleRename(true),
+ },
+ ],
+ };
+ } else {
+ errorMsg = {
+ status: "Rename Failed",
+ text: `${e}`,
+ };
+ }
+ setErrorMsg(errorMsg);
+ }
+ model.refreshCallback();
+ });
+ handleRename(false);
}
setEntryManagerProps(undefined);
},
@@ -495,6 +514,7 @@ function TableBody({
const warningBoxRef = useRef();
const rowRefs = useRef([]);
const conn = useAtomValue(model.connection);
+ const setErrorMsg = useSetAtom(model.errorMsgAtom);
useEffect(() => {
if (focusIndex !== null && rowRefs.current[focusIndex] && bodyRef.current && osRef) {
@@ -529,8 +549,41 @@ function TableBody({
if (finfo == null) {
return;
}
- const normPath = finfo.path;
const fileName = finfo.path.split("/").pop();
+ const handleFileDelete = (recursive: boolean) =>
+ fireAndForget(async () => {
+ const path = await model.formatRemoteUri(finfo.path, globalStore.get);
+ try {
+ await RpcApi.FileDeleteCommand(TabRpcClient, {
+ path,
+ recursive,
+ });
+ } catch (e) {
+ const errorText = `${e}`;
+ console.warn(`Delete failed: ${errorText}`);
+ let errorMsg: ErrorMsg;
+ if (errorText.includes(recursiveError)) {
+ errorMsg = {
+ status: "Confirm Delete Directory",
+ text: "Deleting a directory requires the recursive flag. Proceed?",
+ level: "warning",
+ buttons: [
+ {
+ text: "Delete Recursively",
+ onClick: () => handleFileDelete(true),
+ },
+ ],
+ };
+ } else {
+ errorMsg = {
+ status: "Delete Failed",
+ text: `${e}`,
+ };
+ }
+ setErrorMsg(errorMsg);
+ }
+ setRefreshVersion((current) => current + 1);
+ });
const menu: ContextMenuItem[] = [
{
label: "New File",
@@ -547,7 +600,7 @@ function TableBody({
{
label: "Rename",
click: () => {
- table.options.meta.updateName(finfo.path);
+ table.options.meta.updateName(finfo.path, finfo.isdir);
},
},
{
@@ -577,23 +630,7 @@ function TableBody({
},
{
label: "Delete",
- click: () => {
- fireAndForget(async () => {
- try {
- await RpcApi.FileDeleteCommand(TabRpcClient, {
- path: await model.formatRemoteUri(finfo.path, globalStore.get),
- recursive: false,
- });
- } catch (e) {
- const errorStatus: ErrorMsg = {
- status: "Delete Failed",
- text: `${e}`,
- };
- globalStore.set(model.errorMsgAtom, errorStatus);
- }
- setRefreshVersion((current) => current + 1);
- });
- },
+ click: () => handleFileDelete(false),
}
);
ContextMenuModel.showContextMenu(menu, e);
@@ -728,7 +765,7 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) {
const blockData = useAtomValue(model.blockAtom);
const finfo = useAtomValue(model.statFile);
const dirPath = finfo?.path;
- const [copyStatus, setCopyStatus] = useState(null);
+ const setErrorMsg = useSetAtom(model.errorMsgAtom);
useEffect(() => {
model.refreshCallback = () => {
@@ -754,11 +791,10 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) {
);
entries = file.entries ?? [];
} catch (e) {
- const errorStatus: ErrorMsg = {
+ setErrorMsg({
status: "Cannot Read Directory",
text: `${e}`,
- };
- globalStore.set(model.errorMsgAtom, errorStatus);
+ });
}
setUnfilteredData(entries);
};
@@ -854,28 +890,48 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) {
});
const handleDropCopy = useCallback(
- async (data: CommandFileCopyData, isDir) => {
+ async (data: CommandFileCopyData, isDir: boolean) => {
try {
await RpcApi.FileCopyCommand(TabRpcClient, data, { timeout: data.opts.timeout });
- setCopyStatus(null);
} catch (e) {
- console.log("copy failed:", e);
+ console.warn("Copy failed:", e);
const copyError = `${e}`;
- const allowRetry =
- copyError.includes("overwrite not specified") ||
- copyError.includes("neither overwrite nor merge specified") ||
- copyError.includes("neither merge nor overwrite specified");
- const copyStatus: FileCopyStatus = {
- copyError,
- copyData: data,
- allowRetry,
- isDir: isDir,
- };
- setCopyStatus(copyStatus);
+ const allowRetry = copyError.includes(overwriteError) || copyError.includes(mergeError);
+ let errorMsg: ErrorMsg;
+ if (allowRetry) {
+ errorMsg = {
+ status: "Confirm Overwrite File(s)",
+ text: "This copy operation will overwrite an existing file. Would you like to continue?",
+ level: "warning",
+ buttons: [
+ {
+ text: "Delete Then Copy",
+ onClick: async () => {
+ data.opts.overwrite = true;
+ await handleDropCopy(data, isDir);
+ },
+ },
+ {
+ text: "Sync",
+ onClick: async () => {
+ data.opts.merge = true;
+ await handleDropCopy(data, isDir);
+ },
+ },
+ ],
+ };
+ } else {
+ errorMsg = {
+ status: "Copy Failed",
+ text: copyError,
+ level: "error",
+ };
+ }
+ setErrorMsg(errorMsg);
}
model.refreshCallback();
},
- [setCopyStatus, model.refreshCallback]
+ [model.refreshCallback]
);
const [, drop] = useDrop(
@@ -908,7 +964,7 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) {
},
// TODO: mabe add a hover option?
}),
- [dirPath, model.formatRemoteUri, model.refreshCallback, setCopyStatus]
+ [dirPath, model.formatRemoteUri, model.refreshCallback]
);
useEffect(() => {
@@ -1000,13 +1056,6 @@ function DirectoryPreview({ model }: DirectoryPreviewProps) {
onContextMenu={(e) => handleFileContextMenu(e)}
onClick={() => setEntryManagerProps(undefined)}
>
- {copyStatus != null && (
-
- )}
void;
- handleDropCopy: (data: CommandFileCopyData, isDir: boolean) => Promise;
- }) => {
- const [overlayRefCallback, _, domRect] = useDimensionsWithCallbackRef(30);
- const width = domRect?.width;
-
- const handleRetryCopy = React.useCallback(
- async (copyOpt?: string) => {
- if (!copyStatus) {
- return;
- }
- let overwrite = copyOpt == "overwrite";
- let merge = copyOpt == "merge";
- const updatedData = {
- ...copyStatus.copyData,
- opts: { ...copyStatus.copyData.opts, overwrite, merge },
- };
- await handleDropCopy(updatedData, copyStatus.isDir);
- },
- [copyStatus.copyData]
- );
-
- let statusText = "Copy Error";
- let errorMsg = `error: ${copyStatus?.copyError}`;
- if (copyStatus?.allowRetry) {
- statusText = "Confirm Overwrite File(s)";
- errorMsg = "This copy operation will overwrite an existing file. Would you like to continue?";
- }
-
- const buttonClassName = "outlined grey font-size-11 vertical-padding-3 horizontal-padding-7";
-
- const handleRemoveCopyError = React.useCallback(async () => {
- setCopyStatus(null);
- }, [setCopyStatus]);
-
- const handleCopyToClipboard = React.useCallback(async () => {
- await navigator.clipboard.writeText(errorMsg);
- }, [errorMsg]);
-
- return (
-