From 78e27c3225c368de28abc8ac8cce3dde90a20d40 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 29 Jan 2025 03:28:12 -0800 Subject: [PATCH 1/7] feat: proof of concept settings file schema This adds a version of the schema that relies on hard-coded data but shows that this is a viable way to implement it --- frontend/app/view/codeeditor/codeeditor.tsx | 23 +++++++-- pkg/schema/schema.go | 55 +++++++++++++++++++++ pkg/web/web.go | 3 ++ 3 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 pkg/schema/schema.go diff --git a/frontend/app/view/codeeditor/codeeditor.tsx b/frontend/app/view/codeeditor/codeeditor.tsx index 9947dab69d..0de226b054 100644 --- a/frontend/app/view/codeeditor/codeeditor.tsx +++ b/frontend/app/view/codeeditor/codeeditor.tsx @@ -9,6 +9,7 @@ import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api"; import { configureMonacoYaml } from "monaco-yaml"; import React, { useMemo, useRef } from "react"; +import { getWebServerEndpoint } from "@/util/endpoints"; import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker"; import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker"; import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker"; @@ -75,11 +76,23 @@ export function loadMonaco() { monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ noSemanticValidation: true, }); - monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ - validate: true, - allowComments: false, // Set to true if you want to allow comments in JSON - schemas: [], // You can specify JSON schemas here if needed - }); + const schemaUri = getWebServerEndpoint() + "/schema/settings.json"; + fetch(schemaUri) + .then((data) => data.json()) + .then((schema: object) => { + console.log("schema is", schema); + monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ + validate: true, + allowComments: false, // Set to true if you want to allow comments in JSON + schemas: [ + { + uri: "http://mytest/settings.json", + fileMatch: ["settings.json"], + schema, + }, + ], // You can specify JSON schemas here if needed + }); + }); }) .catch((e) => { console.error("error loading monaco", e); diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go new file mode 100644 index 0000000000..080e2d4bdf --- /dev/null +++ b/pkg/schema/schema.go @@ -0,0 +1,55 @@ +// Copyright 2025, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +package schema + +import ( + "log" + "net/http" + "os" + "path/filepath" + + "github.com/wavetermdev/waveterm/pkg/wavebase" +) + +var schemaHandler http.Handler + +func GetSchemaHandler() http.Handler { + schemaStaticPath := filepath.Join(wavebase.GetWaveAppPath(), "schema") + stat, err := os.Stat(schemaStaticPath) + if schemaHandler == nil { + log.Println("Schema is nil, initializing") + if err == nil && stat.IsDir() { + log.Printf("Found static site at %s, serving\n", schemaStaticPath) + schemaHandler = http.FileServer(JsonDir{http.Dir(schemaStaticPath)}) + } else { + log.Printf("Did not find static site at %s, serving not found handler. stat: %v, err: %v\n", schemaStaticPath, stat, err) + schemaHandler = http.NotFoundHandler() + } + } + return addHeaders(schemaHandler) +} + +func addHeaders(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/schema+json") + w.WriteHeader(http.StatusOK) + next.ServeHTTP(w, r) + }) +} + +type JsonDir struct { + d http.Dir +} + +func (d JsonDir) Open(name string) (http.File, error) { + // Try name as supplied + f, err := d.d.Open(name) + if os.IsNotExist(err) { + // Not found, try with .json + if f, err := d.d.Open(name + ".json"); err == nil { + return f, nil + } + } + return f, err +} diff --git a/pkg/web/web.go b/pkg/web/web.go index 35d9445c68..e863053f2a 100644 --- a/pkg/web/web.go +++ b/pkg/web/web.go @@ -25,6 +25,7 @@ import ( "github.com/wavetermdev/waveterm/pkg/docsite" "github.com/wavetermdev/waveterm/pkg/filestore" "github.com/wavetermdev/waveterm/pkg/panichandler" + "github.com/wavetermdev/waveterm/pkg/schema" "github.com/wavetermdev/waveterm/pkg/service" "github.com/wavetermdev/waveterm/pkg/wavebase" "github.com/wavetermdev/waveterm/pkg/wshrpc" @@ -409,6 +410,7 @@ func MakeUnixListener() (net.Listener, error) { } const docsitePrefix = "/docsite/" +const schemaPrefix = "/schema/" // blocking func RunWebServer(listener net.Listener) { @@ -418,6 +420,7 @@ func RunWebServer(listener net.Listener) { gr.HandleFunc("/wave/service", WebFnWrap(WebFnOpts{JsonErrors: true}, handleService)) gr.HandleFunc("/vdom/{uuid}/{path:.*}", WebFnWrap(WebFnOpts{AllowCaching: true}, handleVDom)) gr.PathPrefix(docsitePrefix).Handler(http.StripPrefix(docsitePrefix, docsite.GetDocsiteHandler())) + gr.PathPrefix(schemaPrefix).Handler(http.StripPrefix(schemaPrefix, schema.GetSchemaHandler())) handler := http.TimeoutHandler(gr, HttpTimeoutDuration, "Timeout") if wavebase.IsDevMode() { handler = handlers.CORS(handlers.AllowedOrigins([]string{"*"}))(handler) From c21d996b123acbb1648bc92ee1b8d474df04805e Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 29 Jan 2025 05:30:00 -0800 Subject: [PATCH 2/7] refactor: schema loading organization This makes it easier to load multiple schema files and configure them with specific filepaths. --- frontend/app/view/codeeditor/codeeditor.tsx | 117 ++++++++++-------- .../app/view/codeeditor/schemaendpoints.ts | 30 +++++ 2 files changed, 92 insertions(+), 55 deletions(-) create mode 100644 frontend/app/view/codeeditor/schemaendpoints.ts diff --git a/frontend/app/view/codeeditor/codeeditor.tsx b/frontend/app/view/codeeditor/codeeditor.tsx index 0de226b054..d876c6062f 100644 --- a/frontend/app/view/codeeditor/codeeditor.tsx +++ b/frontend/app/view/codeeditor/codeeditor.tsx @@ -9,12 +9,15 @@ import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api"; import { configureMonacoYaml } from "monaco-yaml"; import React, { useMemo, useRef } from "react"; -import { getWebServerEndpoint } from "@/util/endpoints"; +import { RpcApi } from "@/app/store/wshclientapi"; +import { TabRpcClient } from "@/app/store/wshrpcutil"; +import { makeConnRoute } from "@/util/util"; import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker"; import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker"; import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker"; import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker"; import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker"; +import { SchemaEndpoints, getEndpointInfo } from "./schemaendpoints"; import ymlWorker from "./yamlworker?worker"; import "./codeeditor.scss"; @@ -43,60 +46,45 @@ window.MonacoEnvironment = { }, }; -export function loadMonaco() { +export async function loadMonaco() { loader.config({ paths: { vs: "monaco" } }); - loader - .init() - .then(() => { - monaco.editor.defineTheme("wave-theme-dark", { - base: "vs-dark", - inherit: true, - rules: [], - colors: { - "editor.background": "#00000000", - "editorStickyScroll.background": "#00000055", - "minimap.background": "#00000077", - focusBorder: "#00000000", - }, - }); - monaco.editor.defineTheme("wave-theme-light", { - base: "vs", - inherit: true, - rules: [], - colors: { - "editor.background": "#fefefe", - focusBorder: "#00000000", - }, - }); - configureMonacoYaml(monaco, { - validate: true, - schemas: [], - }); - // Disable default validation errors for typescript and javascript - monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ - noSemanticValidation: true, - }); - const schemaUri = getWebServerEndpoint() + "/schema/settings.json"; - fetch(schemaUri) - .then((data) => data.json()) - .then((schema: object) => { - console.log("schema is", schema); - monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ - validate: true, - allowComments: false, // Set to true if you want to allow comments in JSON - schemas: [ - { - uri: "http://mytest/settings.json", - fileMatch: ["settings.json"], - schema, - }, - ], // You can specify JSON schemas here if needed - }); - }); - }) - .catch((e) => { - console.error("error loading monaco", e); - }); + await loader.init(); + + monaco.editor.defineTheme("wave-theme-dark", { + base: "vs-dark", + inherit: true, + rules: [], + colors: { + "editor.background": "#00000000", + "editorStickyScroll.background": "#00000055", + "minimap.background": "#00000077", + focusBorder: "#00000000", + }, + }); + monaco.editor.defineTheme("wave-theme-light", { + base: "vs", + inherit: true, + rules: [], + colors: { + "editor.background": "#fefefe", + focusBorder: "#00000000", + }, + }); + configureMonacoYaml(monaco, { + validate: true, + schemas: [], + }); + // Disable default validation errors for typescript and javascript + monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ + noSemanticValidation: true, + }); + const schemas = await Promise.all(SchemaEndpoints.map((endpoint) => getEndpointInfo(endpoint))); + monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ + validate: true, + allowComments: false, // Set to true if you want to allow comments in JSON + enableSchemaRequest: true, + schemas, + }); } function defaultEditorOptions(): MonacoTypes.editor.IEditorOptions { @@ -138,6 +126,7 @@ export function CodeEditor({ blockId, text, language, filename, meta, onChange, const wordWrap = useOverrideConfigAtom(blockId, "editor:wordwrap") ?? false; const fontSize = boundNumber(useOverrideConfigAtom(blockId, "editor:fontsize"), 6, 64); const theme = "wave-theme-dark"; + const [absPath, setAbsPath] = React.useState(""); React.useEffect(() => { return () => { @@ -148,6 +137,24 @@ export function CodeEditor({ blockId, text, language, filename, meta, onChange, }; }, []); + React.useEffect(() => { + const inner = async () => { + try { + const fileInfo = await RpcApi.RemoteFileJoinCommand(TabRpcClient, [filename], { + route: makeConnRoute(meta.connection ?? ""), + }); + setAbsPath(`${fileInfo.dir}/${fileInfo.name}`); + } catch (e) { + setAbsPath(filename); + } + }; + inner(); + }, [filename]); + + React.useEffect(() => { + console.log("abspath is", absPath); + }, [absPath]); + function handleEditorChange(text: string, ev: MonacoTypes.editor.IModelContentChangedEvent) { if (onChange) { onChange(text); @@ -178,7 +185,7 @@ export function CodeEditor({ blockId, text, language, filename, meta, onChange, options={editorOpts} onChange={handleEditorChange} onMount={handleEditorOnMount} - path={filename} + path={absPath} language={language} /> diff --git a/frontend/app/view/codeeditor/schemaendpoints.ts b/frontend/app/view/codeeditor/schemaendpoints.ts new file mode 100644 index 0000000000..685c3df5f9 --- /dev/null +++ b/frontend/app/view/codeeditor/schemaendpoints.ts @@ -0,0 +1,30 @@ +// Copyright 2025, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { getApi } from "@/app/store/global"; +import { getWebServerEndpoint } from "@/util/endpoints"; + +type EndpointInfo = { + uri: string; + fileMatch: Array; + schema: object; +}; + +const allFilepaths: Map> = new Map(); +allFilepaths.set(`${getWebServerEndpoint()}/schema/settings.json`, [`${getApi().getConfigDir()}/settings.json`]); + +async function getEndpointInfo(endpoint: string): Promise { + const data = await fetch(endpoint); + const schema: object = await data.json(); + const fileMatch = allFilepaths.get(endpoint) ?? []; + + return { + uri: endpoint, + fileMatch, + schema, + }; +} + +const SchemaEndpoints = Array.from(allFilepaths.keys()); + +export { getEndpointInfo, SchemaEndpoints }; From ce429045026642cd47121d180326a87fb3bb0541 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 29 Jan 2025 11:10:20 -0800 Subject: [PATCH 3/7] refactor: rename endpoint info func for clarity --- frontend/app/view/codeeditor/codeeditor.tsx | 4 ++-- frontend/app/view/codeeditor/schemaendpoints.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/app/view/codeeditor/codeeditor.tsx b/frontend/app/view/codeeditor/codeeditor.tsx index d876c6062f..a71bd8a7cc 100644 --- a/frontend/app/view/codeeditor/codeeditor.tsx +++ b/frontend/app/view/codeeditor/codeeditor.tsx @@ -17,7 +17,7 @@ import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker"; import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker"; import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker"; import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker"; -import { SchemaEndpoints, getEndpointInfo } from "./schemaendpoints"; +import { SchemaEndpoints, getSchemaEndpointInfo } from "./schemaendpoints"; import ymlWorker from "./yamlworker?worker"; import "./codeeditor.scss"; @@ -78,7 +78,7 @@ export async function loadMonaco() { monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ noSemanticValidation: true, }); - const schemas = await Promise.all(SchemaEndpoints.map((endpoint) => getEndpointInfo(endpoint))); + const schemas = await Promise.all(SchemaEndpoints.map((endpoint) => getSchemaEndpointInfo(endpoint))); monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ validate: true, allowComments: false, // Set to true if you want to allow comments in JSON diff --git a/frontend/app/view/codeeditor/schemaendpoints.ts b/frontend/app/view/codeeditor/schemaendpoints.ts index 685c3df5f9..37dc79164f 100644 --- a/frontend/app/view/codeeditor/schemaendpoints.ts +++ b/frontend/app/view/codeeditor/schemaendpoints.ts @@ -13,7 +13,7 @@ type EndpointInfo = { const allFilepaths: Map> = new Map(); allFilepaths.set(`${getWebServerEndpoint()}/schema/settings.json`, [`${getApi().getConfigDir()}/settings.json`]); -async function getEndpointInfo(endpoint: string): Promise { +async function getSchemaEndpointInfo(endpoint: string): Promise { const data = await fetch(endpoint); const schema: object = await data.json(); const fileMatch = allFilepaths.get(endpoint) ?? []; @@ -27,4 +27,4 @@ async function getEndpointInfo(endpoint: string): Promise { const SchemaEndpoints = Array.from(allFilepaths.keys()); -export { getEndpointInfo, SchemaEndpoints }; +export { getSchemaEndpointInfo, SchemaEndpoints }; From c25f5b70fc93ade83cf7ac94c2aecfcbeb019afc Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 29 Jan 2025 16:30:49 -0800 Subject: [PATCH 4/7] feat: connect schema generation to endpoint This generates the schema files and adds them to the server as a build step. --- Taskfile.yml | 15 ++ cmd/generateschema/main-generateschema.go | 39 +++ .../app/view/codeeditor/schemaendpoints.ts | 4 +- go.mod | 6 + go.sum | 19 ++ pkg/schema/schema.go | 1 - schema/settings.json | 232 ++++++++++++++++++ 7 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 cmd/generateschema/main-generateschema.go create mode 100644 schema/settings.json diff --git a/Taskfile.yml b/Taskfile.yml index 160142e472..9977f79bb9 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -25,6 +25,7 @@ tasks: - yarn - docsite:build:embedded - build:backend + - build:schema env: WCLOUD_ENDPOINT: "https://ot2e112zx5.execute-api.us-west-2.amazonaws.com/dev" WCLOUD_WS_ENDPOINT: "wss://5lfzlg5crl.execute-api.us-west-2.amazonaws.com/dev/" @@ -38,6 +39,7 @@ tasks: - yarn - docsite:build:embedded - build:backend + - build:schema env: WCLOUD_ENDPOINT: "https://ot2e112zx5.execute-api.us-west-2.amazonaws.com/dev" WCLOUD_WS_ENDPOINT: "wss://5lfzlg5crl.execute-api.us-west-2.amazonaws.com/dev/" @@ -107,6 +109,7 @@ tasks: - yarn - docsite:build:embedded - build:backend + - build:schema build:backend: desc: Build the wavesrv and wsh components. @@ -114,6 +117,18 @@ tasks: - task: build:server - task: build:wsh + build:schema: + desc: Build the schema for configuration. + sources: + generates: + - "dist/schema/**/*" + cmds: + - go run cmd/generateschema/main-generateschema.go + - '{{.RMRF}} "dist/schema"' + - task: copyfiles:'schema':'dist/schema' + deps: + - generate + build:server: desc: Build the wavesrv component. cmds: diff --git a/cmd/generateschema/main-generateschema.go b/cmd/generateschema/main-generateschema.go new file mode 100644 index 0000000000..bb0d09d6a4 --- /dev/null +++ b/cmd/generateschema/main-generateschema.go @@ -0,0 +1,39 @@ +// Copyright 2025, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" + + "github.com/invopop/jsonschema" + "github.com/wavetermdev/waveterm/pkg/util/utilfn" + "github.com/wavetermdev/waveterm/pkg/wconfig" +) + +const WaveSchemaSettingsFileName = "schema/settings.json" + +func main() { + settingsSchema := jsonschema.Reflect(&wconfig.SettingsType{}) + + jsonSettingsSchema, err := json.MarshalIndent(settingsSchema, "", " ") + if err != nil { + log.Fatalf("failed to parse local schema: %v", err) + } + /* + err = os.MkdirAll(WaveSchemaSettingsFileName, 0755) + if err != nil { + log.Fatalf("failed to create schema dir: %v", err) + } + */ + written, err := utilfn.WriteFileIfDifferent(WaveSchemaSettingsFileName, jsonSettingsSchema) + if !written { + fmt.Fprintf(os.Stderr, "no changes to %s\n", WaveSchemaSettingsFileName) + } + if err != nil { + log.Fatalf("failed to write local schema: %v", err) + } +} diff --git a/frontend/app/view/codeeditor/schemaendpoints.ts b/frontend/app/view/codeeditor/schemaendpoints.ts index 37dc79164f..454923c343 100644 --- a/frontend/app/view/codeeditor/schemaendpoints.ts +++ b/frontend/app/view/codeeditor/schemaendpoints.ts @@ -15,7 +15,9 @@ allFilepaths.set(`${getWebServerEndpoint()}/schema/settings.json`, [`${getApi(). async function getSchemaEndpointInfo(endpoint: string): Promise { const data = await fetch(endpoint); - const schema: object = await data.json(); + const fullSchema: object = await data.json(); + const schemaRef = fullSchema?.["$ref"]; + const schema = fullSchema?.[schemaRef]; const fileMatch = allFilepaths.get(endpoint) ?? []; return { diff --git a/go.mod b/go.mod index b32bef0ba7..49a8189a57 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/gorilla/handlers v1.5.2 github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.3 + github.com/invopop/jsonschema v0.13.0 github.com/jmoiron/sqlx v1.4.0 github.com/kevinburke/ssh_config v1.2.0 github.com/mattn/go-sqlite3 v1.14.24 @@ -59,6 +60,8 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.24.11 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/ebitengine/purego v0.8.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect @@ -71,12 +74,14 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/ubuntu/decorate v0.0.0-20230125165522-2d5b0a9bb117 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect @@ -93,6 +98,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect google.golang.org/grpc v1.69.4 // indirect google.golang.org/protobuf v1.36.3 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) replace github.com/kevinburke/ssh_config => github.com/wavetermdev/ssh_config v0.0.0-20241219203747-6409e4292f34 diff --git a/go.sum b/go.sum index 64e8d740f6..ed03296947 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,10 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 h1:BRVDbewN6VZcwr+FBOszDKvYeXY1 github.com/aws/aws-sdk-go-v2/service/sts v1.33.9/go.mod h1:f6vjfZER1M17Fokn0IzssOTMT2N8ZSq+7jnNF0tArvw= github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -105,12 +109,21 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= +github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= @@ -122,6 +135,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sashabaranov/go-openai v1.36.1 h1:EVfRXwIlW2rUzpx6vR+aeIKCK/xylSrVYAx1TMTSX3g= github.com/sashabaranov/go-openai v1.36.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= @@ -157,6 +172,8 @@ github.com/wavetermdev/htmltoken v0.2.0 h1:sFVPPemlDv7/jg7n4Hx1AEF2m9MVAFjFpELWf github.com/wavetermdev/htmltoken v0.2.0/go.mod h1:5FM0XV6zNYiNza2iaTcFGj+hnMtgqumFHO31Z8euquk= github.com/wavetermdev/ssh_config v0.0.0-20241219203747-6409e4292f34 h1:I8VZVTZEXhnzfN7jB9a7TZYpzNO48sCUWMRXHM9XWSA= github.com/wavetermdev/ssh_config v0.0.0-20241219203747-6409e4292f34/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= @@ -212,6 +229,8 @@ google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7 google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index 080e2d4bdf..c4825cf3aa 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -33,7 +33,6 @@ func GetSchemaHandler() http.Handler { func addHeaders(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/schema+json") - w.WriteHeader(http.StatusOK) next.ServeHTTP(w, r) }) } diff --git a/schema/settings.json b/schema/settings.json new file mode 100644 index 0000000000..abe5caac48 --- /dev/null +++ b/schema/settings.json @@ -0,0 +1,232 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/wavetermdev/waveterm/pkg/wconfig/settings-type", + "$ref": "#/$defs/SettingsType", + "$defs": { + "SettingsType": { + "properties": { + "app:*": { + "type": "boolean" + }, + "app:globalhotkey": { + "type": "string" + }, + "app:dismissarchitecturewarning": { + "type": "boolean" + }, + "ai:*": { + "type": "boolean" + }, + "ai:preset": { + "type": "string" + }, + "ai:apitype": { + "type": "string" + }, + "ai:baseurl": { + "type": "string" + }, + "ai:apitoken": { + "type": "string" + }, + "ai:name": { + "type": "string" + }, + "ai:model": { + "type": "string" + }, + "ai:orgid": { + "type": "string" + }, + "ai:apiversion": { + "type": "string" + }, + "ai:maxtokens": { + "type": "number" + }, + "ai:timeoutms": { + "type": "number" + }, + "ai:fontsize": { + "type": "number" + }, + "ai:fixedfontsize": { + "type": "number" + }, + "term:*": { + "type": "boolean" + }, + "term:fontsize": { + "type": "number" + }, + "term:fontfamily": { + "type": "string" + }, + "term:theme": { + "type": "string" + }, + "term:disablewebgl": { + "type": "boolean" + }, + "term:localshellpath": { + "type": "string" + }, + "term:localshellopts": { + "items": { + "type": "string" + }, + "type": "array" + }, + "term:scrollback": { + "type": "integer" + }, + "term:copyonselect": { + "type": "boolean" + }, + "term:transparency": { + "type": "number" + }, + "term:allowbracketedpaste": { + "type": "boolean" + }, + "editor:minimapenabled": { + "type": "boolean" + }, + "editor:stickyscrollenabled": { + "type": "boolean" + }, + "editor:wordwrap": { + "type": "boolean" + }, + "editor:fontsize": { + "type": "number" + }, + "web:*": { + "type": "boolean" + }, + "web:openlinksinternally": { + "type": "boolean" + }, + "web:defaulturl": { + "type": "string" + }, + "web:defaultsearch": { + "type": "string" + }, + "blockheader:*": { + "type": "boolean" + }, + "blockheader:showblockids": { + "type": "boolean" + }, + "autoupdate:*": { + "type": "boolean" + }, + "autoupdate:enabled": { + "type": "boolean" + }, + "autoupdate:intervalms": { + "type": "number" + }, + "autoupdate:installonquit": { + "type": "boolean" + }, + "autoupdate:channel": { + "type": "string" + }, + "markdown:fontsize": { + "type": "number" + }, + "markdown:fixedfontsize": { + "type": "number" + }, + "preview:showhiddenfiles": { + "type": "boolean" + }, + "tab:preset": { + "type": "string" + }, + "widget:*": { + "type": "boolean" + }, + "widget:showhelp": { + "type": "boolean" + }, + "window:*": { + "type": "boolean" + }, + "window:transparent": { + "type": "boolean" + }, + "window:blur": { + "type": "boolean" + }, + "window:opacity": { + "type": "number" + }, + "window:bgcolor": { + "type": "string" + }, + "window:reducedmotion": { + "type": "boolean" + }, + "window:tilegapsize": { + "type": "integer" + }, + "window:showmenubar": { + "type": "boolean" + }, + "window:nativetitlebar": { + "type": "boolean" + }, + "window:disablehardwareacceleration": { + "type": "boolean" + }, + "window:maxtabcachesize": { + "type": "integer" + }, + "window:magnifiedblockopacity": { + "type": "number" + }, + "window:magnifiedblocksize": { + "type": "number" + }, + "window:magnifiedblockblurprimarypx": { + "type": "integer" + }, + "window:magnifiedblockblursecondarypx": { + "type": "integer" + }, + "window:confirmclose": { + "type": "boolean" + }, + "window:savelastwindow": { + "type": "boolean" + }, + "window:dimensions": { + "type": "string" + }, + "window:zoom": { + "type": "number" + }, + "telemetry:*": { + "type": "boolean" + }, + "telemetry:enabled": { + "type": "boolean" + }, + "conn:*": { + "type": "boolean" + }, + "conn:askbeforewshinstall": { + "type": "boolean" + }, + "conn:wshenabled": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object" + } + } +} \ No newline at end of file From 5e81b2dee7fa7309c5934324c6b8c85acae6db6d Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 29 Jan 2025 16:33:31 -0800 Subject: [PATCH 5/7] fix: await monaco initialization This may fix the light mode monaco bug along with schema initialization. --- frontend/wave.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/wave.ts b/frontend/wave.ts index 1a0ec3f41c..ea49cbb6b0 100644 --- a/frontend/wave.ts +++ b/frontend/wave.ts @@ -183,7 +183,7 @@ async function initWave(initOpts: WaveInitOpts) { registerGlobalKeys(); registerElectronReinjectKeyHandler(); registerControlShiftStateUpdateHandler(); - setTimeout(loadMonaco, 30); + await loadMonaco(); const fullConfig = await RpcApi.GetFullConfigCommand(TabRpcClient); console.log("fullconfig", fullConfig); globalStore.set(atoms.fullConfigAtom, fullConfig); From 475436d8ce3c63c4739eb4d66c27e33a1b5b363c Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Mon, 3 Feb 2025 13:46:05 -0800 Subject: [PATCH 6/7] fix: move build:schema to generate in taskfile --- Taskfile.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 7fbd7b704a..7be83f6c4f 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -25,7 +25,6 @@ tasks: - yarn - docsite:build:embedded - build:backend - - build:schema env: WCLOUD_ENDPOINT: "https://ot2e112zx5.execute-api.us-west-2.amazonaws.com/dev" WCLOUD_WS_ENDPOINT: "wss://5lfzlg5crl.execute-api.us-west-2.amazonaws.com/dev/" @@ -39,7 +38,6 @@ tasks: - yarn - docsite:build:embedded - build:backend - - build:schema env: WCLOUD_ENDPOINT: "https://ot2e112zx5.execute-api.us-west-2.amazonaws.com/dev" WCLOUD_WS_ENDPOINT: "wss://5lfzlg5crl.execute-api.us-west-2.amazonaws.com/dev/" @@ -109,7 +107,6 @@ tasks: - yarn - docsite:build:embedded - build:backend - - build:schema build:backend: desc: Build the wavesrv and wsh components. @@ -120,14 +117,14 @@ tasks: build:schema: desc: Build the schema for configuration. sources: + - "cmd/generateschema/*.go" + - "pkg/wconfig/*.go" generates: - "dist/schema/**/*" cmds: - go run cmd/generateschema/main-generateschema.go - '{{.RMRF}} "dist/schema"' - task: copyfiles:'schema':'dist/schema' - deps: - - generate build:server: desc: Build the wavesrv component. @@ -258,6 +255,8 @@ tasks: cmds: - go run cmd/generatets/main-generatets.go - go run cmd/generatego/main-generatego.go + deps: + - build:schema sources: - "cmd/generatego/*.go" - "cmd/generatets/*.go" From 29a734cae59e9b70296aa97ec7636307532647cd Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Mon, 3 Feb 2025 14:04:37 -0800 Subject: [PATCH 7/7] fix: error handling for missing schema --- frontend/app/view/codeeditor/schemaendpoints.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/frontend/app/view/codeeditor/schemaendpoints.ts b/frontend/app/view/codeeditor/schemaendpoints.ts index 454923c343..19b1a546a6 100644 --- a/frontend/app/view/codeeditor/schemaendpoints.ts +++ b/frontend/app/view/codeeditor/schemaendpoints.ts @@ -14,10 +14,16 @@ const allFilepaths: Map> = new Map(); allFilepaths.set(`${getWebServerEndpoint()}/schema/settings.json`, [`${getApi().getConfigDir()}/settings.json`]); async function getSchemaEndpointInfo(endpoint: string): Promise { - const data = await fetch(endpoint); - const fullSchema: object = await data.json(); - const schemaRef = fullSchema?.["$ref"]; - const schema = fullSchema?.[schemaRef]; + let schema: Object; + try { + const data = await fetch(endpoint); + const fullSchema: object = await data.json(); + const schemaRef: string = fullSchema?.["$ref"]; + schema = fullSchema?.[schemaRef]; + } catch (e) { + console.log("cannot find schema:", e); + schema = {}; + } const fileMatch = allFilepaths.get(endpoint) ?? []; return {