mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-31 23:11:28 +01:00
Set up electron-builder for new app (#113)
Adds electron-builder, which we will use to package and distribute our application, same as in the existing app. Replaces explicit port assignments with dynamic ones, which are then stored into environment variables. Adds a ~/.w2-dev folder for use when running a dev build. The build-helper pipeline from the old repo is included here too, but it is not updated yet so it will fail. Also removes some redundant utility functions and cleans up some let vs. const usage. The packaging can be run using the `package:prod` and `package:dev` tasks. --------- Co-authored-by: sawka <mike.sawka@gmail.com>
This commit is contained in:
parent
e4204b96d8
commit
8971e2feba
75
.github/workflows/build-helper.yml
vendored
Normal file
75
.github/workflows/build-helper.yml
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
name: "Build Helper"
|
||||||
|
on: workflow_dispatch
|
||||||
|
env:
|
||||||
|
GO_VERSION: "1.22.0"
|
||||||
|
NODE_VERSION: "21.5.0"
|
||||||
|
jobs:
|
||||||
|
runbuild:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- platform: "darwin"
|
||||||
|
arch: "universal"
|
||||||
|
runner: "macos-latest-xlarge"
|
||||||
|
task: "build-package"
|
||||||
|
- platform: "linux"
|
||||||
|
arch: "amd64"
|
||||||
|
runner: "ubuntu-latest"
|
||||||
|
scripthaus: "build-package-linux"
|
||||||
|
- platform: "linux"
|
||||||
|
arch: "arm64"
|
||||||
|
runner: "ubuntu-latest"
|
||||||
|
scripthaus: "build-package-linux"
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: scripthaus-dev/scripthaus
|
||||||
|
path: scripthaus
|
||||||
|
- name: Install Linux Build Dependencies (Linux only)
|
||||||
|
if: matrix.platform == 'linux'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --no-install-recommends -y libarchive-tools libopenjp2-tools rpm
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: ${{env.GO_VERSION}}
|
||||||
|
cache-dependency-path: |
|
||||||
|
wavesrv/go.sum
|
||||||
|
waveshell/go.sum
|
||||||
|
scripthaus/go.sum
|
||||||
|
- name: Install Scripthaus
|
||||||
|
run: |
|
||||||
|
go work use ./scripthaus;
|
||||||
|
cd scripthaus;
|
||||||
|
go get ./...;
|
||||||
|
CGO_ENABLED=1 go build -o scripthaus cmd/main.go
|
||||||
|
echo $PWD >> $GITHUB_PATH
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{env.NODE_VERSION}}
|
||||||
|
- name: Install yarn
|
||||||
|
run: |
|
||||||
|
corepack enable
|
||||||
|
yarn install
|
||||||
|
- name: Set Version
|
||||||
|
id: set-version
|
||||||
|
run: |
|
||||||
|
VERSION=$(node -e 'console.log(require("./version.js"))')
|
||||||
|
echo "WAVETERM_VERSION=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Build ${{ matrix.platform }}/${{ matrix.arch }}
|
||||||
|
run: scripthaus run ${{ matrix.scripthaus }}
|
||||||
|
env:
|
||||||
|
GOARCH: ${{ matrix.arch }}
|
||||||
|
CSC_LINK: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_CERTIFICATE}}
|
||||||
|
CSC_KEY_PASSWORD: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_CERTIFICATE_PWD }}
|
||||||
|
APPLE_ID: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
|
||||||
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_NOTARIZATION_PWD }}
|
||||||
|
APPLE_TEAM_ID: ${{ matrix.platform == 'darwin' && secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}
|
||||||
|
- name: Upload to S3 staging
|
||||||
|
run: aws s3 cp make/ s3://waveterm-github-artifacts/staging/${{ steps.set-version.outputs.WAVETERM_VERSION }}/ --recursive --exclude "*/*" --exclude "builder-*.yml"
|
||||||
|
env:
|
||||||
|
AWS_ACCESS_KEY_ID: "${{ secrets.S3_USERID }}"
|
||||||
|
AWS_SECRET_ACCESS_KEY: "${{ secrets.S3_SECRETKEY }}"
|
||||||
|
AWS_DEFAULT_REGION: us-west-2
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,6 +13,7 @@ bin/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*~
|
*~
|
||||||
out/
|
out/
|
||||||
|
make/
|
||||||
|
|
||||||
# Yarn Modern
|
# Yarn Modern
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
108
Taskfile.yml
108
Taskfile.yml
@ -10,6 +10,39 @@ vars:
|
|||||||
sh: node version.cjs
|
sh: node version.cjs
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
|
||||||
|
package:dev:
|
||||||
|
cmds:
|
||||||
|
- yarn build:dev && yarn electron-builder -c electron-builder.config.cjs -p never
|
||||||
|
deps:
|
||||||
|
- generate
|
||||||
|
- build:server
|
||||||
|
- build:wsh
|
||||||
|
|
||||||
|
package:prod:
|
||||||
|
cmds:
|
||||||
|
- yarn build:prod && yarn electron-builder -c electron-builder.config.cjs -p never
|
||||||
|
deps:
|
||||||
|
- generate
|
||||||
|
- build:server
|
||||||
|
- build:wsh
|
||||||
|
|
||||||
|
electron:dev:
|
||||||
|
cmds:
|
||||||
|
- WAVETERM_DEV=1 yarn dev
|
||||||
|
deps:
|
||||||
|
- generate
|
||||||
|
- build:server
|
||||||
|
- build:wsh
|
||||||
|
|
||||||
|
electron:start:
|
||||||
|
cmds:
|
||||||
|
- WAVETERM_DEV=1 yarn start
|
||||||
|
deps:
|
||||||
|
- generate
|
||||||
|
- build:server
|
||||||
|
- build:wsh
|
||||||
|
|
||||||
generate:
|
generate:
|
||||||
cmds:
|
cmds:
|
||||||
- go run cmd/generate/main-generate.go
|
- go run cmd/generate/main-generate.go
|
||||||
@ -20,41 +53,78 @@ tasks:
|
|||||||
- "pkg/wstore/*.go"
|
- "pkg/wstore/*.go"
|
||||||
- "pkg/wshrpc/**/*.go"
|
- "pkg/wshrpc/**/*.go"
|
||||||
|
|
||||||
electron:dev:
|
|
||||||
cmds:
|
|
||||||
- WAVETERM_DEV=1 yarn dev
|
|
||||||
deps:
|
|
||||||
- build:server
|
|
||||||
- build:wsh
|
|
||||||
|
|
||||||
electron:start:
|
|
||||||
cmds:
|
|
||||||
- WAVETERM_DEV=1 yarn start
|
|
||||||
deps:
|
|
||||||
- build:server
|
|
||||||
- build:wsh
|
|
||||||
|
|
||||||
build:server:
|
build:server:
|
||||||
|
deps:
|
||||||
|
- task: build:server:internal
|
||||||
|
vars:
|
||||||
|
GOARCH: arm64
|
||||||
|
- task: build:server:internal
|
||||||
|
vars:
|
||||||
|
GOARCH: amd64
|
||||||
|
|
||||||
|
build:server:internal:
|
||||||
|
requires:
|
||||||
|
vars:
|
||||||
|
- GOARCH
|
||||||
cmds:
|
cmds:
|
||||||
- go build -o dist/bin/wavesrv{{exeExt}} cmd/server/main-server.go
|
- CGO_ENABLED=1 GOARCH={{.GOARCH}} go build -tags "osusergo,netgo,sqlite_omit_load_extension" -ldflags "-X main.BuildTime=$(date +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wavesrv.{{.GOARCH}}{{exeExt}} cmd/server/main-server.go
|
||||||
sources:
|
sources:
|
||||||
- "cmd/server/*.go"
|
- "cmd/server/*.go"
|
||||||
- "pkg/**/*.go"
|
- "pkg/**/*.go"
|
||||||
generates:
|
generates:
|
||||||
- dist/bin/wavesrv{{exeExt}}
|
- dist/bin/wavesrv.{{.GOARCH}}{{exeExt}}
|
||||||
deps:
|
deps:
|
||||||
- go:mod:tidy
|
- go:mod:tidy
|
||||||
|
internal: true
|
||||||
|
|
||||||
build:wsh:
|
build:wsh:
|
||||||
cmds:
|
deps:
|
||||||
- go build -o dist/bin/wsh{{exeExt}} cmd/wsh/main-wsh.go
|
- task: build:wsh:internal
|
||||||
|
vars:
|
||||||
|
GOOS: darwin
|
||||||
|
GOARCH: arm64
|
||||||
|
- task: build:wsh:internal
|
||||||
|
vars:
|
||||||
|
GOOS: darwin
|
||||||
|
GOARCH: amd64
|
||||||
|
- task: build:wsh:internal
|
||||||
|
vars:
|
||||||
|
GOOS: linux
|
||||||
|
GOARCH: arm64
|
||||||
|
- task: build:wsh:internal
|
||||||
|
vars:
|
||||||
|
GOOS: linux
|
||||||
|
GOARCH: amd64
|
||||||
|
- task: build:wsh:internal
|
||||||
|
vars:
|
||||||
|
GOOS: windows
|
||||||
|
GOARCH: amd64
|
||||||
|
- task: build:wsh:internal
|
||||||
|
vars:
|
||||||
|
GOOS: windows
|
||||||
|
GOARCH: arm64
|
||||||
|
|
||||||
|
build:wsh:internal:
|
||||||
|
vars:
|
||||||
|
GO_LDFLAGS:
|
||||||
|
sh: echo "-s -w -X main.BuildTime=$(date +'%Y%m%d%H%M')"
|
||||||
|
EXT:
|
||||||
|
sh: echo {{if eq .GOOS "windows"}}.exe{{end}}
|
||||||
|
requires:
|
||||||
|
vars:
|
||||||
|
- GOOS
|
||||||
|
- GOARCH
|
||||||
|
- VERSION
|
||||||
sources:
|
sources:
|
||||||
- "cmd/wsh/**/*.go"
|
- "cmd/wsh/**/*.go"
|
||||||
- "pkg/**/*.go"
|
- "pkg/**/*.go"
|
||||||
generates:
|
generates:
|
||||||
- dist/bin/wsh{{exeExt}}
|
- dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.GOARCH}}{{.EXT}}
|
||||||
|
cmds:
|
||||||
|
- (CGO_ENABLED=0 GOOS={{.GOOS}} GOARCH={{.GOARCH}} go build -ldflags="{{.GO_LDFLAGS}}" -o dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.GOARCH}}{{.EXT}} cmd/wsh/main-wsh.go)
|
||||||
deps:
|
deps:
|
||||||
- go:mod:tidy
|
- go:mod:tidy
|
||||||
|
internal: true
|
||||||
|
|
||||||
go:mod:tidy:
|
go:mod:tidy:
|
||||||
summary: Runs `go mod tidy`
|
summary: Runs `go mod tidy`
|
||||||
|
@ -124,12 +124,17 @@ func main() {
|
|||||||
|
|
||||||
go stdinReadWatch()
|
go stdinReadWatch()
|
||||||
configWatcher()
|
configWatcher()
|
||||||
go web.RunWebSocketServer()
|
webListener, err := web.MakeTCPListener("web")
|
||||||
webListener, err := web.MakeTCPListener()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error creating web listener: %v\n", err)
|
log.Printf("error creating web listener: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
wsListener, err := web.MakeTCPListener("websocket")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error creating websocket listener: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go web.RunWebSocketServer(wsListener)
|
||||||
unixListener, err := web.MakeUnixListener()
|
unixListener, err := web.MakeUnixListener()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error creating unix listener: %v\n", err)
|
log.Printf("error creating unix listener: %v\n", err)
|
||||||
@ -141,7 +146,7 @@ func main() {
|
|||||||
_, err := strconv.Atoi(pidStr)
|
_, err := strconv.Atoi(pidStr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// use fmt instead of log here to make sure it goes directly to stderr
|
// use fmt instead of log here to make sure it goes directly to stderr
|
||||||
fmt.Fprintf(os.Stderr, "WAVESRV-ESTART\n")
|
fmt.Fprintf(os.Stderr, "WAVESRV-ESTART ws:%s web:%s\n", wsListener.Addr(), webListener.Addr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
79
electron-builder.config.cjs
Normal file
79
electron-builder.config.cjs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
const pkg = require("./package.json");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('electron-builder').Configuration}
|
||||||
|
* @see https://www.electron.build/configuration/configuration
|
||||||
|
*/
|
||||||
|
const config = {
|
||||||
|
appId: pkg.build.appId,
|
||||||
|
productName: pkg.productName,
|
||||||
|
artifactName: "${productName}-${platform}-${arch}-${version}.${ext}",
|
||||||
|
npmRebuild: false,
|
||||||
|
nodeGypRebuild: false,
|
||||||
|
electronCompile: false,
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
from: "./dist",
|
||||||
|
to: "./dist",
|
||||||
|
filter: ["**/*"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: ".",
|
||||||
|
to: ".",
|
||||||
|
filter: ["package.json"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
directories: {
|
||||||
|
output: "make",
|
||||||
|
},
|
||||||
|
asarUnpack: ["dist/bin/**/*"],
|
||||||
|
mac: {
|
||||||
|
target: [
|
||||||
|
{
|
||||||
|
target: "zip",
|
||||||
|
arch: "universal",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "dmg",
|
||||||
|
arch: "universal",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
icon: "build/icons.icns",
|
||||||
|
category: "public.app-category.developer-tools",
|
||||||
|
minimumSystemVersion: "10.15.0",
|
||||||
|
notarize: process.env.APPLE_TEAM_ID
|
||||||
|
? {
|
||||||
|
teamId: process.env.APPLE_TEAM_ID,
|
||||||
|
}
|
||||||
|
: false,
|
||||||
|
binaries: fs
|
||||||
|
.readdirSync("dist/bin", { recursive: true, withFileTypes: true })
|
||||||
|
.filter((f) => f.isFile() && (f.name.startsWith("wavesrv") || f.name.includes("darwin")))
|
||||||
|
.map((f) => path.resolve(f.path, f.name)),
|
||||||
|
},
|
||||||
|
linux: {
|
||||||
|
executableName: pkg.productName,
|
||||||
|
category: "TerminalEmulator",
|
||||||
|
icon: "build/icons.icns",
|
||||||
|
target: ["zip", "deb", "rpm", "AppImage", "pacman"],
|
||||||
|
synopsis: pkg.description,
|
||||||
|
description: null,
|
||||||
|
desktop: {
|
||||||
|
Name: pkg.productName,
|
||||||
|
Comment: pkg.description,
|
||||||
|
Keywords: "developer;terminal;emulator;",
|
||||||
|
category: "Development;Utility;",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
appImage: {
|
||||||
|
license: "LICENSE",
|
||||||
|
},
|
||||||
|
publish: {
|
||||||
|
provider: "generic",
|
||||||
|
url: "https://dl.waveterm.dev/releases",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
144
emain/emain.ts
144
emain/emain.ts
@ -8,32 +8,34 @@ import os from "os";
|
|||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import * as readline from "readline";
|
import * as readline from "readline";
|
||||||
import { debounce } from "throttle-debounce";
|
import { debounce } from "throttle-debounce";
|
||||||
import { getBackendHostPort } from "../frontend/app/store/global";
|
import * as util from "util";
|
||||||
|
import winston from "winston";
|
||||||
import * as services from "../frontend/app/store/services";
|
import * as services from "../frontend/app/store/services";
|
||||||
import * as keyutil from "../frontend/util/keyutil";
|
import * as keyutil from "../frontend/util/keyutil";
|
||||||
import { fireAndForget } from "../frontend/util/util";
|
import { fireAndForget } from "../frontend/util/util";
|
||||||
|
|
||||||
|
import { getServerWebEndpoint, WebServerEndpointVarName, WSServerEndpointVarName } from "@/util/endpoints";
|
||||||
|
import { WaveDevVarName, WaveDevViteVarName } from "@/util/isdev";
|
||||||
|
import { sprintf } from "sprintf-js";
|
||||||
const electronApp = electron.app;
|
const electronApp = electron.app;
|
||||||
const isDev = process.env.WAVETERM_DEV;
|
|
||||||
const isDevServer = !electronApp.isPackaged && process.env.ELECTRON_RENDERER_URL;
|
|
||||||
|
|
||||||
const WaveAppPathVarName = "WAVETERM_APP_PATH";
|
const WaveAppPathVarName = "WAVETERM_APP_PATH";
|
||||||
const WaveDevVarName = "WAVETERM_DEV";
|
|
||||||
const WaveSrvReadySignalPidVarName = "WAVETERM_READY_SIGNAL_PID";
|
const WaveSrvReadySignalPidVarName = "WAVETERM_READY_SIGNAL_PID";
|
||||||
const AuthKeyFile = "waveterm.authkey";
|
const AuthKeyFile = "waveterm.authkey";
|
||||||
const DevServerEndpoint = "http://127.0.0.1:8190";
|
|
||||||
const ProdServerEndpoint = "http://127.0.0.1:1719";
|
|
||||||
electron.nativeTheme.themeSource = "dark";
|
electron.nativeTheme.themeSource = "dark";
|
||||||
|
|
||||||
type WaveBrowserWindow = Electron.BrowserWindow & { waveWindowId: string; readyPromise: Promise<void> };
|
type WaveBrowserWindow = Electron.BrowserWindow & { waveWindowId: string; readyPromise: Promise<void> };
|
||||||
|
|
||||||
let waveSrvReadyResolve = (value: boolean) => {};
|
let waveSrvReadyResolve = (value: boolean) => {};
|
||||||
let waveSrvReady: Promise<boolean> = new Promise((resolve, _) => {
|
const waveSrvReady: Promise<boolean> = new Promise((resolve, _) => {
|
||||||
waveSrvReadyResolve = resolve;
|
waveSrvReadyResolve = resolve;
|
||||||
});
|
});
|
||||||
let globalIsQuitting = false;
|
let globalIsQuitting = false;
|
||||||
let globalIsStarting = true;
|
let globalIsStarting = true;
|
||||||
|
|
||||||
|
const isDev = !electron.app.isPackaged;
|
||||||
|
const isDevVite = isDev && process.env.ELECTRON_RENDERER_URL;
|
||||||
|
|
||||||
let waveSrvProc: child_process.ChildProcessWithoutNullStreams | null = null;
|
let waveSrvProc: child_process.ChildProcessWithoutNullStreams | null = null;
|
||||||
electronApp.setName(isDev ? "NextWave (Dev)" : "NextWave");
|
electronApp.setName(isDev ? "NextWave (Dev)" : "NextWave");
|
||||||
const unamePlatform = process.platform;
|
const unamePlatform = process.platform;
|
||||||
@ -43,16 +45,50 @@ if (unameArch == "x64") {
|
|||||||
}
|
}
|
||||||
keyutil.setKeyUtilPlatform(unamePlatform);
|
keyutil.setKeyUtilPlatform(unamePlatform);
|
||||||
|
|
||||||
function getBaseHostPort(): string {
|
|
||||||
if (isDev) {
|
|
||||||
return DevServerEndpoint;
|
|
||||||
}
|
|
||||||
return ProdServerEndpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
// must match golang
|
// must match golang
|
||||||
function getWaveHomeDir() {
|
function getWaveHomeDir() {
|
||||||
return path.join(os.homedir(), ".w2");
|
return path.join(os.homedir(), isDev ? ".w2-dev" : ".w2");
|
||||||
|
}
|
||||||
|
|
||||||
|
const waveHome = getWaveHomeDir();
|
||||||
|
|
||||||
|
const oldConsoleLog = console.log;
|
||||||
|
|
||||||
|
const loggerTransports: winston.transport[] = [
|
||||||
|
new winston.transports.File({ filename: path.join(waveHome, "waveterm-app.log"), level: "info" }),
|
||||||
|
];
|
||||||
|
if (isDev) {
|
||||||
|
loggerTransports.push(new winston.transports.Console());
|
||||||
|
}
|
||||||
|
const loggerConfig = {
|
||||||
|
level: "info",
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
||||||
|
winston.format.printf((info) => `${info.timestamp} ${info.message}`)
|
||||||
|
),
|
||||||
|
transports: loggerTransports,
|
||||||
|
};
|
||||||
|
const logger = winston.createLogger(loggerConfig);
|
||||||
|
function log(...msg: any[]) {
|
||||||
|
try {
|
||||||
|
logger.info(util.format(...msg));
|
||||||
|
} catch (e) {
|
||||||
|
oldConsoleLog(...msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log = log;
|
||||||
|
console.log(
|
||||||
|
sprintf(
|
||||||
|
"waveterm-app starting, WAVETERM_HOME=%s, electronpath=%s gopath=%s arch=%s/%s",
|
||||||
|
waveHome,
|
||||||
|
getElectronAppBasePath(),
|
||||||
|
getGoAppBasePath(),
|
||||||
|
unamePlatform,
|
||||||
|
unameArch
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (isDev) {
|
||||||
|
console.log("waveterm-app WAVETERM_DEV set");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getElectronAppBasePath(): string {
|
function getElectronAppBasePath(): string {
|
||||||
@ -60,20 +96,18 @@ function getElectronAppBasePath(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getGoAppBasePath(): string {
|
function getGoAppBasePath(): string {
|
||||||
const appDir = getElectronAppBasePath();
|
return getElectronAppBasePath().replace("app.asar", "app.asar.unpacked");
|
||||||
if (appDir.endsWith(".asar")) {
|
|
||||||
return `${appDir}.unpacked`;
|
|
||||||
} else {
|
|
||||||
return appDir;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const wavesrvBinName = `wavesrv.${unameArch}`;
|
||||||
|
|
||||||
function getWaveSrvPath(): string {
|
function getWaveSrvPath(): string {
|
||||||
return path.join(getGoAppBasePath(), "bin", "wavesrv");
|
return path.join(getGoAppBasePath(), "bin", wavesrvBinName);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWaveSrvPathWin(): string {
|
function getWaveSrvPathWin(): string {
|
||||||
const appPath = path.join(getGoAppBasePath(), "bin", "wavesrv.exe");
|
const winBinName = `${wavesrvBinName}.exe`;
|
||||||
|
const appPath = path.join(getGoAppBasePath(), "bin", winBinName);
|
||||||
return `& "${appPath}"`;
|
return `& "${appPath}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,11 +127,16 @@ function runWaveSrv(): Promise<boolean> {
|
|||||||
pResolve = argResolve;
|
pResolve = argResolve;
|
||||||
pReject = argReject;
|
pReject = argReject;
|
||||||
});
|
});
|
||||||
|
if (isDev) {
|
||||||
|
process.env[WaveDevVarName] = "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDevVite) {
|
||||||
|
process.env[WaveDevViteVarName] = "1";
|
||||||
|
}
|
||||||
|
|
||||||
const envCopy = { ...process.env };
|
const envCopy = { ...process.env };
|
||||||
envCopy[WaveAppPathVarName] = getGoAppBasePath();
|
envCopy[WaveAppPathVarName] = getGoAppBasePath();
|
||||||
if (isDev) {
|
|
||||||
envCopy[WaveDevVarName] = "1";
|
|
||||||
}
|
|
||||||
envCopy[WaveSrvReadySignalPidVarName] = process.pid.toString();
|
envCopy[WaveSrvReadySignalPidVarName] = process.pid.toString();
|
||||||
let waveSrvCmd: string;
|
let waveSrvCmd: string;
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
@ -139,6 +178,14 @@ function runWaveSrv(): Promise<boolean> {
|
|||||||
});
|
});
|
||||||
rlStderr.on("line", (line) => {
|
rlStderr.on("line", (line) => {
|
||||||
if (line.includes("WAVESRV-ESTART")) {
|
if (line.includes("WAVESRV-ESTART")) {
|
||||||
|
const addrs = /ws:([a-z0-9.:]+) web:([a-z0-9.:]+)/gm.exec(line);
|
||||||
|
if (addrs == null) {
|
||||||
|
console.log("error parsing WAVESRV-ESTART line", line);
|
||||||
|
electron.app.quit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
process.env[WSServerEndpointVarName] = addrs[1];
|
||||||
|
process.env[WebServerEndpointVarName] = addrs[2];
|
||||||
waveSrvReadyResolve(true);
|
waveSrvReadyResolve(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -159,12 +206,12 @@ function runWaveSrv(): Promise<boolean> {
|
|||||||
|
|
||||||
async function handleWSEvent(evtMsg: WSEventType) {
|
async function handleWSEvent(evtMsg: WSEventType) {
|
||||||
if (evtMsg.eventtype == "electron:newwindow") {
|
if (evtMsg.eventtype == "electron:newwindow") {
|
||||||
let windowId: string = evtMsg.data;
|
const windowId: string = evtMsg.data;
|
||||||
let windowData: WaveWindow = (await services.ObjectService.GetObject("window:" + windowId)) as WaveWindow;
|
const windowData: WaveWindow = (await services.ObjectService.GetObject("window:" + windowId)) as WaveWindow;
|
||||||
if (windowData == null) {
|
if (windowData == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let clientData = await services.ClientService.GetClientData();
|
const clientData = await services.ClientService.GetClientData();
|
||||||
const newWin = createBrowserWindow(clientData.oid, windowData);
|
const newWin = createBrowserWindow(clientData.oid, windowData);
|
||||||
await newWin.readyPromise;
|
await newWin.readyPromise;
|
||||||
newWin.show();
|
newWin.show();
|
||||||
@ -221,7 +268,7 @@ function shFrameNavHandler(event: Electron.Event<Electron.WebContentsWillFrameNa
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
event.frame.name == "pdfview" &&
|
event.frame.name == "pdfview" &&
|
||||||
(url.startsWith("blob:file:///") || url.startsWith(getBaseHostPort() + "/wave/stream-file?"))
|
(url.startsWith("blob:file:///") || url.startsWith(getServerWebEndpoint() + "/wave/stream-file?"))
|
||||||
) {
|
) {
|
||||||
// allowed
|
// allowed
|
||||||
return;
|
return;
|
||||||
@ -266,12 +313,11 @@ function createBrowserWindow(clientId: string, waveWindow: WaveWindow): WaveBrow
|
|||||||
readyResolve = resolve;
|
readyResolve = resolve;
|
||||||
});
|
});
|
||||||
const win: WaveBrowserWindow = bwin as WaveBrowserWindow;
|
const win: WaveBrowserWindow = bwin as WaveBrowserWindow;
|
||||||
// const indexHtml = isDev ? "index-dev.html" : "index.html";
|
const usp = new URLSearchParams();
|
||||||
let usp = new URLSearchParams();
|
|
||||||
usp.set("clientid", clientId);
|
usp.set("clientid", clientId);
|
||||||
usp.set("windowid", waveWindow.oid);
|
usp.set("windowid", waveWindow.oid);
|
||||||
const indexHtml = "index.html";
|
const indexHtml = "index.html";
|
||||||
if (isDevServer) {
|
if (isDevVite) {
|
||||||
console.log("running as dev server");
|
console.log("running as dev server");
|
||||||
win.loadURL(`${process.env.ELECTRON_RENDERER_URL}/index.html?${usp.toString()}`);
|
win.loadURL(`${process.env.ELECTRON_RENDERER_URL}/index.html?${usp.toString()}`);
|
||||||
} else {
|
} else {
|
||||||
@ -343,7 +389,7 @@ function isWindowFullyVisible(bounds: electron.Rectangle): boolean {
|
|||||||
|
|
||||||
// Helper function to check if a point is inside any display
|
// Helper function to check if a point is inside any display
|
||||||
function isPointInDisplay(x, y) {
|
function isPointInDisplay(x, y) {
|
||||||
for (let display of displays) {
|
for (const display of displays) {
|
||||||
const { x: dx, y: dy, width, height } = display.bounds;
|
const { x: dx, y: dy, width, height } = display.bounds;
|
||||||
if (x >= dx && x < dx + width && y >= dy && y < dy + height) {
|
if (x >= dx && x < dx + width && y >= dy && y < dy + height) {
|
||||||
return true;
|
return true;
|
||||||
@ -418,18 +464,9 @@ function ensureBoundsAreVisible(bounds: electron.Rectangle): electron.Rectangle
|
|||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
electron.ipcMain.on("isDev", (event) => {
|
|
||||||
event.returnValue = isDev;
|
|
||||||
});
|
|
||||||
|
|
||||||
electron.ipcMain.on("isDevServer", (event) => {
|
|
||||||
event.returnValue = isDevServer;
|
|
||||||
});
|
|
||||||
|
|
||||||
electron.ipcMain.on("getPlatform", (event, url) => {
|
electron.ipcMain.on("getPlatform", (event, url) => {
|
||||||
event.returnValue = unamePlatform;
|
event.returnValue = unamePlatform;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for the open-external event from the renderer process
|
// Listen for the open-external event from the renderer process
|
||||||
electron.ipcMain.on("open-external", (event, url) => {
|
electron.ipcMain.on("open-external", (event, url) => {
|
||||||
if (url && typeof url === "string") {
|
if (url && typeof url === "string") {
|
||||||
@ -443,8 +480,7 @@ electron.ipcMain.on("open-external", (event, url) => {
|
|||||||
|
|
||||||
electron.ipcMain.on("download", (event, payload) => {
|
electron.ipcMain.on("download", (event, payload) => {
|
||||||
const window = electron.BrowserWindow.fromWebContents(event.sender);
|
const window = electron.BrowserWindow.fromWebContents(event.sender);
|
||||||
const baseName = payload.filePath.split(/[\\/]/).pop();
|
const streamingUrl = getServerWebEndpoint() + "/wave/stream-file?path=" + encodeURIComponent(payload.filePath);
|
||||||
const streamingUrl = getBackendHostPort() + "/wave/stream-file?path=" + encodeURIComponent(payload.filePath);
|
|
||||||
window.webContents.downloadURL(streamingUrl);
|
window.webContents.downloadURL(streamingUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -459,8 +495,12 @@ electron.ipcMain.on("getCursorPoint", (event) => {
|
|||||||
event.returnValue = retVal;
|
event.returnValue = retVal;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("getEnv", (event, varName) => {
|
||||||
|
event.returnValue = process.env[varName] ?? null;
|
||||||
|
});
|
||||||
|
|
||||||
async function createNewWaveWindow() {
|
async function createNewWaveWindow() {
|
||||||
let clientData = await services.ClientService.GetClientData();
|
const clientData = await services.ClientService.GetClientData();
|
||||||
const newWindow = await services.ClientService.MakeWindow();
|
const newWindow = await services.ClientService.MakeWindow();
|
||||||
const newBrowserWindow = createBrowserWindow(clientData.oid, newWindow);
|
const newBrowserWindow = createBrowserWindow(clientData.oid, newWindow);
|
||||||
newBrowserWindow.show();
|
newBrowserWindow.show();
|
||||||
@ -498,7 +538,7 @@ function convertMenuDefArrToMenu(menuDefArr: ElectronContextMenuItem[]): electro
|
|||||||
}
|
}
|
||||||
|
|
||||||
function makeAppMenu() {
|
function makeAppMenu() {
|
||||||
let fileMenu: Electron.MenuItemConstructorOptions[] = [];
|
const fileMenu: Electron.MenuItemConstructorOptions[] = [];
|
||||||
fileMenu.push({
|
fileMenu.push({
|
||||||
label: "New Window",
|
label: "New Window",
|
||||||
accelerator: "CommandOrControl+N",
|
accelerator: "CommandOrControl+N",
|
||||||
@ -557,12 +597,12 @@ async function appMain() {
|
|||||||
const ready = await waveSrvReady;
|
const ready = await waveSrvReady;
|
||||||
console.log("wavesrv ready signal received", ready, Date.now() - startTs, "ms");
|
console.log("wavesrv ready signal received", ready, Date.now() - startTs, "ms");
|
||||||
console.log("get client data");
|
console.log("get client data");
|
||||||
let clientData = await services.ClientService.GetClientData();
|
const clientData = await services.ClientService.GetClientData();
|
||||||
console.log("client data ready");
|
console.log("client data ready");
|
||||||
await electronApp.whenReady();
|
await electronApp.whenReady();
|
||||||
let wins: WaveBrowserWindow[] = [];
|
const wins: WaveBrowserWindow[] = [];
|
||||||
for (let windowId of clientData.windowids.slice().reverse()) {
|
for (const windowId of clientData.windowids.slice().reverse()) {
|
||||||
let windowData: WaveWindow = (await services.ObjectService.GetObject("window:" + windowId)) as WaveWindow;
|
const windowData: WaveWindow = (await services.ObjectService.GetObject("window:" + windowId)) as WaveWindow;
|
||||||
if (windowData == null) {
|
if (windowData == null) {
|
||||||
services.WindowService.CloseWindow(windowId).catch((e) => {
|
services.WindowService.CloseWindow(windowId).catch((e) => {
|
||||||
/* ignore */
|
/* ignore */
|
||||||
@ -572,7 +612,7 @@ async function appMain() {
|
|||||||
const win = createBrowserWindow(clientData.oid, windowData);
|
const win = createBrowserWindow(clientData.oid, windowData);
|
||||||
wins.push(win);
|
wins.push(win);
|
||||||
}
|
}
|
||||||
for (let win of wins) {
|
for (const win of wins) {
|
||||||
await win.readyPromise;
|
await win.readyPromise;
|
||||||
console.log("show", win.waveWindowId);
|
console.log("show", win.waveWindowId);
|
||||||
win.show();
|
win.show();
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
let { contextBridge, ipcRenderer } = require("electron");
|
let { contextBridge, ipcRenderer } = require("electron");
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("api", {
|
contextBridge.exposeInMainWorld("api", {
|
||||||
isDev: () => ipcRenderer.sendSync("isDev"),
|
|
||||||
isDevServer: () => ipcRenderer.sendSync("isDevServer"),
|
|
||||||
getPlatform: () => ipcRenderer.sendSync("getPlatform"),
|
getPlatform: () => ipcRenderer.sendSync("getPlatform"),
|
||||||
getCursorPoint: () => ipcRenderer.sendSync("getCursorPoint"),
|
getCursorPoint: () => ipcRenderer.sendSync("getCursorPoint"),
|
||||||
openNewWindow: () => ipcRenderer.send("openNewWindow"),
|
openNewWindow: () => ipcRenderer.send("openNewWindow"),
|
||||||
@ -19,6 +17,7 @@ contextBridge.exposeInMainWorld("api", {
|
|||||||
console.error("Invalid URL passed to openExternal:", url);
|
console.error("Invalid URL passed to openExternal:", url);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getEnv: (varName) => ipcRenderer.sendSync("getEnv", varName),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Custom event for "new-window"
|
// Custom event for "new-window"
|
||||||
|
@ -6,6 +6,7 @@ import { getLayoutStateAtomForTab } from "@/faraday/lib/layoutAtom";
|
|||||||
import { layoutTreeStateReducer } from "@/faraday/lib/layoutState";
|
import { layoutTreeStateReducer } from "@/faraday/lib/layoutState";
|
||||||
|
|
||||||
import { handleIncomingRpcMessage } from "@/app/store/wshrpc";
|
import { handleIncomingRpcMessage } from "@/app/store/wshrpc";
|
||||||
|
import { getServerWebEndpoint, getServerWSEndpoint } from "@/util/endpoints";
|
||||||
import * as layoututil from "@/util/layoututil";
|
import * as layoututil from "@/util/layoututil";
|
||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
import * as jotai from "jotai";
|
import * as jotai from "jotai";
|
||||||
@ -193,15 +194,6 @@ function useBlockAtom<T>(blockId: string, name: string, makeFn: () => jotai.Atom
|
|||||||
return atom as jotai.Atom<T>;
|
return atom as jotai.Atom<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBackendHostPort(): string {
|
|
||||||
// TODO deal with dev/production
|
|
||||||
return "http://127.0.0.1:8190";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBackendWSHostPort(): string {
|
|
||||||
return "ws://127.0.0.1:8191";
|
|
||||||
}
|
|
||||||
|
|
||||||
let globalWS: WSControl = null;
|
let globalWS: WSControl = null;
|
||||||
|
|
||||||
function handleWSEventMessage(msg: WSEventType) {
|
function handleWSEventMessage(msg: WSEventType) {
|
||||||
@ -278,7 +270,7 @@ function handleWSMessage(msg: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initWS() {
|
function initWS() {
|
||||||
globalWS = new WSControl(getBackendWSHostPort(), globalStore, globalWindowId, "", (msg) => {
|
globalWS = new WSControl(getServerWSEndpoint(), globalStore, globalWindowId, "", (msg) => {
|
||||||
handleWSMessage(msg);
|
handleWSMessage(msg);
|
||||||
});
|
});
|
||||||
globalWS.connectNow("initWS");
|
globalWS.connectNow("initWS");
|
||||||
@ -332,7 +324,7 @@ async function fetchWaveFile(
|
|||||||
if (offset != null) {
|
if (offset != null) {
|
||||||
usp.set("offset", offset.toString());
|
usp.set("offset", offset.toString());
|
||||||
}
|
}
|
||||||
const resp = await fetch(getBackendHostPort() + "/wave/file?" + usp.toString());
|
const resp = await fetch(getServerWebEndpoint() + "/wave/file?" + usp.toString());
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
if (resp.status === 404) {
|
if (resp.status === 404) {
|
||||||
return { data: null, fileInfo: null };
|
return { data: null, fileInfo: null };
|
||||||
@ -375,13 +367,10 @@ function getObjectId(obj: any): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
PLATFORM,
|
|
||||||
WOS,
|
|
||||||
atoms,
|
atoms,
|
||||||
createBlock,
|
createBlock,
|
||||||
fetchWaveFile,
|
fetchWaveFile,
|
||||||
getApi,
|
getApi,
|
||||||
getBackendHostPort,
|
|
||||||
getEventORefSubject,
|
getEventORefSubject,
|
||||||
getEventSubject,
|
getEventSubject,
|
||||||
getFileSubject,
|
getFileSubject,
|
||||||
@ -389,10 +378,12 @@ export {
|
|||||||
globalStore,
|
globalStore,
|
||||||
globalWS,
|
globalWS,
|
||||||
initWS,
|
initWS,
|
||||||
|
PLATFORM,
|
||||||
sendWSCommand,
|
sendWSCommand,
|
||||||
setBlockFocus,
|
setBlockFocus,
|
||||||
setPlatform,
|
setPlatform,
|
||||||
useBlockAtom,
|
useBlockAtom,
|
||||||
useBlockCache,
|
useBlockCache,
|
||||||
useSettingsAtom,
|
useSettingsAtom,
|
||||||
|
WOS,
|
||||||
};
|
};
|
||||||
|
@ -4,10 +4,11 @@
|
|||||||
// WaveObjectStore
|
// WaveObjectStore
|
||||||
|
|
||||||
import { sendRpcCommand } from "@/app/store/wshrpc";
|
import { sendRpcCommand } from "@/app/store/wshrpc";
|
||||||
|
import { getServerWebEndpoint } from "@/util/endpoints";
|
||||||
import * as jotai from "jotai";
|
import * as jotai from "jotai";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { atoms, getBackendHostPort, globalStore } from "./global";
|
import { atoms, globalStore } from "./global";
|
||||||
import * as services from "./services";
|
import * as services from "./services";
|
||||||
|
|
||||||
const IsElectron = true;
|
const IsElectron = true;
|
||||||
@ -67,22 +68,23 @@ function callBackendService(service: string, method: string, args: any[], noUICo
|
|||||||
if (!noUIContext) {
|
if (!noUIContext) {
|
||||||
uiContext = globalStore.get(atoms.uiContext);
|
uiContext = globalStore.get(atoms.uiContext);
|
||||||
}
|
}
|
||||||
let waveCall: WebCallType = {
|
const waveCall: WebCallType = {
|
||||||
service: service,
|
service: service,
|
||||||
method: method,
|
method: method,
|
||||||
args: args,
|
args: args,
|
||||||
uicontext: uiContext,
|
uicontext: uiContext,
|
||||||
};
|
};
|
||||||
// usp is just for debugging (easier to filter URLs)
|
// usp is just for debugging (easier to filter URLs)
|
||||||
let methodName = service + "." + method;
|
const methodName = `${service}.${method}`;
|
||||||
let usp = new URLSearchParams();
|
const usp = new URLSearchParams();
|
||||||
usp.set("service", service);
|
usp.set("service", service);
|
||||||
usp.set("method", method);
|
usp.set("method", method);
|
||||||
let fetchPromise = fetch(getBackendHostPort() + "/wave/service?" + usp.toString(), {
|
const url = getServerWebEndpoint() + "/wave/service?" + usp.toString();
|
||||||
|
const fetchPromise = fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(waveCall),
|
body: JSON.stringify(waveCall),
|
||||||
});
|
});
|
||||||
let prtn = fetchPromise
|
const prtn = fetchPromise
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
throw new Error(`call ${methodName} failed: ${resp.status} ${resp.statusText}`);
|
throw new Error(`call ${methodName} failed: ${resp.status} ${resp.statusText}`);
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
import { WindowDrag } from "@/element/windowdrag";
|
import { WindowDrag } from "@/element/windowdrag";
|
||||||
import { deleteLayoutStateAtomForTab } from "@/faraday/lib/layoutAtom";
|
import { deleteLayoutStateAtomForTab } from "@/faraday/lib/layoutAtom";
|
||||||
import { debounce } from "@/faraday/lib/utils";
|
|
||||||
import { atoms } from "@/store/global";
|
import { atoms } from "@/store/global";
|
||||||
import * as services from "@/store/services";
|
import * as services from "@/store/services";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
@ -12,6 +11,7 @@ import React, { createRef, useCallback, useEffect, useRef, useState } from "reac
|
|||||||
|
|
||||||
import { Tab } from "./tab";
|
import { Tab } from "./tab";
|
||||||
|
|
||||||
|
import { debounce } from "throttle-debounce";
|
||||||
import "./tabbar.less";
|
import "./tabbar.less";
|
||||||
|
|
||||||
const TAB_DEFAULT_WIDTH = 130;
|
const TAB_DEFAULT_WIDTH = 130;
|
||||||
@ -111,7 +111,7 @@ const TabBar = React.memo(({ workspace }: TabBarProps) => {
|
|||||||
|
|
||||||
// const debouncedSetTabWidth = debounce((width) => setTabWidth(width), 100);
|
// const debouncedSetTabWidth = debounce((width) => setTabWidth(width), 100);
|
||||||
// const debouncedSetScrollable = debounce((scrollable) => setScrollable(scrollable), 100);
|
// const debouncedSetScrollable = debounce((scrollable) => setScrollable(scrollable), 100);
|
||||||
const debouncedUpdateTabPositions = debounce(() => updateTabPositions(), 100);
|
const debouncedUpdateTabPositions = debounce(100, () => updateTabPositions());
|
||||||
|
|
||||||
const handleResizeTabs = useCallback(() => {
|
const handleResizeTabs = useCallback(() => {
|
||||||
const tabBar = tabBarRef.current;
|
const tabBar = tabBarRef.current;
|
||||||
|
@ -3,9 +3,10 @@
|
|||||||
|
|
||||||
import { ContextMenuModel } from "@/app/store/contextmenu";
|
import { ContextMenuModel } from "@/app/store/contextmenu";
|
||||||
import { Markdown } from "@/element/markdown";
|
import { Markdown } from "@/element/markdown";
|
||||||
import { getBackendHostPort, globalStore, useBlockAtom } from "@/store/global";
|
import { globalStore, useBlockAtom } from "@/store/global";
|
||||||
import * as services from "@/store/services";
|
import * as services from "@/store/services";
|
||||||
import * as WOS from "@/store/wos";
|
import * as WOS from "@/store/wos";
|
||||||
|
import { getServerWebEndpoint } from "@/util/endpoints";
|
||||||
import * as util from "@/util/util";
|
import * as util from "@/util/util";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import * as jotai from "jotai";
|
import * as jotai from "jotai";
|
||||||
@ -274,7 +275,7 @@ function MarkdownPreview({ contentAtom }: { contentAtom: jotai.Atom<Promise<stri
|
|||||||
|
|
||||||
function StreamingPreview({ fileInfo }: { fileInfo: FileInfo }) {
|
function StreamingPreview({ fileInfo }: { fileInfo: FileInfo }) {
|
||||||
const filePath = fileInfo.path;
|
const filePath = fileInfo.path;
|
||||||
const streamingUrl = getBackendHostPort() + "/wave/stream-file?path=" + encodeURIComponent(filePath);
|
const streamingUrl = getServerWebEndpoint() + "/wave/stream-file?path=" + encodeURIComponent(filePath);
|
||||||
if (fileInfo.mimetype == "application/pdf") {
|
if (fileInfo.mimetype == "application/pdf") {
|
||||||
return (
|
return (
|
||||||
<div className="view-preview view-preview-pdf">
|
<div className="view-preview view-preview-pdf">
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import { WshServer } from "@/app/store/wshserver";
|
import { WshServer } from "@/app/store/wshserver";
|
||||||
import { createBlock, getBackendHostPort } from "@/store/global";
|
import { createBlock } from "@/store/global";
|
||||||
|
import { getServerWebEndpoint } from "@/util/endpoints";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import * as jotai from "jotai";
|
import * as jotai from "jotai";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
@ -116,7 +117,7 @@ function TermSticker({ sticker, config }: { sticker: StickerType; config: Sticke
|
|||||||
if (sticker.imgsrc == null) {
|
if (sticker.imgsrc == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const streamingUrl = getBackendHostPort() + "/wave/stream-file?path=" + encodeURIComponent(sticker.imgsrc);
|
const streamingUrl = getServerWebEndpoint() + "/wave/stream-file?path=" + encodeURIComponent(sticker.imgsrc);
|
||||||
return (
|
return (
|
||||||
<div className="term-sticker term-sticker-image" style={style} onClick={clickHandler}>
|
<div className="term-sticker term-sticker-image" style={style} onClick={clickHandler}>
|
||||||
<img src={streamingUrl} />
|
<img src={streamingUrl} />
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
// Copyright 2024, Command Line Inc.
|
// Copyright 2024, Command Line Inc.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
import { getCrypto } from "@/util/util";
|
||||||
import { DefaultNodeSize, LayoutNode } from "./model";
|
import { DefaultNodeSize, LayoutNode } from "./model";
|
||||||
import { FlexDirection, getCrypto, reverseFlexDirection } from "./utils";
|
import { FlexDirection, reverseFlexDirection } from "./utils";
|
||||||
|
|
||||||
const crypto = getCrypto();
|
const crypto = getCrypto();
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright 2024, Command Line Inc.
|
// Copyright 2024, Command Line Inc.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
import { lazy } from "@/util/util";
|
||||||
import {
|
import {
|
||||||
addChildAt,
|
addChildAt,
|
||||||
addIntermediateNode,
|
addIntermediateNode,
|
||||||
@ -25,7 +26,7 @@ import {
|
|||||||
LayoutTreeSwapNodeAction,
|
LayoutTreeSwapNodeAction,
|
||||||
MoveOperation,
|
MoveOperation,
|
||||||
} from "./model";
|
} from "./model";
|
||||||
import { DropDirection, FlexDirection, lazy } from "./utils";
|
import { DropDirection, FlexDirection } from "./utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a layout tree state.
|
* Initializes a layout tree state.
|
||||||
|
@ -97,43 +97,3 @@ export function setTransform({ top, left, width, height }: Dimensions, setSize:
|
|||||||
position: "absolute",
|
position: "absolute",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const debounce = <T extends (...args: any[]) => any>(callback: T, waitFor: number) => {
|
|
||||||
let timeout: NodeJS.Timeout;
|
|
||||||
return (...args: Parameters<T>): ReturnType<T> => {
|
|
||||||
let result: any;
|
|
||||||
clearTimeout(timeout);
|
|
||||||
timeout = setTimeout(() => {
|
|
||||||
result = callback(...args);
|
|
||||||
}, waitFor);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple wrapper function that lazily evaluates the provided function and caches its result for future calls.
|
|
||||||
* @param callback The function to lazily run.
|
|
||||||
* @returns The result of the function.
|
|
||||||
*/
|
|
||||||
export const lazy = <T extends (...args: any[]) => any>(callback: T) => {
|
|
||||||
let res: ReturnType<T>;
|
|
||||||
let processed = false;
|
|
||||||
return (...args: Parameters<T>): ReturnType<T> => {
|
|
||||||
if (processed) return res;
|
|
||||||
res = callback(...args);
|
|
||||||
processed = true;
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Workaround for NodeJS compatibility. Will attempt to resolve the Crypto API from the browser and fallback to NodeJS if it isn't present.
|
|
||||||
* @returns The Crypto API.
|
|
||||||
*/
|
|
||||||
export function getCrypto() {
|
|
||||||
try {
|
|
||||||
return window.crypto;
|
|
||||||
} catch {
|
|
||||||
return crypto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
15
frontend/types/custom.d.ts
vendored
15
frontend/types/custom.d.ts
vendored
@ -17,23 +17,10 @@ declare global {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type ElectronApi = {
|
type ElectronApi = {
|
||||||
/**
|
|
||||||
* Determines whether the current app instance is a development build.
|
|
||||||
* @returns True if the current app instance is a development build.
|
|
||||||
*/
|
|
||||||
isDev: () => boolean;
|
|
||||||
/**
|
|
||||||
* Determines whether the current app instance is hosted in a Vite dev server.
|
|
||||||
* @returns True if the current app instance is hosted in a Vite dev server.
|
|
||||||
*/
|
|
||||||
isDevServer: () => boolean;
|
|
||||||
/**
|
|
||||||
* Get a point value representing the cursor's position relative to the calling BrowserWindow
|
|
||||||
* @returns A point value.
|
|
||||||
*/
|
|
||||||
getCursorPoint: () => Electron.Point;
|
getCursorPoint: () => Electron.Point;
|
||||||
|
|
||||||
getPlatform: () => NodeJS.Platform;
|
getPlatform: () => NodeJS.Platform;
|
||||||
|
getEnv: (varName: string) => string;
|
||||||
|
|
||||||
showContextMenu: (menu: ElectronContextMenuItem[], position: { x: number; y: number }) => void;
|
showContextMenu: (menu: ElectronContextMenuItem[], position: { x: number; y: number }) => void;
|
||||||
onContextMenuClick: (callback: (id: string) => void) => void;
|
onContextMenuClick: (callback: (id: string) => void) => void;
|
||||||
|
72
frontend/types/gotypes.d.ts
vendored
72
frontend/types/gotypes.d.ts
vendored
@ -15,24 +15,6 @@ declare global {
|
|||||||
meta: MetaType;
|
meta: MetaType;
|
||||||
};
|
};
|
||||||
|
|
||||||
// wshutil.BlockAppendFileCommand
|
|
||||||
type BlockAppendFileCommand = {
|
|
||||||
command: "blockfile:append";
|
|
||||||
filename: string;
|
|
||||||
data: number[];
|
|
||||||
};
|
|
||||||
|
|
||||||
// wshutil.BlockAppendIJsonCommand
|
|
||||||
type BlockAppendIJsonCommand = {
|
|
||||||
command: "blockfile:appendijson";
|
|
||||||
filename: string;
|
|
||||||
data: MetaType;
|
|
||||||
};
|
|
||||||
|
|
||||||
type BlockCommand = {
|
|
||||||
command: string;
|
|
||||||
} & ( BlockAppendFileCommand | BlockAppendIJsonCommand | BlockInputCommand | BlockRestartCommand | CreateBlockCommand | BlockGetMetaCommand | BlockMessageCommand | ResolveIdsCommand | BlockSetMetaCommand | BlockSetViewCommand );
|
|
||||||
|
|
||||||
// blockcontroller.BlockControllerRuntimeStatus
|
// blockcontroller.BlockControllerRuntimeStatus
|
||||||
type BlockControllerRuntimeStatus = {
|
type BlockControllerRuntimeStatus = {
|
||||||
blockid: string;
|
blockid: string;
|
||||||
@ -48,26 +30,11 @@ declare global {
|
|||||||
meta?: MetaType;
|
meta?: MetaType;
|
||||||
};
|
};
|
||||||
|
|
||||||
// wshutil.BlockGetMetaCommand
|
|
||||||
type BlockGetMetaCommand = {
|
|
||||||
command: "getmeta";
|
|
||||||
oref: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// wconfig.BlockHeaderOpts
|
// wconfig.BlockHeaderOpts
|
||||||
type BlockHeaderOpts = {
|
type BlockHeaderOpts = {
|
||||||
showblockids: boolean;
|
showblockids: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
// wshutil.BlockInputCommand
|
|
||||||
type BlockInputCommand = {
|
|
||||||
blockid: string;
|
|
||||||
command: "controller:input";
|
|
||||||
inputdata64?: string;
|
|
||||||
signame?: string;
|
|
||||||
termsize?: TermSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
// webcmd.BlockInputWSCommand
|
// webcmd.BlockInputWSCommand
|
||||||
type BlockInputWSCommand = {
|
type BlockInputWSCommand = {
|
||||||
wscommand: "blockinput";
|
wscommand: "blockinput";
|
||||||
@ -75,31 +42,6 @@ declare global {
|
|||||||
inputdata64: string;
|
inputdata64: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// wshutil.BlockMessageCommand
|
|
||||||
type BlockMessageCommand = {
|
|
||||||
command: "message";
|
|
||||||
message: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// wshutil.BlockRestartCommand
|
|
||||||
type BlockRestartCommand = {
|
|
||||||
command: "controller:restart";
|
|
||||||
blockid: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// wshutil.BlockSetMetaCommand
|
|
||||||
type BlockSetMetaCommand = {
|
|
||||||
command: "setmeta";
|
|
||||||
oref?: string;
|
|
||||||
meta: MetaType;
|
|
||||||
};
|
|
||||||
|
|
||||||
// wshutil.BlockSetViewCommand
|
|
||||||
type BlockSetViewCommand = {
|
|
||||||
command: "setview";
|
|
||||||
view: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// wstore.Client
|
// wstore.Client
|
||||||
type Client = WaveObj & {
|
type Client = WaveObj & {
|
||||||
mainwindowid: string;
|
mainwindowid: string;
|
||||||
@ -174,14 +116,6 @@ declare global {
|
|||||||
meta: MetaType;
|
meta: MetaType;
|
||||||
};
|
};
|
||||||
|
|
||||||
// wshutil.CreateBlockCommand
|
|
||||||
type CreateBlockCommand = {
|
|
||||||
command: "createblock";
|
|
||||||
tabid: string;
|
|
||||||
blockdef: BlockDef;
|
|
||||||
rtopts?: RuntimeOpts;
|
|
||||||
};
|
|
||||||
|
|
||||||
// wstore.FileDef
|
// wstore.FileDef
|
||||||
type FileDef = {
|
type FileDef = {
|
||||||
filetype?: string;
|
filetype?: string;
|
||||||
@ -248,12 +182,6 @@ declare global {
|
|||||||
y: number;
|
y: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// wshutil.ResolveIdsCommand
|
|
||||||
type ResolveIdsCommand = {
|
|
||||||
command: "resolveids";
|
|
||||||
ids: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
// wshutil.RpcMessage
|
// wshutil.RpcMessage
|
||||||
type RpcMessage = {
|
type RpcMessage = {
|
||||||
command?: string;
|
command?: string;
|
||||||
|
9
frontend/util/endpoints.ts
Normal file
9
frontend/util/endpoints.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { getEnv } from "./getenv";
|
||||||
|
import { lazy } from "./util";
|
||||||
|
|
||||||
|
export const WebServerEndpointVarName = "WAVE_SERVER_WEB_ENDPOINT";
|
||||||
|
export const WSServerEndpointVarName = "WAVE_SERVER_WS_ENDPOINT";
|
||||||
|
|
||||||
|
export const getServerWebEndpoint = lazy(() => `http://${getEnv(WebServerEndpointVarName)}`);
|
||||||
|
|
||||||
|
export const getServerWSEndpoint = lazy(() => `ws://${getEnv(WSServerEndpointVarName)}`);
|
26
frontend/util/getenv.ts
Normal file
26
frontend/util/getenv.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { getApi } from "@/app/store/global";
|
||||||
|
|
||||||
|
function getWindow(): Window {
|
||||||
|
return globalThis.window;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProcess(): NodeJS.Process {
|
||||||
|
return globalThis.process;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an environment variable from the host process, either directly or via IPC if called from the browser.
|
||||||
|
* @param paramName The name of the environment variable to attempt to retrieve.
|
||||||
|
* @returns The value of the environment variable or null if not present.
|
||||||
|
*/
|
||||||
|
export function getEnv(paramName: string): string {
|
||||||
|
const win = getWindow();
|
||||||
|
if (win != null) {
|
||||||
|
return getApi().getEnv(paramName);
|
||||||
|
}
|
||||||
|
const proc = getProcess();
|
||||||
|
if (proc != null) {
|
||||||
|
return proc.env[paramName];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
17
frontend/util/isdev.ts
Normal file
17
frontend/util/isdev.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { getEnv } from "./getenv";
|
||||||
|
import { lazy } from "./util";
|
||||||
|
|
||||||
|
export const WaveDevVarName = "WAVETERM_DEV";
|
||||||
|
export const WaveDevViteVarName = "WAVETERM_DEV_VITE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the current app instance is a development build.
|
||||||
|
* @returns True if the current app instance is a development build.
|
||||||
|
*/
|
||||||
|
export const isDev = lazy(() => !!getEnv(WaveDevVarName));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the current app instance is running via the Vite dev server.
|
||||||
|
* @returns True if the app is running via the Vite dev server.
|
||||||
|
*/
|
||||||
|
export const isDevVite = lazy(() => !!getEnv(WaveDevViteVarName));
|
@ -162,15 +162,45 @@ function useAtomValueSafe<T>(atom: jotai.Atom<T>): T {
|
|||||||
return jotai.useAtomValue(atom);
|
return jotai.useAtomValue(atom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple wrapper function that lazily evaluates the provided function and caches its result for future calls.
|
||||||
|
* @param callback The function to lazily run.
|
||||||
|
* @returns The result of the function.
|
||||||
|
*/
|
||||||
|
const lazy = <T extends (...args: any[]) => any>(callback: T) => {
|
||||||
|
let res: ReturnType<T>;
|
||||||
|
let processed = false;
|
||||||
|
return (...args: Parameters<T>): ReturnType<T> => {
|
||||||
|
if (processed) return res;
|
||||||
|
res = callback(...args);
|
||||||
|
processed = true;
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Workaround for NodeJS compatibility. Will attempt to resolve the Crypto API from the browser and fallback to NodeJS if it isn't present.
|
||||||
|
* @returns The Crypto API.
|
||||||
|
*/
|
||||||
|
function getCrypto() {
|
||||||
|
try {
|
||||||
|
return window.crypto;
|
||||||
|
} catch {
|
||||||
|
return crypto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
base64ToArray,
|
base64ToArray,
|
||||||
base64ToString,
|
base64ToString,
|
||||||
fireAndForget,
|
fireAndForget,
|
||||||
|
getCrypto,
|
||||||
getPromiseState,
|
getPromiseState,
|
||||||
getPromiseValue,
|
getPromiseValue,
|
||||||
isBlank,
|
isBlank,
|
||||||
jotaiLoadableValue,
|
jotaiLoadableValue,
|
||||||
jsonDeepEqual,
|
jsonDeepEqual,
|
||||||
|
lazy,
|
||||||
makeIconClass,
|
makeIconClass,
|
||||||
stringToBase64,
|
stringToBase64,
|
||||||
useAtomValueSafe,
|
useAtomValueSafe,
|
||||||
|
@ -12,13 +12,13 @@ import { App } from "./app/app";
|
|||||||
import { loadFonts } from "./util/fontutil";
|
import { loadFonts } from "./util/fontutil";
|
||||||
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
let windowId = urlParams.get("windowid");
|
const windowId = urlParams.get("windowid");
|
||||||
let clientId = urlParams.get("clientid");
|
const clientId = urlParams.get("clientid");
|
||||||
|
|
||||||
console.log("Wave Starting");
|
console.log("Wave Starting");
|
||||||
console.log("clientid", clientId, "windowid", windowId);
|
console.log("clientid", clientId, "windowid", windowId);
|
||||||
|
|
||||||
let platform = getApi().getPlatform();
|
const platform = getApi().getPlatform();
|
||||||
setPlatform(platform);
|
setPlatform(platform);
|
||||||
keyutil.setKeyUtilPlatform(platform);
|
keyutil.setKeyUtilPlatform(platform);
|
||||||
|
|
||||||
|
24
package.json
24
package.json
@ -1,7 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "thenextwave",
|
"name": "thenextwave",
|
||||||
"private": true,
|
"author": {
|
||||||
|
"name": "Command Line Inc",
|
||||||
|
"email": "info@commandline.dev"
|
||||||
|
},
|
||||||
|
"productName": "TheNextWave",
|
||||||
|
"description": "An Open-Source, AI-Native, Terminal Built for Seamless Workflows",
|
||||||
|
"license": "Apache-2.0",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
|
"homepage": "https://waveterm.dev",
|
||||||
|
"build": {
|
||||||
|
"appId": "dev.commandline.thenextwave"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
"main": "./dist/main/index.js",
|
"main": "./dist/main/index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -12,7 +23,8 @@
|
|||||||
"storybook": "storybook dev -p 6006 --no-open",
|
"storybook": "storybook dev -p 6006 --no-open",
|
||||||
"build-storybook": "storybook build",
|
"build-storybook": "storybook build",
|
||||||
"coverage": "vitest run --coverage",
|
"coverage": "vitest run --coverage",
|
||||||
"test": "vitest"
|
"test": "vitest",
|
||||||
|
"postinstall": "electron-builder install-app-deps"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@chromatic-com/storybook": "^1.5.0",
|
"@chromatic-com/storybook": "^1.5.0",
|
||||||
@ -27,11 +39,14 @@
|
|||||||
"@types/node": "^20.12.12",
|
"@types/node": "^20.12.12",
|
||||||
"@types/papaparse": "^5",
|
"@types/papaparse": "^5",
|
||||||
"@types/react": "^18.3.2",
|
"@types/react": "^18.3.2",
|
||||||
|
"@types/sprintf-js": "^1",
|
||||||
"@types/throttle-debounce": "^5",
|
"@types/throttle-debounce": "^5",
|
||||||
"@types/tinycolor2": "^1",
|
"@types/tinycolor2": "^1",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
"@vitejs/plugin-react": "^4.3.0",
|
"@vitejs/plugin-react": "^4.3.0",
|
||||||
"@vitest/coverage-istanbul": "^1.6.0",
|
"@vitest/coverage-istanbul": "^1.6.0",
|
||||||
|
"electron": "^31.1.0",
|
||||||
|
"electron-builder": "^24.13.3",
|
||||||
"electron-vite": "^2.2.0",
|
"electron-vite": "^2.2.0",
|
||||||
"eslint": "^9.2.0",
|
"eslint": "^9.2.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
@ -65,7 +80,6 @@
|
|||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.11",
|
"dayjs": "^1.11.11",
|
||||||
"electron": "^31.1.0",
|
|
||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"jotai": "^2.8.0",
|
"jotai": "^2.8.0",
|
||||||
@ -83,10 +97,12 @@
|
|||||||
"rehype-raw": "^7.0.0",
|
"rehype-raw": "^7.0.0",
|
||||||
"remark-gfm": "^4.0.0",
|
"remark-gfm": "^4.0.0",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
|
"sprintf-js": "^1.1.3",
|
||||||
"throttle-debounce": "^5.0.0",
|
"throttle-debounce": "^5.0.0",
|
||||||
"tinycolor2": "^1.6.0",
|
"tinycolor2": "^1.6.0",
|
||||||
"use-device-pixel-ratio": "^1.1.2",
|
"use-device-pixel-ratio": "^1.1.2",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1",
|
||||||
|
"winston": "^3.13.1"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@4.3.1"
|
"packageManager": "yarn@4.3.1"
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
|
|
||||||
const WaveVersion = "v0.1.0"
|
const WaveVersion = "v0.1.0"
|
||||||
const DefaultWaveHome = "~/.w2"
|
const DefaultWaveHome = "~/.w2"
|
||||||
|
const DevWaveHome = "~/.w2-dev"
|
||||||
const WaveHomeVarName = "WAVETERM_HOME"
|
const WaveHomeVarName = "WAVETERM_HOME"
|
||||||
const WaveDevVarName = "WAVETERM_DEV"
|
const WaveDevVarName = "WAVETERM_DEV"
|
||||||
const WaveLockFile = "waveterm.lock"
|
const WaveLockFile = "waveterm.lock"
|
||||||
@ -74,6 +75,9 @@ func GetWaveHomeDir() string {
|
|||||||
if homeVar != "" {
|
if homeVar != "" {
|
||||||
return ExpandHomeDir(homeVar)
|
return ExpandHomeDir(homeVar)
|
||||||
}
|
}
|
||||||
|
if IsDevMode() {
|
||||||
|
return ExpandHomeDir(DevWaveHome)
|
||||||
|
}
|
||||||
return ExpandHomeDir(DefaultWaveHome)
|
return ExpandHomeDir(DefaultWaveHome)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,10 +47,6 @@ const HttpWriteTimeout = 21 * time.Second
|
|||||||
const HttpMaxHeaderBytes = 60000
|
const HttpMaxHeaderBytes = 60000
|
||||||
const HttpTimeoutDuration = 21 * time.Second
|
const HttpTimeoutDuration = 21 * time.Second
|
||||||
|
|
||||||
const MainServerAddr = "127.0.0.1:1719" // wavesrv, P=16+1, S=19, PS=1719
|
|
||||||
const WebSocketServerAddr = "127.0.0.1:1723" // wavesrv:websocket, P=16+1, W=23, PW=1723
|
|
||||||
const MainServerDevAddr = "127.0.0.1:8190"
|
|
||||||
const WebSocketServerDevAddr = "127.0.0.1:8191"
|
|
||||||
const WSStateReconnectTime = 30 * time.Second
|
const WSStateReconnectTime = 30 * time.Second
|
||||||
const WSStatePacketChSize = 20
|
const WSStatePacketChSize = 20
|
||||||
|
|
||||||
@ -213,16 +209,13 @@ func WebFnWrap(opts WebFnOpts, fn WebFnType) WebFnType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeTCPListener() (net.Listener, error) {
|
func MakeTCPListener(serviceName string) (net.Listener, error) {
|
||||||
serverAddr := MainServerAddr
|
serverAddr := "127.0.0.1:"
|
||||||
if wavebase.IsDevMode() {
|
|
||||||
serverAddr = MainServerDevAddr
|
|
||||||
}
|
|
||||||
rtn, err := net.Listen("tcp", serverAddr)
|
rtn, err := net.Listen("tcp", serverAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating listener at %v: %v", serverAddr, err)
|
return nil, fmt.Errorf("error creating listener at %v: %v", serverAddr, err)
|
||||||
}
|
}
|
||||||
log.Printf("Server listening on %s\n", serverAddr)
|
log.Printf("Server [%s] listening on %s\n", serviceName, rtn.Addr())
|
||||||
return rtn, nil
|
return rtn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,7 +227,7 @@ func MakeUnixListener() (net.Listener, error) {
|
|||||||
return nil, fmt.Errorf("error creating listener at %v: %v", serverAddr, err)
|
return nil, fmt.Errorf("error creating listener at %v: %v", serverAddr, err)
|
||||||
}
|
}
|
||||||
os.Chmod(serverAddr, 0700)
|
os.Chmod(serverAddr, 0700)
|
||||||
log.Printf("Server listening on %s\n", serverAddr)
|
log.Printf("Server [unix-domain] listening on %s\n", serverAddr)
|
||||||
return rtn, nil
|
return rtn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,15 +237,15 @@ func RunWebServer(listener net.Listener) {
|
|||||||
gr.HandleFunc("/wave/stream-file", WebFnWrap(WebFnOpts{AllowCaching: true}, handleStreamFile))
|
gr.HandleFunc("/wave/stream-file", WebFnWrap(WebFnOpts{AllowCaching: true}, handleStreamFile))
|
||||||
gr.HandleFunc("/wave/file", WebFnWrap(WebFnOpts{AllowCaching: false}, handleWaveFile))
|
gr.HandleFunc("/wave/file", WebFnWrap(WebFnOpts{AllowCaching: false}, handleWaveFile))
|
||||||
gr.HandleFunc("/wave/service", WebFnWrap(WebFnOpts{JsonErrors: true}, handleService))
|
gr.HandleFunc("/wave/service", WebFnWrap(WebFnOpts{JsonErrors: true}, handleService))
|
||||||
var allowedOrigins handlers.CORSOption
|
handler := http.TimeoutHandler(gr, HttpTimeoutDuration, "Timeout")
|
||||||
if wavebase.IsDevMode() {
|
if wavebase.IsDevMode() {
|
||||||
allowedOrigins = handlers.AllowedOrigins([]string{"*"})
|
handler = handlers.CORS(handlers.AllowedOrigins([]string{"*"}))(handler)
|
||||||
}
|
}
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
ReadTimeout: HttpReadTimeout,
|
ReadTimeout: HttpReadTimeout,
|
||||||
WriteTimeout: HttpWriteTimeout,
|
WriteTimeout: HttpWriteTimeout,
|
||||||
MaxHeaderBytes: HttpMaxHeaderBytes,
|
MaxHeaderBytes: HttpMaxHeaderBytes,
|
||||||
Handler: handlers.CORS(allowedOrigins)(http.TimeoutHandler(gr, HttpTimeoutDuration, "Timeout")),
|
Handler: handler,
|
||||||
}
|
}
|
||||||
err := server.Serve(listener)
|
err := server.Serve(listener)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sync"
|
"sync"
|
||||||
@ -31,20 +32,18 @@ const wsInitialPingTime = 1 * time.Second
|
|||||||
|
|
||||||
const DefaultCommandTimeout = 2 * time.Second
|
const DefaultCommandTimeout = 2 * time.Second
|
||||||
|
|
||||||
func RunWebSocketServer() {
|
func RunWebSocketServer(listener net.Listener) {
|
||||||
gr := mux.NewRouter()
|
gr := mux.NewRouter()
|
||||||
gr.HandleFunc("/ws", HandleWs)
|
gr.HandleFunc("/ws", HandleWs)
|
||||||
serverAddr := WebSocketServerDevAddr
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: serverAddr,
|
|
||||||
ReadTimeout: HttpReadTimeout,
|
ReadTimeout: HttpReadTimeout,
|
||||||
WriteTimeout: HttpWriteTimeout,
|
WriteTimeout: HttpWriteTimeout,
|
||||||
MaxHeaderBytes: HttpMaxHeaderBytes,
|
MaxHeaderBytes: HttpMaxHeaderBytes,
|
||||||
Handler: gr,
|
Handler: gr,
|
||||||
}
|
}
|
||||||
server.SetKeepAlivesEnabled(false)
|
server.SetKeepAlivesEnabled(false)
|
||||||
log.Printf("Running websocket server on %s\n", serverAddr)
|
log.Printf("Running websocket server on %s\n", listener.Addr())
|
||||||
err := server.ListenAndServe()
|
err := server.Serve(listener)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[error] trying to run websocket server: %v\n", err)
|
log.Printf("[error] trying to run websocket server: %v\n", err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user