diff --git a/Taskfile.yml b/Taskfile.yml index 6509bbef8..7be83f6c4 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -114,6 +114,18 @@ tasks: - task: build:server - task: build:wsh + 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' + build:server: desc: Build the wavesrv component. cmds: @@ -243,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" diff --git a/cmd/generateschema/main-generateschema.go b/cmd/generateschema/main-generateschema.go new file mode 100644 index 000000000..bb0d09d6a --- /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/codeeditor.tsx b/frontend/app/view/codeeditor/codeeditor.tsx index 9947dab69..a71bd8a7c 100644 --- a/frontend/app/view/codeeditor/codeeditor.tsx +++ b/frontend/app/view/codeeditor/codeeditor.tsx @@ -9,11 +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 { 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, getSchemaEndpointInfo } from "./schemaendpoints"; import ymlWorker from "./yamlworker?worker"; import "./codeeditor.scss"; @@ -42,48 +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, - }); - 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 - }); - }) - .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) => getSchemaEndpointInfo(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 { @@ -125,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 () => { @@ -135,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); @@ -165,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 000000000..19b1a546a --- /dev/null +++ b/frontend/app/view/codeeditor/schemaendpoints.ts @@ -0,0 +1,38 @@ +// 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 getSchemaEndpointInfo(endpoint: string): Promise { + 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 { + uri: endpoint, + fileMatch, + schema, + }; +} + +const SchemaEndpoints = Array.from(allFilepaths.keys()); + +export { getSchemaEndpointInfo, SchemaEndpoints }; diff --git a/frontend/wave.ts b/frontend/wave.ts index 1a0ec3f41..ea49cbb6b 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); diff --git a/go.mod b/go.mod index 6bca8039d..73748e4fa 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.13 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.12 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.33.11 // 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-20250124145028-65684f501c47 // indirect google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.4 // 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 e883c11e2..e871be95d 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,10 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.33.11 h1:RIXOjp7Dp4siCYJRwBHUcBdVgOW github.com/aws/aws-sdk-go-v2/service/sts v1.33.11/go.mod h1:ZR17k9bPKPR8u0IkyA6xVsjr56doNQ4ZB1fs7abYBfE= 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.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40Rmc google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= google.golang.org/protobuf v1.36.4/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 new file mode 100644 index 000000000..c4825cf3a --- /dev/null +++ b/pkg/schema/schema.go @@ -0,0 +1,54 @@ +// 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") + 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 6cc1f999b..7450d6cb5 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" @@ -419,6 +420,7 @@ func MakeUnixListener() (net.Listener, error) { } const docsitePrefix = "/docsite/" +const schemaPrefix = "/schema/" // blocking func RunWebServer(listener net.Listener) { @@ -428,6 +430,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) diff --git a/schema/settings.json b/schema/settings.json new file mode 100644 index 000000000..abe5caac4 --- /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