Compare commits

...

6 Commits

Author SHA1 Message Date
Evan Simkowitz 81d4b9d3af save work 2024-03-12 14:35:25 -07:00
Evan Simkowitz fecdc5abdc Fix another acknowledgements readme typo (#431) 2024-03-11 17:46:43 -07:00
Evan Simkowitz 769f3fc43c Fix acknowledgements readme typo (#430) 2024-03-11 17:45:43 -07:00
Evan Simkowitz c73ac66ed7 Fix broken license disclaimers (#429) 2024-03-11 17:43:45 -07:00
Cole Lashley 5258a67a78 Expose config path to frontend (#404)
* added config api path

* addressed feedback

* initial change for http file server

* removed old handle config func

* added user keybind config path

* fixed logs
2024-03-11 15:07:29 -07:00
Mike Sawka 77ea45392a quick v0.7.0 fixes (#428)
* update mask color for tabswitcher to work on lightmode

* fix background color on code blocks in markdown in light mode

* update wording on disconnected modal (restart wave backend instead of restart server)

* fix tooltip (should be Ctrl-Space)

* cleanup line-height/padding for code blocks
2024-03-11 12:03:30 -07:00
15 changed files with 2399 additions and 100 deletions
+23
View File
@@ -0,0 +1,23 @@
name: Publish a new release
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Get the version
id: get_version
run: |
VERSION=$(node -e 'console.log(require("./version.js"))')
echo "WAVETERM_VERSION=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Copy staged artifacts to the release bucket
run: |
. ./buildres/publish-from-staging.sh
env:
AWS_ACCESS_KEY_ID: "${{ secrets.S3_USERID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.S3_SECRETKEY }}"
AWS_DEFAULT_REGION: us-west-2
- name: Publish to Ubuntu PPA
+2 -2
View File
@@ -11,9 +11,9 @@ The license disclaimers for the backend are generated using the [go-licenses](ht
The license disclaimers for the frontend are generated using the [`yarn licenses` tool](https://classic.yarnpkg.com/lang/en/docs/cli/licenses/). This outputs to the file [`frontend.md`](./disclaimers/frontend.md).
These three disclaimer files linked above will be periodically regenerated to reflect new dependencies.
These disclaimer files linked above will be periodically regenerated to reflect new dependencies.
The [`scripthaus.md` file](../scripthaus.md) contains scripts to genrate the disclaimers and package them. To manually generate the disclaimers, run the following from the repository root directory:
The [`scripthaus.md` file](../scripthaus.md) contains scripts to generate the disclaimers and package them. To manually generate the disclaimers, run the following from the repository root directory:
```bash
scripthaus run generate-license-disclaimers
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -60,6 +60,7 @@
--app-panel-bg-color-dev: rgb(21, 23, 48);
--app-icon-color: rgb(139, 145, 138);
--app-icon-hover-color: #fff;
--app-selected-mask-color: rgba(255, 255, 255, 0.06);
/* icon colors */
+3
View File
@@ -17,6 +17,7 @@
--app-panel-bg-color-dev: #e0e0e0;
--app-icon-color: rgb(80, 80, 80);
--app-icon-hover-color: rgb(100, 100, 100);
--app-selected-mask-color: rgba(0, 0, 0, 0.06);
--input-bg-color: #eeeeee;
@@ -50,6 +51,8 @@
--form-element-disabled-text-color: #b7b7b7;
--form-element-placeholder-color: #b7b7b7;
--markdown-bg-color: rgb(0, 0, 0, 0.1);
/* modal colors */
--modal-header-bottom-border-color: rgba(0, 0, 0, 0.3);
+16 -14
View File
@@ -8,19 +8,6 @@
margin-bottom: 10px;
}
code {
background-color: var(--markdown-bg-color);
color: var(--app-text-color);
font-family: var(--termfontfamily);
border-radius: 4px;
}
code.inline {
padding-top: 0;
padding-bottom: 0;
font-family: var(--termfontfamily);
}
.title {
color: var(--app-text-color);
margin-top: 16px;
@@ -62,14 +49,29 @@
pre {
background-color: var(--markdown-bg-color);
margin: 4px 10px 4px 10px;
padding: 6px 6px 6px 10px;
padding: 0.7em;
border-radius: 4px;
code {
background-color: transparent;
padding: 0;
line-height: normal;
}
}
pre.selected {
outline: 2px solid var(--markdown-outline-color);
}
code {
color: var(--app-text-color);
font-family: var(--termfontfamily);
border-radius: 4px;
background-color: var(--markdown-bg-color);
padding: 0.15em 0.5em;
line-height: 1.5;
}
.title {
font-weight: semibold;
padding-top: 6px;
+1 -1
View File
@@ -24,7 +24,7 @@ function HeaderRenderer(props: any, hnum: number): any {
}
function CodeRenderer(props: any): any {
return <code className={cn({ inline: props.inline })}>{props.children}</code>;
return <code>{props.children}</code>;
}
@mobxReact.observer
+1 -1
View File
@@ -90,7 +90,7 @@ class DisconnectedModal extends React.Component<{}, {}> {
onClick={this.restartServer}
leftIcon={<i className="fa-sharp fa-solid fa-triangle-exclamation"></i>}
>
Restart Server
Restart Wave Backend
</Button>
</div>
</Modal>
+1 -1
View File
@@ -74,7 +74,7 @@
.focused-option {
border: 1px solid rgba(241, 246, 243, 0.15);
border-radius: 4px;
background: rgba(255, 255, 255, 0.06);
background: var(--app-selected-mask-color);
}
}
}
+1 -1
View File
@@ -161,7 +161,7 @@ class CmdInput extends React.Component<{}, {}> {
</CenteredIcon>
</div>
</If>
<div key="ai" title="Wave AI (Cmd-Space)" className="cmdinput-icon" onClick={this.clickAIAction}>
<div key="ai" title="Wave AI (Ctrl-Space)" className="cmdinput-icon" onClick={this.clickAIAction}>
<i className="fa-sharp fa-regular fa-sparkles fa-fw" />
</div>
<div
+30 -9
View File
@@ -36,6 +36,7 @@ import { Cmd } from "./cmd";
import { GlobalCommandRunner } from "./global";
import { clearMonoFontCache, getMonoFontSize } from "@/util/textmeasure";
import type { TermWrap } from "@/plugins/terminal/term";
import * as util from "@/util/util";
type SWLinePtr = {
line: LineType;
@@ -143,6 +144,8 @@ class Model {
});
this.ws.reconnect();
this.keybindManager = new KeybindManager();
this.readConfigKeybindings();
this.initSystemKeybindings();
this.inputModel = new InputModel(this);
this.pluginsModel = new PluginsModel(this);
this.bookmarksModel = new BookmarksModel(this);
@@ -171,13 +174,6 @@ class Model {
}
return fontSize;
});
this.keybindManager.registerKeybinding("system", "electron", "any", (waveEvent) => {
if (this.keybindManager.checkKeyPressed(waveEvent, "system:toggleDeveloperTools")) {
getApi().toggleDeveloperTools();
return true;
}
return false;
});
getApi().onTCmd(this.onTCmd.bind(this));
getApi().onICmd(this.onICmd.bind(this));
getApi().onLCmd(this.onLCmd.bind(this));
@@ -208,6 +204,31 @@ class Model {
};
}
readConfigKeybindings() {
const url = new URL(this.getBaseHostPort() + "/config/keybindings.json");
let prtn = fetch(url, { method: "get", body: null, headers: this.getFetchHeaders() });
prtn.then((resp) => {
if (resp.status == 404) {
return [];
} else if (!resp.ok) {
util.handleNotOkResp(resp, url);
}
return resp.json();
}).then((userKeybindings) => {
this.keybindManager.setUserKeybindings(userKeybindings);
});
}
initSystemKeybindings() {
this.keybindManager.registerKeybinding("system", "electron", "any", (waveEvent) => {
if (this.keybindManager.checkKeyPressed(waveEvent, "system:toggleDeveloperTools")) {
getApi().toggleDeveloperTools();
return true;
}
return false;
});
}
static getInstance(): Model {
if (!(window as any).GlobalModel) {
(window as any).GlobalModel = new Model();
@@ -1298,7 +1319,7 @@ class Model {
};
/**
console.log(
"CMD",
"CMD"
pk.metacmd + (pk.metasubcmd != null ? ":" + pk.metasubcmd : ""),
pk.args,
pk.kwargs,
@@ -1526,7 +1547,7 @@ class Model {
.then((resp) => {
if (!resp.ok) {
badResponseStr = sprintf(
"Bad fetch response for /api/read-file: %d %s",
"Bad fetch response for /apiread-file: %d %s",
resp.status,
resp.statusText
);
+66 -42
View File
@@ -1,8 +1,10 @@
import * as React from "react";
import * as mobx from "mobx";
import * as electron from "electron";
import { parse } from "node:path";
import { v4 as uuidv4 } from "uuid";
import keybindings from "../../assets/keybindings.json";
import defaultKeybindingsFile from "../../assets/keybindings.json";
const defaultKeybindings: KeybindConfig = defaultKeybindingsFile;
type KeyPressDecl = {
mods: {
@@ -22,6 +24,7 @@ const KeyTypeKey = "key";
const KeyTypeCode = "code";
type KeybindCallback = (event: WaveKeyboardEvent) => boolean;
type KeybindConfig = Array<{ command: string; keys: Array<string> }>;
type Keybind = {
domain: string;
@@ -36,6 +39,66 @@ class KeybindManager {
levelMap: Map<string, Array<Keybind>>;
levelArray: Array<string>;
keyDescriptionsMap: Map<string, Array<string>>;
userKeybindings: KeybindConfig;
userKeybindingError: OV<string>;
constructor() {
this.levelMap = new Map();
this.domainCallbacks = new Map();
this.levelArray = KeybindLevels;
for (let index = 0; index < this.levelArray.length; index++) {
let curLevel = this.levelArray[index];
this.levelMap.set(curLevel, new Array<Keybind>());
}
this.userKeybindingError = mobx.observable.box(null, {
name: "keyutil-userKeybindingError",
});
this.initKeyDescriptionsMap();
}
initKeyDescriptionsMap() {
mobx.action(() => {
this.userKeybindingError.set(null);
})();
let newKeyDescriptions = new Map();
for (let index = 0; index < defaultKeybindings.length; index++) {
let curKeybind = defaultKeybindings[index];
newKeyDescriptions.set(curKeybind.command, curKeybind.keys);
}
let curUserCommand = "";
if (this.userKeybindings != null && this.userKeybindings instanceof Array) {
try {
console.log("setting user keybindings");
for (let index = 0; index < this.userKeybindings.length; index++) {
let curKeybind = this.userKeybindings[index];
if (curKeybind == null) {
throw new Error("keybind entry is null");
}
curUserCommand = curKeybind.command;
if (typeof curKeybind.command != "string") {
throw new Error("invalid keybind command");
}
if (curKeybind.keys == null || !(curKeybind.keys instanceof Array)) {
throw new Error("invalid keybind keys");
}
for (let key of curKeybind.keys) {
if (typeof key != "string") {
throw new Error("invalid keybind key");
}
}
newKeyDescriptions.set(curKeybind.command, curKeybind.keys);
}
} catch (e) {
let userError = `${curUserCommand} is invalid: error: ${e}`;
console.log(userError);
mobx.action(() => {
this.userKeybindingError.set(userError);
})();
}
}
this.keyDescriptionsMap = newKeyDescriptions;
console.log("key desc map:", this.keyDescriptionsMap);
}
processLevel(nativeEvent: any, event: WaveKeyboardEvent, keybindsArray: Array<Keybind>): boolean {
// iterate through keybinds in backwards order
@@ -196,50 +259,11 @@ class KeybindManager {
this.domainCallbacks.set(domain, callback);
}
constructor() {
this.levelMap = new Map();
this.domainCallbacks = new Map();
this.levelArray = KeybindLevels;
for (let index = 0; index < this.levelArray.length; index++) {
let curLevel = this.levelArray[index];
this.levelMap.set(curLevel, new Array<Keybind>());
}
setUserKeybindings(userKeybindings) {
this.userKeybindings = userKeybindings;
this.initKeyDescriptionsMap();
}
initKeyDescriptionsMap() {
this.keyDescriptionsMap = new Map();
for (let index = 0; index < keybindings.length; index++) {
let curKeybind = keybindings[index];
this.keyDescriptionsMap.set(curKeybind.command, curKeybind.keys);
}
let error = false;
let numberedTabKeybinds = [];
for (let index = 1; index <= 9; index++) {
let curKeybind = this.keyDescriptionsMap.get("app:selectTab-" + index);
if (curKeybind == null) {
error = true;
break;
}
numberedTabKeybinds = numberedTabKeybinds.concat(curKeybind);
}
if (!error) {
this.keyDescriptionsMap.set("app:selectNumberedTab", numberedTabKeybinds);
}
let numberedWorkspaceKeybinds = [];
for (let index = 1; index <= 9; index++) {
let curKeybind = this.keyDescriptionsMap.get("app:selectTab-" + index);
if (curKeybind == null) {
error = true;
break;
}
numberedWorkspaceKeybinds = numberedWorkspaceKeybinds.concat(curKeybind);
}
if (!error) {
this.keyDescriptionsMap.set("app:selectNumberedTab", numberedWorkspaceKeybinds);
}
}
checkKeyPressed(event: WaveKeyboardEvent, keyDescription: string): boolean {
if (keyDescription == "any") {
return true;
+1
View File
@@ -398,6 +398,7 @@ function fireAndForget(f: () => Promise<any>) {
}
export {
handleNotOkResp,
handleJsonFetchResponse,
base64ToString,
stringToBase64,
+23
View File
@@ -17,6 +17,7 @@ import (
"net/http"
"os"
"os/signal"
"path"
"path/filepath"
"regexp"
"runtime"
@@ -676,6 +677,24 @@ func HandleRunCommand(w http.ResponseWriter, r *http.Request) {
WriteJsonSuccess(w, update)
}
func AuthKeyMiddleWare(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqAuthKey := r.Header.Get("X-AuthKey")
w.Header().Set(CacheControlHeaderKey, CacheControlHeaderNoCache)
if reqAuthKey == "" {
w.WriteHeader(500)
w.Write([]byte("no x-authkey header"))
return
}
if reqAuthKey != GlobalAuthKey {
w.WriteHeader(500)
w.Write([]byte("x-authkey header is invalid"))
return
}
next.ServeHTTP(w, r)
})
}
func AuthKeyWrap(fn WebFnType) WebFnType {
return func(w http.ResponseWriter, r *http.Request) {
reqAuthKey := r.Header.Get("X-AuthKey")
@@ -904,6 +923,10 @@ func main() {
gr.HandleFunc("/api/log-active-state", AuthKeyWrap(HandleLogActiveState))
gr.HandleFunc("/api/read-file", AuthKeyWrap(HandleReadFile))
gr.HandleFunc("/api/write-file", AuthKeyWrap(HandleWriteFile)).Methods("POST")
configPath := path.Join(scbase.GetWaveHomeDir(), "config") + "/"
log.Printf("[wave] config path: %q\n", configPath)
gr.PathPrefix("/config/").Handler(AuthKeyMiddleWare(http.StripPrefix("/config/", http.FileServer(http.Dir(configPath)))))
serverAddr := MainServerAddr
if scbase.IsDevMode() {
serverAddr = MainServerDevAddr