mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ea8f45284 | |||
| 4ff247d2f2 | |||
| 7708db7060 | |||
| 4907552379 | |||
| 96ad70ffdd | |||
| 62cc9d8a6b | |||
| de08cc8340 | |||
| 190a53662c | |||
| b19bef2ffb | |||
| af38ebab54 | |||
| 38f07ae3f9 | |||
| 8423334cde | |||
| bc057876a1 | |||
| d344fd9b6d | |||
| 31414a7536 | |||
| e281cebb56 | |||
| 65c446e4a1 | |||
| 047513bf3a | |||
| 78f838439a | |||
| 470dfb19b7 | |||
| ade5bc01b3 | |||
| c768616a24 | |||
| bb7ea131b4 | |||
| 0b2fdf0ca0 | |||
| eced7120c7 | |||
| 4395c7410d | |||
| b0c5443916 | |||
| 85ec6d45fe | |||
| fd97f895d0 | |||
| 071a051382 |
@@ -58,6 +58,7 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: "Push version bump: ${{ steps.bump-version.outputs.WAVETERM_VERSION }}"
|
||||
if: github.ref_protected
|
||||
run: |
|
||||
# Create a new commit for the package version bump in package.json
|
||||
export VERSION=${{ steps.bump-version.outputs.WAVETERM_VERSION }}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
body {
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#storybook-root {
|
||||
|
||||
@@ -3,9 +3,6 @@ import type { Preview } from "@storybook/react";
|
||||
import React from "react";
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import "../frontend/app/theme.less";
|
||||
import "../frontend/app/app.less";
|
||||
import "../frontend/app/reset.less";
|
||||
import "./global.css";
|
||||
|
||||
const preview: Preview = {
|
||||
|
||||
Vendored
-18
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Start Storybook",
|
||||
"type": "shell",
|
||||
"command": "yarn storybook",
|
||||
"presentation": {
|
||||
"reveal": "silent",
|
||||
"panel": "shared"
|
||||
},
|
||||
"runOptions": {
|
||||
"instanceLimit": 1,
|
||||
"runOn": "folderOpen"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+15
-16
@@ -17,8 +17,7 @@ vars:
|
||||
tasks:
|
||||
electron:dev:
|
||||
desc: Run the Electron application via the Vite dev server (enables hot reloading).
|
||||
cmds:
|
||||
- yarn dev
|
||||
cmd: yarn dev
|
||||
deps:
|
||||
- yarn
|
||||
- build:backend
|
||||
@@ -28,12 +27,15 @@ tasks:
|
||||
|
||||
electron:start:
|
||||
desc: Run the Electron application directly.
|
||||
cmds:
|
||||
- yarn start
|
||||
cmd: yarn start
|
||||
deps:
|
||||
- yarn
|
||||
- build:backend
|
||||
|
||||
storybook:
|
||||
desc: Start the Storybook server.
|
||||
cmd: yarn storybook
|
||||
|
||||
package:
|
||||
desc: Package the application for the current platform.
|
||||
cmds:
|
||||
@@ -98,12 +100,12 @@ tasks:
|
||||
requires:
|
||||
vars:
|
||||
- ARCHS
|
||||
cmds:
|
||||
- cmd: CGO_ENABLED=1 GOARCH={{.GOARCH}} go build -tags "osusergo,netcgo,sqlite_omit_load_extension" -ldflags "{{.GO_LDFLAGS}} -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wavesrv.{{if eq .GOARCH "amd64"}}x64{{else}}{{.GOARCH}}{{end}}{{exeExt}} cmd/server/main-server.go
|
||||
for:
|
||||
var: ARCHS
|
||||
split: ","
|
||||
as: GOARCH
|
||||
cmd:
|
||||
cmd: CGO_ENABLED=1 GOARCH={{.GOARCH}} go build -tags "osusergo,netcgo,sqlite_omit_load_extension" -ldflags "{{.GO_LDFLAGS}} -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wavesrv.{{if eq .GOARCH "amd64"}}x64{{else}}{{.GOARCH}}{{end}}{{exeExt}} cmd/server/main-server.go
|
||||
for:
|
||||
var: ARCHS
|
||||
split: ","
|
||||
as: GOARCH
|
||||
sources:
|
||||
- "cmd/server/*.go"
|
||||
- "pkg/**/*.go"
|
||||
@@ -173,8 +175,7 @@ tasks:
|
||||
- "pkg/**/*.go"
|
||||
generates:
|
||||
- dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.NORMALIZEDARCH}}{{.EXT}}
|
||||
cmds:
|
||||
- (CGO_ENABLED=0 GOOS={{.GOOS}} GOARCH={{.GOARCH}} go build -ldflags="-s -w -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.NORMALIZEDARCH}}{{.EXT}} cmd/wsh/main-wsh.go)
|
||||
cmd: (CGO_ENABLED=0 GOOS={{.GOOS}} GOARCH={{.GOARCH}} go build -ldflags="-s -w -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.NORMALIZEDARCH}}{{.EXT}} cmd/wsh/main-wsh.go)
|
||||
deps:
|
||||
- go:mod:tidy
|
||||
internal: true
|
||||
@@ -251,8 +252,7 @@ tasks:
|
||||
- yarn.lock
|
||||
- package.json
|
||||
- .yarnrc.yml
|
||||
cmds:
|
||||
- yarn
|
||||
cmd: yarn
|
||||
|
||||
go:mod:tidy:
|
||||
desc: Runs `go mod tidy`
|
||||
@@ -261,5 +261,4 @@ tasks:
|
||||
- go.sum
|
||||
sources:
|
||||
- go.mod
|
||||
cmds:
|
||||
- go mod tidy
|
||||
cmd: go mod tidy
|
||||
|
||||
@@ -42,8 +42,8 @@ import (
|
||||
var WaveVersion = "0.0.0"
|
||||
var BuildTime = "0"
|
||||
|
||||
const InitialTelemetryWait = 30 * time.Second
|
||||
const TelemetryTick = 10 * time.Minute
|
||||
const InitialTelemetryWait = 10 * time.Second
|
||||
const TelemetryTick = 2 * time.Minute
|
||||
const TelemetryInterval = 4 * time.Hour
|
||||
|
||||
const ReadySignalPidVarName = "WAVETERM_READY_SIGNAL_PID"
|
||||
|
||||
@@ -47,11 +47,16 @@ func parseMetaSets(metaSets []string) (map[string]interface{}, error) {
|
||||
}
|
||||
meta[fields[0]] = val
|
||||
} else {
|
||||
fval, err := strconv.ParseFloat(setVal, 64)
|
||||
ival, err := strconv.ParseInt(setVal, 0, 64)
|
||||
if err == nil {
|
||||
meta[fields[0]] = fval
|
||||
meta[fields[0]] = ival
|
||||
} else {
|
||||
meta[fields[0]] = setVal
|
||||
fval, err := strconv.ParseFloat(setVal, 64)
|
||||
if err == nil {
|
||||
meta[fields[0]] = fval
|
||||
} else {
|
||||
meta[fields[0]] = setVal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ const path = require("path");
|
||||
const config = {
|
||||
appId: pkg.build.appId,
|
||||
productName: pkg.productName,
|
||||
executableName: pkg.name,
|
||||
executableName: pkg.productName,
|
||||
artifactName: "${productName}-${platform}-${arch}-${version}.${ext}",
|
||||
generateUpdatesFilesForAllChannels: true,
|
||||
npmRebuild: false,
|
||||
@@ -55,6 +55,7 @@ const config = {
|
||||
linux: {
|
||||
artifactName: "${name}-${platform}-${arch}-${version}.${ext}",
|
||||
category: "TerminalEmulator",
|
||||
executableName: pkg.name,
|
||||
icon: "build/icons.icns",
|
||||
target: ["zip", "deb", "rpm", "AppImage", "pacman"],
|
||||
synopsis: pkg.description,
|
||||
@@ -86,7 +87,7 @@ const config = {
|
||||
if (context.electronPlatformName === "darwin" && context.arch === Arch.universal) {
|
||||
const packageBinDir = path.resolve(
|
||||
context.appOutDir,
|
||||
`${pkg.name}.app/Contents/Resources/app.asar.unpacked/dist/bin`
|
||||
`${pkg.productName}.app/Contents/Resources/app.asar.unpacked/dist/bin`
|
||||
);
|
||||
|
||||
// Reapply file permissions to the wavesrv binaries in the final app package
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { fn } from "@storybook/test";
|
||||
import { Button } from "./button";
|
||||
|
||||
const meta = {
|
||||
title: "Elements/Button",
|
||||
component: Button,
|
||||
args: {
|
||||
children: "Click Me",
|
||||
disabled: false,
|
||||
className: "",
|
||||
onClick: fn(),
|
||||
},
|
||||
argTypes: {
|
||||
onClick: {
|
||||
action: "clicked",
|
||||
description: "Click event handler",
|
||||
},
|
||||
children: {
|
||||
description: "Content inside the button",
|
||||
},
|
||||
disabled: {
|
||||
description: "Disables the button if true",
|
||||
},
|
||||
className: {
|
||||
description: "Additional class names to style the button",
|
||||
},
|
||||
},
|
||||
} satisfies Meta<typeof Button>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
disabled: true,
|
||||
children: "Disabled Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const GreySolid: Story = {
|
||||
args: {
|
||||
className: "solid grey",
|
||||
children: "Grey Solid Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const RedSolid: Story = {
|
||||
args: {
|
||||
className: "solid red",
|
||||
children: "Red Solid Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const YellowSolid: Story = {
|
||||
args: {
|
||||
className: "solid yellow",
|
||||
children: "Yellow Solid Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const GreenOutlined: Story = {
|
||||
args: {
|
||||
className: "outlined green",
|
||||
children: "Green Outline Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const GreyOutlined: Story = {
|
||||
args: {
|
||||
className: "outlined grey",
|
||||
children: "Grey Outline Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const RedOutlined: Story = {
|
||||
args: {
|
||||
className: "outlined red",
|
||||
children: "Red Outline Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const YellowOutlined: Story = {
|
||||
args: {
|
||||
className: "outlined yellow",
|
||||
children: "Yellow Outline Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const GreenGhostText: Story = {
|
||||
args: {
|
||||
className: "ghost green",
|
||||
children: "Yellow Ghost Text Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const GreyGhostText: Story = {
|
||||
args: {
|
||||
className: "ghost grey",
|
||||
children: "Grey Ghost Text Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const RedGhost: Story = {
|
||||
args: {
|
||||
className: "ghost red",
|
||||
children: "Red Ghost Text Button",
|
||||
},
|
||||
};
|
||||
|
||||
export const YellowGhostText: Story = {
|
||||
args: {
|
||||
className: "ghost yellow",
|
||||
children: "Yellow Ghost Text Button",
|
||||
},
|
||||
};
|
||||
@@ -9,39 +9,41 @@ import "./button.less";
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
target?: string;
|
||||
source?: string;
|
||||
}
|
||||
|
||||
const Button = memo(
|
||||
forwardRef<HTMLButtonElement, ButtonProps>(({ children, disabled, className = "", ...props }: ButtonProps, ref) => {
|
||||
const btnRef = useRef<HTMLButtonElement>(null);
|
||||
useImperativeHandle(ref, () => btnRef.current as HTMLButtonElement);
|
||||
forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ children, disabled, source, className = "", ...props }: ButtonProps, ref) => {
|
||||
const btnRef = useRef<HTMLButtonElement>(null);
|
||||
useImperativeHandle(ref, () => btnRef.current as HTMLButtonElement);
|
||||
|
||||
const childrenArray = Children.toArray(children);
|
||||
const childrenArray = Children.toArray(children);
|
||||
|
||||
// Check if the className contains any of the categories: solid, outlined, or ghost
|
||||
const containsButtonCategory = /(solid|outline|ghost)/.test(className);
|
||||
// If no category is present, default to 'solid'
|
||||
const categoryClassName = containsButtonCategory ? className : `solid ${className}`;
|
||||
// Check if the className contains any of the categories: solid, outlined, or ghost
|
||||
const containsButtonCategory = /(solid|outline|ghost)/.test(className);
|
||||
// If no category is present, default to 'solid'
|
||||
const categoryClassName = containsButtonCategory ? className : `solid ${className}`;
|
||||
|
||||
// Check if the className contains any of the color options: green, grey, red, or yellow
|
||||
const containsColor = /(green|grey|red|yellow)/.test(categoryClassName);
|
||||
// If no color is present, default to 'green'
|
||||
const finalClassName = containsColor ? categoryClassName : `green ${categoryClassName}`;
|
||||
// Check if the className contains any of the color options: green, grey, red, or yellow
|
||||
const containsColor = /(green|grey|red|yellow)/.test(categoryClassName);
|
||||
// If no color is present, default to 'green'
|
||||
const finalClassName = containsColor ? categoryClassName : `green ${categoryClassName}`;
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={btnRef}
|
||||
tabIndex={disabled ? -1 : 0}
|
||||
className={clsx("button", finalClassName)}
|
||||
disabled={disabled}
|
||||
{...props}
|
||||
>
|
||||
{childrenArray}
|
||||
</button>
|
||||
);
|
||||
})
|
||||
return (
|
||||
<button
|
||||
ref={btnRef}
|
||||
tabIndex={disabled ? -1 : 0}
|
||||
className={clsx("button", finalClassName)}
|
||||
disabled={disabled}
|
||||
{...props}
|
||||
>
|
||||
{childrenArray}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
Button.displayName = "Button";
|
||||
|
||||
export { Button };
|
||||
|
||||
@@ -144,6 +144,17 @@ const QuickTips = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="tip-section-header">Need More Help?</div>
|
||||
<div className="tip">
|
||||
<div>
|
||||
<div>
|
||||
<a target="_blank" href="https://discord.gg/XfvZ334gwU" rel="noopener">
|
||||
Join Our Discord
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -12,8 +12,6 @@ import { FlexiModal } from "./modal";
|
||||
import { QuickTips } from "@/app/element/quicktips";
|
||||
import { atoms } from "@/app/store/global";
|
||||
import { modalsModel } from "@/app/store/modalmodel";
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { WindowRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { atom, PrimitiveAtom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||
import "./tos.less";
|
||||
|
||||
@@ -34,7 +32,7 @@ const ModalPage1 = () => {
|
||||
};
|
||||
|
||||
const setTelemetry = (value: boolean) => {
|
||||
RpcApi.SetConfigCommand(WindowRpcClient, { "telemetry:enabled": value })
|
||||
services.ClientService.TelemetryUpdate(value)
|
||||
.then(() => {
|
||||
setTelemetryEnabled(value);
|
||||
})
|
||||
|
||||
@@ -47,6 +47,9 @@ class ClientServiceType {
|
||||
MakeWindow(): Promise<WaveWindow> {
|
||||
return WOS.callBackendService("client", "MakeWindow", Array.from(arguments))
|
||||
}
|
||||
TelemetryUpdate(arg2: boolean): Promise<void> {
|
||||
return WOS.callBackendService("client", "TelemetryUpdate", Array.from(arguments))
|
||||
}
|
||||
}
|
||||
|
||||
export const ClientService = new ClientServiceType();
|
||||
|
||||
@@ -11,7 +11,7 @@ import React, { createRef, useCallback, useEffect, useRef, useState } from "reac
|
||||
import { debounce } from "throttle-debounce";
|
||||
import { Tab } from "./tab";
|
||||
import "./tabbar.less";
|
||||
import { UpdateStatusBanner } from "./updatestatus";
|
||||
import { UpdateStatusBanner } from "./updatebanner";
|
||||
|
||||
const TAB_DEFAULT_WIDTH = 130;
|
||||
const TAB_MIN_WIDTH = 100;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Button } from "@/element/button";
|
||||
import { atoms, getApi } from "@/store/global";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { memo, useEffect, useState } from "react";
|
||||
import "./updatebanner.less";
|
||||
|
||||
const UpdateStatusBannerComponent = ({ buttonRef }: { buttonRef: React.RefObject<HTMLButtonElement> }) => {
|
||||
const appUpdateStatus = useAtomValue(atoms.updaterStatusAtom);
|
||||
const [updateStatusMessage, setUpdateStatusMessage] = useState<string>();
|
||||
const [dismissBannerTimeout, setDismissBannerTimeout] = useState<NodeJS.Timeout>();
|
||||
|
||||
useEffect(() => {
|
||||
let message: string;
|
||||
let dismissBanner = false;
|
||||
switch (appUpdateStatus) {
|
||||
case "ready":
|
||||
message = "Update Available";
|
||||
break;
|
||||
case "downloading":
|
||||
message = "Downloading Update";
|
||||
break;
|
||||
case "installing":
|
||||
message = "Installing Update";
|
||||
break;
|
||||
case "error":
|
||||
message = "Updater Error: Try Checking Again";
|
||||
dismissBanner = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
setUpdateStatusMessage(message);
|
||||
|
||||
// Clear any existing timeout
|
||||
if (dismissBannerTimeout) {
|
||||
clearTimeout(dismissBannerTimeout);
|
||||
}
|
||||
|
||||
// If we want to dismiss the banner, set the new timeout, otherwise clear the state
|
||||
if (dismissBanner) {
|
||||
setDismissBannerTimeout(
|
||||
setTimeout(() => {
|
||||
setUpdateStatusMessage(null);
|
||||
setDismissBannerTimeout(null);
|
||||
}, 10000)
|
||||
);
|
||||
} else {
|
||||
setDismissBannerTimeout(null);
|
||||
}
|
||||
}, [appUpdateStatus]);
|
||||
|
||||
function onClick() {
|
||||
getApi().installAppUpdate();
|
||||
}
|
||||
|
||||
if (updateStatusMessage) {
|
||||
return (
|
||||
<Button
|
||||
ref={buttonRef}
|
||||
className="update-available-button"
|
||||
title={appUpdateStatus === "ready" ? "Click to Install Update" : updateStatusMessage}
|
||||
onClick={onClick}
|
||||
disabled={appUpdateStatus !== "ready"}
|
||||
>
|
||||
{updateStatusMessage}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const UpdateStatusBanner = memo(UpdateStatusBannerComponent) as typeof UpdateStatusBannerComponent;
|
||||
@@ -1,49 +0,0 @@
|
||||
import { Button } from "@/element/button";
|
||||
import { atoms, getApi } from "@/store/global";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { memo } from "react";
|
||||
import "./updatestatus.less";
|
||||
|
||||
const UpdateStatusBannerComponent = ({ buttonRef }: { buttonRef: React.RefObject<HTMLButtonElement> }) => {
|
||||
const appUpdateStatus = useAtomValue(atoms.updaterStatusAtom);
|
||||
function onClick() {
|
||||
getApi().installAppUpdate();
|
||||
}
|
||||
|
||||
let buttonText: string;
|
||||
switch (appUpdateStatus) {
|
||||
case "ready":
|
||||
buttonText = "Update Available";
|
||||
break;
|
||||
case "checking":
|
||||
buttonText = "Checking for Updates";
|
||||
break;
|
||||
case "downloading":
|
||||
buttonText = "Downloading Update";
|
||||
break;
|
||||
case "installing":
|
||||
buttonText = "Installing Update";
|
||||
break;
|
||||
case "error":
|
||||
buttonText = "Updater Error: Try Checking Again";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (buttonText) {
|
||||
return (
|
||||
<Button
|
||||
ref={buttonRef}
|
||||
className="update-available-button"
|
||||
title={appUpdateStatus === "ready" ? "Click to Install Update" : buttonText}
|
||||
onClick={onClick}
|
||||
disabled={appUpdateStatus !== "ready"}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const UpdateStatusBanner = memo(UpdateStatusBannerComponent) as typeof UpdateStatusBannerComponent;
|
||||
@@ -149,8 +149,11 @@ class CpuPlotViewModel {
|
||||
if (initialData == null) {
|
||||
return;
|
||||
}
|
||||
const newData = this.getDefaultData();
|
||||
const initialDataItems: DataItem[] = initialData.map(convertWaveEventToDataItem);
|
||||
globalStore.set(this.addDataAtom, initialDataItems);
|
||||
// splice the initial data into the default data (replacing the newest points)
|
||||
newData.splice(newData.length - initialDataItems.length, initialDataItems.length, ...initialDataItems);
|
||||
globalStore.set(this.addDataAtom, newData);
|
||||
} catch (e) {
|
||||
console.log("Error loading initial data for cpuplot", e);
|
||||
} finally {
|
||||
@@ -158,7 +161,7 @@ class CpuPlotViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
getDefaultData(): Array<DataItem> {
|
||||
getDefaultData(): DataItem[] {
|
||||
// set it back one to avoid backwards line being possible
|
||||
const numPoints = globalStore.get(this.numPoints);
|
||||
const currentTime = Date.now() - 1000;
|
||||
@@ -197,6 +200,8 @@ function CpuPlotView({ model, blockId }: CpuPlotViewProps) {
|
||||
lastConnName.current = connName;
|
||||
model.loadInitialData();
|
||||
}
|
||||
}, [connStatus.status, connName]);
|
||||
React.useEffect(() => {
|
||||
const unsubFn = waveEventSubscribe({
|
||||
eventType: "sysinfo",
|
||||
scope: connName,
|
||||
@@ -209,11 +214,11 @@ function CpuPlotView({ model, blockId }: CpuPlotViewProps) {
|
||||
addPlotData([dataItem]);
|
||||
},
|
||||
});
|
||||
console.log("subscribe to sysinfo", connName);
|
||||
return () => {
|
||||
unsubFn();
|
||||
};
|
||||
}, [connName]);
|
||||
React.useEffect(() => {}, [connName]);
|
||||
if (connStatus?.status != "connected") {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import { Atom, atom, PrimitiveAtom } from "jotai";
|
||||
import "./helpview.less";
|
||||
|
||||
const helpText = `
|
||||
For more up-to-date documentation, please visit [our docs site](http://betadocs.waveterm.dev)
|
||||
|
||||
|
||||
## Blocks
|
||||
Every individual Component is contained in its own block. These can be added, removed, moved and resized. Each block has its own header which can be right clicked to reveal more operations you can do with that block.
|
||||
|
||||
|
||||
@@ -97,54 +97,6 @@ function isStreamingType(mimeType: string): boolean {
|
||||
mimeType.startsWith("image/")
|
||||
);
|
||||
}
|
||||
const previewTipText = `
|
||||
### Preview
|
||||
Preview is the generic type of block used for viewing files. This can take many different forms based on the type of file being viewed.
|
||||
You can use \`wsh view [path]\` from any Wave terminal window to open a preview block with the contents of the specified path (e.g. \`wsh view .\` or \`wsh view ~/myimage.jpg\`).
|
||||
|
||||
#### Directory
|
||||
When looking at a directory, preview will show a file viewer much like MacOS' *Finder* application or Windows' *File Explorer* application. This variant is slightly more geared toward software development with the focus on seeing what is shown by the \`ls -alh\` command.
|
||||
|
||||
##### View a New File
|
||||
The simplest way to view a new file is to double click its row in the file viewer. Alternatively, while the block is focused, you can use the ↑ and ↓ arrow keys to select a row and press enter to preview the associated file.
|
||||
|
||||
##### View the Parent Directory
|
||||
In the directory view, this is as simple as opening the \`..\` file as if it were a regular file. This can be done with the method above. You can also use the keyboard shortcut \`Cmd + ArrowUp\`.
|
||||
|
||||
##### Navigate Back and Forward
|
||||
When looking at a file, you can navigate back by clicking the back button in the block header or the keyboard shortcut \`Cmd + ArrowLeft\`. You can always navigate back and forward using \`Cmd + ArrowLeft\` and \`Cmd + ArrowRight\`.
|
||||
|
||||
##### Filter the List of Files
|
||||
While the block is focused, you can filter by filename by typing a substring of the filename you're working for. To clear the filter, you can click the ✕ on the filter dropdown or press esc.
|
||||
|
||||
##### Sort by a File Column
|
||||
To sort a file by a specific column, click on the header for that column. If you click the header again, it will reverse the sort order.
|
||||
|
||||
##### Hide and Show Hidden Files
|
||||
At the right of the block header, there is an 👁️ button. Clicking this button hides and shows hidden files.
|
||||
|
||||
##### Refresh the Directory
|
||||
At the right of the block header, there is a refresh button. Clicking this button refreshes the directory contents.
|
||||
|
||||
##### Navigate to Common Directories
|
||||
At the left of the block header, there is a file icon. Clicking and holding on this icon opens a menu where you can select a common folder to navigate to. The available options are *Home*, *Desktop*, *Downloads*, and *Root*.
|
||||
|
||||
##### Open a New Terminal in the Current Directory
|
||||
If you right click the header of the block (alternatively, click the gear icon), one of the menu items listed is **Open Terminal in New Block**. This will create a new terminal block at your current directory.
|
||||
|
||||
##### Open a New Terminal in a Child Directory
|
||||
If you want to open a terminal for a child directory instead, you can right click on that file's row to get the **Open Terminal in New Block** option. Clicking this will open a terminal at that directory. Note that this option is only available for children that are directories.
|
||||
|
||||
##### Open a New Preview for a Child
|
||||
To open a new Preview Block for a Child, you can right click on that file's row and select the **Open Preview in New Block** option.
|
||||
|
||||
#### Markdown
|
||||
Opening a markdown file will bring up a view of the rendered markdown. These files cannot be edited in the preview at this time.
|
||||
|
||||
#### Images/Media
|
||||
Opening a picture will bring up the image of that picture. Opening a video will bring up a player that lets you watch the video.
|
||||
`;
|
||||
|
||||
export class PreviewModel implements ViewModel {
|
||||
viewType: string;
|
||||
blockId: string;
|
||||
@@ -173,7 +125,8 @@ export class PreviewModel implements ViewModel {
|
||||
fullFile: jotai.Atom<Promise<FullFile>>;
|
||||
fileMimeType: jotai.Atom<Promise<string>>;
|
||||
fileMimeTypeLoadable: jotai.Atom<Loadable<string>>;
|
||||
fileContent: jotai.Atom<Promise<string>>;
|
||||
fileContentSaved: jotai.PrimitiveAtom<string | null>;
|
||||
fileContent: jotai.WritableAtom<Promise<string>, [string], void>;
|
||||
newFileContent: jotai.PrimitiveAtom<string | null>;
|
||||
|
||||
openFileModal: jotai.PrimitiveAtom<boolean>;
|
||||
@@ -191,6 +144,8 @@ export class PreviewModel implements ViewModel {
|
||||
codeEditKeyDownHandler: (waveEvent: WaveKeyboardEvent) => boolean;
|
||||
|
||||
setPreviewFileName(fileName: string) {
|
||||
globalStore.set(this.fileContentSaved, null);
|
||||
globalStore.set(this.newFileContent, null);
|
||||
services.ObjectService.UpdateObjectMeta(`block:${this.blockId}`, { file: fileName });
|
||||
}
|
||||
|
||||
@@ -332,7 +287,8 @@ export class PreviewModel implements ViewModel {
|
||||
return null;
|
||||
}
|
||||
const mimeType = util.jotaiLoadableValue(get(this.fileMimeTypeLoadable), "");
|
||||
if (mimeType == "directory") {
|
||||
const metaPath = get(this.metaFilePath);
|
||||
if (mimeType == "directory" && metaPath == "/") {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
@@ -430,10 +386,25 @@ export class PreviewModel implements ViewModel {
|
||||
return file;
|
||||
});
|
||||
|
||||
const fileContentAtom = jotai.atom<Promise<string>>(async (get) => {
|
||||
const fullFile = await get(fullFileAtom);
|
||||
return util.base64ToString(fullFile?.data64);
|
||||
});
|
||||
this.fileContentSaved = jotai.atom(null) as jotai.PrimitiveAtom<string | null>;
|
||||
const fileContentAtom = jotai.atom(
|
||||
async (get) => {
|
||||
const _ = get(this.metaFilePath);
|
||||
const newContent = get(this.newFileContent);
|
||||
if (newContent != null) {
|
||||
return newContent;
|
||||
}
|
||||
const savedContent = get(this.fileContentSaved);
|
||||
if (savedContent != null) {
|
||||
return savedContent;
|
||||
}
|
||||
const fullFile = await get(fullFileAtom);
|
||||
return util.base64ToString(fullFile?.data64);
|
||||
},
|
||||
(get, set, update: string) => {
|
||||
set(this.fileContentSaved, update);
|
||||
}
|
||||
);
|
||||
|
||||
this.fullFile = fullFileAtom;
|
||||
this.fileContent = fileContentAtom;
|
||||
@@ -522,29 +493,24 @@ export class PreviewModel implements ViewModel {
|
||||
}
|
||||
|
||||
async goParentDirectory() {
|
||||
const blockMeta = globalStore.get(this.blockAtom)?.meta;
|
||||
const metaPath = globalStore.get(this.metaFilePath);
|
||||
const fileInfo = await globalStore.get(this.statFile);
|
||||
if (fileInfo == null) {
|
||||
return;
|
||||
this.updateOpenFileModalAndError(false);
|
||||
return true;
|
||||
}
|
||||
let newPath: string = null;
|
||||
if (!fileInfo.isdir) {
|
||||
newPath = fileInfo.dir;
|
||||
} else {
|
||||
const lastSlash = fileInfo.dir.lastIndexOf("/");
|
||||
newPath = fileInfo.dir.slice(0, lastSlash);
|
||||
if (newPath.indexOf("/") == -1) {
|
||||
return;
|
||||
}
|
||||
const conn = globalStore.get(this.connection);
|
||||
try {
|
||||
const newFileInfo = await RpcApi.RemoteFileJoinCommand(WindowRpcClient, [fileInfo.dir, ".."], {
|
||||
route: makeConnRoute(conn),
|
||||
});
|
||||
console.log(newFileInfo.path);
|
||||
this.updateOpenFileModalAndError(false);
|
||||
this.goHistory(newFileInfo.path);
|
||||
refocusNode(this.blockId);
|
||||
} catch (e) {
|
||||
globalStore.set(this.openFileError, e.message);
|
||||
console.error("Error opening file", [fileInfo.dir, ".."], e);
|
||||
}
|
||||
const updateMeta = historyutil.goHistory("file", metaPath, newPath, blockMeta);
|
||||
if (updateMeta == null) {
|
||||
return;
|
||||
}
|
||||
updateMeta.edit = false;
|
||||
const blockOref = WOS.makeORef("block", this.blockId);
|
||||
services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
|
||||
}
|
||||
|
||||
goHistoryBack() {
|
||||
@@ -590,6 +556,7 @@ export class PreviewModel implements ViewModel {
|
||||
const conn = globalStore.get(this.connection) ?? "";
|
||||
try {
|
||||
services.FileService.SaveFile(conn, filePath, util.stringToBase64(newFileContent));
|
||||
globalStore.set(this.fileContent, newFileContent);
|
||||
globalStore.set(this.newFileContent, null);
|
||||
console.log("saved file", filePath);
|
||||
} catch (error) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user