add client app
This commit is contained in:
parent
8706badec5
commit
bb7cdac04f
|
@ -2,3 +2,6 @@ adventure/custom_actions.py
|
|||
worlds/
|
||||
__pycache__/
|
||||
.env
|
||||
venv/
|
||||
client/node_modules/
|
||||
client/out/
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"projectFolder": ".",
|
||||
"mainEntryPointFilePath": "<projectFolder>/out/src/main.d.ts",
|
||||
"apiReport": {
|
||||
"enabled": true,
|
||||
"reportFolder": "<projectFolder>/docs/",
|
||||
"reportTempFolder": "<projectFolder>/out/tmp/"
|
||||
},
|
||||
"docModel": {
|
||||
"enabled": true,
|
||||
"apiJsonFilePath": "<projectFolder>/out/api/<unscopedPackageName>.api.json"
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "<projectFolder>/out/main.d.ts",
|
||||
"betaTrimmedFilePath": "<projectFolder>/out/main-beta.d.ts",
|
||||
"publicTrimmedFilePath": "<projectFolder>/out/main-public.d.ts"
|
||||
},
|
||||
"tsdocMetadata": {
|
||||
},
|
||||
"messages": {
|
||||
"compilerMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
},
|
||||
"extractorMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
},
|
||||
"tsdocMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,314 @@
|
|||
{
|
||||
"env": {
|
||||
"es6": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "tsconfig.json",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"eslint-plugin-chai",
|
||||
"eslint-plugin-chai-expect",
|
||||
"eslint-plugin-chai-expect-keywords",
|
||||
"eslint-plugin-import",
|
||||
"eslint-plugin-mocha",
|
||||
"eslint-plugin-no-null",
|
||||
"eslint-plugin-sonarjs",
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/adjacent-overload-signatures": "error",
|
||||
"@typescript-eslint/array-type": [
|
||||
"error",
|
||||
{
|
||||
"default": "generic"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/await-thenable": "error",
|
||||
"@typescript-eslint/ban-types": [
|
||||
"error",
|
||||
{
|
||||
"types": {
|
||||
"null": "Use 'undefined' instead of 'null'"
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/consistent-type-assertions": "error",
|
||||
"@typescript-eslint/consistent-type-definitions": "error",
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"error",
|
||||
{
|
||||
"accessibility": "explicit",
|
||||
"overrides": {
|
||||
"constructors": "no-public"
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/indent": [
|
||||
"error",
|
||||
2,
|
||||
{
|
||||
"ObjectExpression": "first",
|
||||
"FunctionDeclaration": {
|
||||
"parameters": "first"
|
||||
},
|
||||
"FunctionExpression": {
|
||||
"parameters": "first"
|
||||
},
|
||||
"SwitchCase": 1
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"error",
|
||||
{
|
||||
"multiline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": true
|
||||
},
|
||||
"singleline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": [
|
||||
"error",
|
||||
{
|
||||
"default": [
|
||||
"public-static-method",
|
||||
"public-static-field",
|
||||
"public-instance-field",
|
||||
"protected-instance-field",
|
||||
"public-constructor",
|
||||
"public-instance-method",
|
||||
"protected-instance-method"
|
||||
]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-empty-function": "error",
|
||||
"@typescript-eslint/no-empty-interface": "error",
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/no-for-in-array": "error",
|
||||
"@typescript-eslint/no-inferrable-types": "error",
|
||||
"@typescript-eslint/no-misused-new": "error",
|
||||
"@typescript-eslint/no-namespace": "error",
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
"no-param-reassign": "error",
|
||||
"@typescript-eslint/no-parameter-properties": "error",
|
||||
"@typescript-eslint/no-this-alias": "error",
|
||||
"@typescript-eslint/no-unnecessary-type-arguments": "error",
|
||||
"@typescript-eslint/no-use-before-declare": "off",
|
||||
"@typescript-eslint/no-var-requires": "error",
|
||||
"@typescript-eslint/prefer-for-of": "error",
|
||||
"@typescript-eslint/prefer-function-type": "error",
|
||||
"@typescript-eslint/prefer-namespace-keyword": "error",
|
||||
"@typescript-eslint/quotes": [
|
||||
"error",
|
||||
"single",
|
||||
{
|
||||
"avoidEscape": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/restrict-plus-operands": "error",
|
||||
"@typescript-eslint/semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"space-in-parens": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"@typescript-eslint/strict-boolean-expressions": "error",
|
||||
"@typescript-eslint/triple-slash-reference": "error",
|
||||
"@typescript-eslint/type-annotation-spacing": "error",
|
||||
"@typescript-eslint/unbound-method": "error",
|
||||
"@typescript-eslint/unified-signatures": "error",
|
||||
"arrow-body-style": "error",
|
||||
"arrow-parens": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"camelcase": "error",
|
||||
"complexity": [
|
||||
"error",
|
||||
{
|
||||
"max": 12
|
||||
}
|
||||
],
|
||||
"constructor-super": "error",
|
||||
"curly": "error",
|
||||
"default-case": "error",
|
||||
"dot-notation": "error",
|
||||
"eol-last": "error",
|
||||
"eqeqeq": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"guard-for-in": "error",
|
||||
"id-blacklist": [
|
||||
"error",
|
||||
"any",
|
||||
"String",
|
||||
"Boolean",
|
||||
"Undefined"
|
||||
],
|
||||
"id-match": "error",
|
||||
"import/no-default-export": "error",
|
||||
"import/no-deprecated": "error",
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"import/no-internal-modules": "off",
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
"groups": [
|
||||
[
|
||||
"builtin",
|
||||
"external"
|
||||
],
|
||||
[
|
||||
"index",
|
||||
"parent",
|
||||
"sibling",
|
||||
"unknown"
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
"max-classes-per-file": [
|
||||
"off",
|
||||
1
|
||||
],
|
||||
"max-len": [
|
||||
"error",
|
||||
{
|
||||
"code": 180
|
||||
}
|
||||
],
|
||||
"max-lines": [
|
||||
"error",
|
||||
500
|
||||
],
|
||||
"new-parens": "error",
|
||||
"no-bitwise": "off",
|
||||
"no-caller": "error",
|
||||
"no-cond-assign": "error",
|
||||
"no-console": "error",
|
||||
"no-debugger": "error",
|
||||
"no-duplicate-case": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
"no-empty": "error",
|
||||
"no-eval": "error",
|
||||
"no-extra-bind": "error",
|
||||
"no-fallthrough": "off",
|
||||
"no-invalid-this": "error",
|
||||
"no-irregular-whitespace": "error",
|
||||
"@typescript-eslint/no-magic-numbers": [
|
||||
"error",
|
||||
{
|
||||
"ignore": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
10
|
||||
],
|
||||
"ignoreEnums": true
|
||||
}
|
||||
],
|
||||
"no-multiple-empty-lines": "error",
|
||||
"no-new-func": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-null/no-null": "error",
|
||||
"no-plusplus": [
|
||||
"error",
|
||||
{
|
||||
"allowForLoopAfterthoughts": true
|
||||
}
|
||||
],
|
||||
"no-redeclare": "off",
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
"ForInStatement",
|
||||
"WithStatement",
|
||||
"MemberExpression[optional=true]",
|
||||
"IfStatement[alternate.type='IfStatement']",
|
||||
"UnaryExpression[operator='!']",
|
||||
"BinaryExpression[operator='==='][right.value=true]"
|
||||
],
|
||||
"no-return-await": "error",
|
||||
"no-sequences": "error",
|
||||
"no-shadow": "off",
|
||||
"@typescript-eslint/no-redeclare": [
|
||||
"error"
|
||||
],
|
||||
"@typescript-eslint/no-shadow": [
|
||||
"error"
|
||||
],
|
||||
"no-sparse-arrays": "error",
|
||||
"no-template-curly-in-string": "error",
|
||||
"no-throw-literal": "error",
|
||||
"no-trailing-spaces": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-underscore-dangle": "error",
|
||||
"no-unsafe-finally": "error",
|
||||
"no-unused-expressions": "error",
|
||||
"no-unused-labels": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"no-var": "error",
|
||||
"no-void": "error",
|
||||
"max-params": [
|
||||
"error",
|
||||
4
|
||||
],
|
||||
"object-shorthand": "error",
|
||||
"one-var": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"prefer-const": "error",
|
||||
"prefer-object-spread": "error",
|
||||
"@typescript-eslint/prefer-readonly": "error",
|
||||
"quote-props": [
|
||||
"error",
|
||||
"consistent-as-needed"
|
||||
],
|
||||
"radix": "error",
|
||||
"space-before-function-paren": [
|
||||
"error",
|
||||
{
|
||||
"anonymous": "never",
|
||||
"asyncArrow": "always",
|
||||
"named": "never"
|
||||
}
|
||||
],
|
||||
"spaced-comment": [
|
||||
"error",
|
||||
"always",
|
||||
{
|
||||
"markers": [
|
||||
"/"
|
||||
]
|
||||
}
|
||||
],
|
||||
"use-isnan": "error",
|
||||
"valid-typeof": "off",
|
||||
"sonarjs/max-switch-cases": "error",
|
||||
"sonarjs/cognitive-complexity": "error",
|
||||
"sonarjs/no-all-duplicated-branches": "error",
|
||||
"sonarjs/no-collapsible-if": "error",
|
||||
"sonarjs/no-collection-size-mischeck": "error",
|
||||
"sonarjs/no-duplicate-string": "error",
|
||||
"sonarjs/no-duplicated-branches": "error",
|
||||
"sonarjs/no-element-overwrite": "error",
|
||||
"sonarjs/no-identical-conditions": "error",
|
||||
"sonarjs/no-identical-expressions": "error",
|
||||
"sonarjs/no-identical-functions": "error",
|
||||
"sonarjs/no-inverted-boolean-check": "error",
|
||||
"sonarjs/no-redundant-boolean": "error",
|
||||
"sonarjs/no-redundant-jump": "error",
|
||||
"sonarjs/no-same-line-conditional": "error",
|
||||
"sonarjs/no-useless-catch": "error",
|
||||
"sonarjs/prefer-immediate-return": "error"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"reporter": "@mochajs/multi-reporter",
|
||||
"ui": ["bdd"]
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
.awcache/
|
||||
.github/
|
||||
.nyc_output/
|
||||
|
||||
config/
|
||||
deploy/
|
||||
node_modules/
|
||||
out/cache/
|
||||
out/coverage/
|
||||
out/coverage-*
|
||||
out/docs/
|
||||
out/test/
|
||||
out/typings/
|
||||
out/*.db
|
||||
out/*.html
|
||||
out/*.json
|
||||
out/test-*
|
||||
out/tmp/
|
||||
scripts/
|
||||
src/
|
||||
temp/
|
||||
test/
|
||||
vendor/
|
||||
|
||||
.api-extractor.json
|
||||
.codeclimate.yml
|
||||
.dockerignore
|
||||
.eslintrc.json
|
||||
.mocharc.json
|
||||
.reporters.json
|
||||
|
||||
Containerfile.*
|
||||
Makefile
|
||||
esbuild.js
|
||||
serve.js
|
||||
tsconfig.json
|
||||
yarn-*
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"mocha-junit-reporter": true,
|
||||
"spec": true
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
.PHONY: build bundle ci clean docs docs-local lint package run test
|
||||
|
||||
# JS targets
|
||||
node_modules: deps
|
||||
|
||||
ci: deps lint build-shebang test bundle
|
||||
|
||||
clean:
|
||||
rm -rf node_modules/
|
||||
rm -rf out/
|
||||
|
||||
deps:
|
||||
yarn install
|
||||
|
||||
docs:
|
||||
yarn api-extractor run -c .api-extractor.json
|
||||
yarn api-documenter markdown -i out/api -o docs/api
|
||||
|
||||
docs-local:
|
||||
yarn api-extractor run -c .api-extractor.json --local
|
||||
yarn api-documenter markdown -i out/api -o docs/api
|
||||
|
||||
build: deps
|
||||
yarn tsc
|
||||
# cp -v src/components/main.css out/src/components/
|
||||
|
||||
build-shebang: build
|
||||
sed -i '1s;^;#! /usr/bin/env node\n\n;g' $(shell pwd)/out/src/main.js
|
||||
chmod ug+x out/src/main.js
|
||||
|
||||
bundle: build
|
||||
node esbuild.js
|
||||
# copy other files into output to make a complete UI
|
||||
cp -v src/index.html out/
|
||||
# cp -v src/config.json out/
|
||||
# copy everything into the server's default path
|
||||
# cp -v src/index.html ../api/gui/
|
||||
# cp -v src/config.json ../api/gui/
|
||||
# cp -v out/bundle/main.css ../api/gui/bundle/
|
||||
# cp -v out/bundle/main.js ../api/gui/bundle/
|
||||
|
||||
COVER_OPTS := --all \
|
||||
--exclude ".eslintrc.js" \
|
||||
--exclude "docs/**" \
|
||||
--exclude "out/bundle/**" \
|
||||
--exclude "out/coverage/**" \
|
||||
--exclude "vendor/**" \
|
||||
--reporter=text-summary \
|
||||
--reporter=lcov \
|
||||
--reporter=cobertura \
|
||||
--report-dir=out/coverage
|
||||
|
||||
MOCHA_OPTS := --async-only \
|
||||
--check-leaks \
|
||||
--forbid-only \
|
||||
--recursive \
|
||||
--require source-map-support/register \
|
||||
--require out/test/setup.js \
|
||||
--sort
|
||||
|
||||
lint: deps
|
||||
yarn eslint src/ test/ --ext .ts,.tsx
|
||||
|
||||
test: build
|
||||
MOCHA_FILE=out/test-results.xml yarn c8 $(COVER_OPTS) mocha $(MOCHA_OPTS) "out/**/Test*.js"
|
||||
|
||||
watch: deps
|
||||
WATCH=TRUE make ci
|
||||
|
||||
serve:
|
||||
node serve.js
|
|
@ -0,0 +1,75 @@
|
|||
import { build, context } from 'esbuild';
|
||||
import { join } from 'path';
|
||||
import alias from 'esbuild-plugin-alias';
|
||||
import { copy } from 'esbuild-plugin-copy';
|
||||
|
||||
function envTrue(key) {
|
||||
const val = (process.env[key] || '').toLowerCase();
|
||||
return val === '1' || val === 't' || val === 'true' || val === 'y' || val === 'yes';
|
||||
}
|
||||
|
||||
const debug = envTrue('DEBUG');
|
||||
const watch = envTrue('WATCH');
|
||||
const root = process.cwd();
|
||||
|
||||
const plugins = [];
|
||||
|
||||
if (debug) {
|
||||
plugins.push(alias({
|
||||
'react-dom$': 'react-dom/profiling',
|
||||
'scheduler/tracing': 'scheduler/tracing-profiling',
|
||||
}));
|
||||
}
|
||||
|
||||
const config = {
|
||||
bundle: true,
|
||||
define: {
|
||||
global: 'window',
|
||||
},
|
||||
entryPoints: [
|
||||
join(root, 'out/src/main.js'),
|
||||
],
|
||||
keepNames: true,
|
||||
outdir: 'out/bundle/',
|
||||
platform: 'browser',
|
||||
plugins,
|
||||
sourcemap: true,
|
||||
};
|
||||
|
||||
if (watch) {
|
||||
const copyArray = (files) => files.map(file =>
|
||||
copy({
|
||||
resolveFrom: 'cwd',
|
||||
assets: {
|
||||
from: [file],
|
||||
to: ['out/'],
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const ctx = await context({
|
||||
...config,
|
||||
entryPoints: [
|
||||
join(root, 'src/main.tsx'),
|
||||
],
|
||||
plugins: [
|
||||
...plugins,
|
||||
...copyArray(['src/index.html']),
|
||||
],
|
||||
banner: {
|
||||
js: `new EventSource('/esbuild').addEventListener('change', () => location.reload());`,
|
||||
},
|
||||
});
|
||||
|
||||
await ctx.watch();
|
||||
|
||||
const { host, port } = await ctx.serve({
|
||||
host: '0.0.0.0',
|
||||
port: 8000,
|
||||
servedir: 'out/',
|
||||
});
|
||||
|
||||
console.log(`Serving on http://${host}:${port}`);
|
||||
} else {
|
||||
build(config).catch(() => process.exit(1));
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"name": "@apextoaster/onnx-web",
|
||||
"version": "0.12.0",
|
||||
"description": "onnx web gui",
|
||||
"type": "module",
|
||||
"main": "out/src/main.js",
|
||||
"author": "ssube",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apextoaster/js-utils": "^0.5.0",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@mui/icons-material": "^5.15.16",
|
||||
"@mui/lab": "^5.0.0-alpha.170",
|
||||
"@mui/material": "^5.15.16",
|
||||
"@mui/x-tree-view": "^7.3.1",
|
||||
"@types/lodash": "^4.14.192",
|
||||
"@types/node": "^20.11.0",
|
||||
"browser-bunyan": "^1.8.0",
|
||||
"i18next": "^22.4.14",
|
||||
"i18next-browser-languagedetector": "^7.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"noicejs": "^5.0.0-3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^12.2.0",
|
||||
"react-use": "^17.4.3",
|
||||
"react-use-websocket": "^4.8.1",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mochajs/multi-reporter": "^1.1.0",
|
||||
"@types/chai-as-promised": "^7.1.5",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/react": "^18.3.1",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"@types/sinon-chai": "^3.2.9",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
||||
"@typescript-eslint/parser": "^5.59.0",
|
||||
"c8": "^7.13.0",
|
||||
"chai": "^4.3.7",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"esbuild": "^0.17.17",
|
||||
"esbuild-plugin-alias": "^0.2.1",
|
||||
"esbuild-plugin-copy": "^2.1.0",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-plugin-chai": "^0.0.1",
|
||||
"eslint-plugin-chai-expect": "^3.0.0",
|
||||
"eslint-plugin-chai-expect-keywords": "^2.1.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-mocha": "^10.1.0",
|
||||
"eslint-plugin-no-null": "^1.0.2",
|
||||
"eslint-plugin-sonarjs": "^0.19.0",
|
||||
"mocha": "^10.2.0",
|
||||
"mocha-junit-reporter": "^2.2.0",
|
||||
"sinon": "^15.0.4",
|
||||
"sinon-chai": "^3.7.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "18.3.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { mustDefault } from '@apextoaster/js-utils';
|
||||
import { readFile } from 'fs';
|
||||
import { createServer } from 'http';
|
||||
import { join } from 'path';
|
||||
|
||||
const { env } = process;
|
||||
|
||||
const host = mustDefault(env.ONNX_WEB_DEV_HOST, '127.0.0.1');
|
||||
const port = mustDefault(env.ONNX_WEB_DEV_PORT, '8000');
|
||||
const root = process.cwd();
|
||||
|
||||
const portNum = parseInt(port, 10);
|
||||
|
||||
const contentTypes = [
|
||||
[/^.*\.html$/, 'text/html'],
|
||||
[/^.*\.js$/, 'application/javascript'],
|
||||
[/^.*\.json$/, 'text/json'],
|
||||
];
|
||||
|
||||
function getContentType(path) {
|
||||
for (const [regex, type] of contentTypes) {
|
||||
if (regex.test(path)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
const path = join(root, 'out', req.url || 'index.html');
|
||||
readFile(path, function (err, data) {
|
||||
if (err) {
|
||||
res.writeHead(404);
|
||||
res.end(JSON.stringify(err));
|
||||
return;
|
||||
}
|
||||
res.writeHead(200, {
|
||||
'Content-Type': getContentType(path),
|
||||
});
|
||||
res.end(data);
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(portNum, host, () => {
|
||||
console.log(`Dev server running at http://${host}:${port}/index.html`);
|
||||
});
|
|
@ -0,0 +1,247 @@
|
|||
/* eslint-disable no-restricted-syntax */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import useWebSocketModule, { ReadyState } from 'react-use-websocket';
|
||||
import { Maybe, Optional, doesExist } from '@apextoaster/js-utils';
|
||||
import List from '@mui/material/List';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import ListItemAvatar from '@mui/material/ListItemAvatar';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Container from '@mui/material/Container';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import Switch from '@mui/material/Switch';
|
||||
import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
|
||||
import { TreeItem } from '@mui/x-tree-view/TreeItem';
|
||||
import { CssBaseline, PaletteMode, ThemeProvider, createTheme } from '@mui/material';
|
||||
|
||||
import { formatters } from './format.js';
|
||||
|
||||
const useWebSocket = (useWebSocketModule as any).default;
|
||||
|
||||
export interface EventItemProps {
|
||||
event: any;
|
||||
}
|
||||
|
||||
const statusStrings = {
|
||||
[ReadyState.CONNECTING]: 'Connecting',
|
||||
[ReadyState.OPEN]: 'Running',
|
||||
[ReadyState.CLOSING]: 'Closing',
|
||||
[ReadyState.CLOSED]: 'Closed',
|
||||
[ReadyState.UNINSTANTIATED]: 'Unready',
|
||||
};
|
||||
|
||||
export function interleave(arr: Array<any>) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
||||
return arr.reduce((acc, val, idx) => acc.concat(val, <Divider component='li' key={`sep-${idx}`} variant='inset' />), []).slice(0, -1);
|
||||
}
|
||||
|
||||
export function ActionItem(props: EventItemProps) {
|
||||
const { event } = props;
|
||||
const { actor, room, type } = event;
|
||||
const content = formatters[type](event);
|
||||
|
||||
return <ListItem alignItems="flex-start">
|
||||
<ListItemAvatar>
|
||||
<Avatar alt={actor} src="/static/images/avatar/1.jpg" />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={room}
|
||||
secondary={
|
||||
<React.Fragment>
|
||||
<Typography
|
||||
sx={{ display: 'block' }}
|
||||
component="span"
|
||||
variant="body2"
|
||||
color="text.primary"
|
||||
>
|
||||
{actor}
|
||||
</Typography>
|
||||
{content}
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
</ListItem>;
|
||||
}
|
||||
|
||||
export function WorldItem(props: EventItemProps) {
|
||||
const { event } = props;
|
||||
const { step, world } = event;
|
||||
const { theme } = world;
|
||||
|
||||
return <ListItem alignItems="flex-start">
|
||||
<ListItemAvatar>
|
||||
<Avatar alt={step} src="/static/images/avatar/1.jpg" />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={theme}
|
||||
secondary={
|
||||
<Typography
|
||||
sx={{ display: 'block' }}
|
||||
component="span"
|
||||
variant="body2"
|
||||
color="text.primary"
|
||||
>
|
||||
Step {step}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</ListItem>;
|
||||
}
|
||||
|
||||
export function MessageItem(props: EventItemProps) {
|
||||
const { event } = props;
|
||||
const { message } = event;
|
||||
|
||||
return <ListItem alignItems="flex-start">
|
||||
<ListItemAvatar>
|
||||
<Avatar alt="System" src="/static/images/avatar/1.jpg" />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary="System"
|
||||
secondary={
|
||||
<Typography
|
||||
sx={{ display: 'block' }}
|
||||
component="span"
|
||||
variant="body2"
|
||||
color="text.primary"
|
||||
>
|
||||
{message}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</ListItem>;
|
||||
}
|
||||
|
||||
export function EventItem(props: EventItemProps) {
|
||||
const { event } = props;
|
||||
const { type } = event;
|
||||
|
||||
switch (type) {
|
||||
case 'action':
|
||||
case 'result':
|
||||
return <ActionItem event={event} />;
|
||||
case 'event':
|
||||
return <MessageItem event={event} />;
|
||||
case 'world':
|
||||
return <WorldItem event={event} />;
|
||||
default:
|
||||
return <ListItem>
|
||||
<ListItemText primary={`Unknown event type: ${type}`} />
|
||||
</ListItem>;
|
||||
}
|
||||
}
|
||||
|
||||
export interface AppProps {
|
||||
socketUrl: string;
|
||||
}
|
||||
|
||||
export interface Item {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface Actor {
|
||||
name: string;
|
||||
description: string;
|
||||
items: Array<Item>;
|
||||
}
|
||||
|
||||
export interface Room {
|
||||
name: string;
|
||||
description: string;
|
||||
portals: Record<string, string>;
|
||||
actors: Array<Actor>;
|
||||
items: Array<Item>;
|
||||
}
|
||||
|
||||
export interface World {
|
||||
name: string;
|
||||
order: Array<string>;
|
||||
rooms: Array<Room>;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
export function WorldPanel(props: { world: Maybe<World> }) {
|
||||
const { world } = props;
|
||||
|
||||
return <Stack direction="column">
|
||||
<Typography variant="h4">
|
||||
World: {world?.name}
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
Theme: {world?.theme}
|
||||
</Typography>
|
||||
<SimpleTreeView>
|
||||
{world?.rooms.map((room) => <TreeItem itemId={room.name} label={room.name}>
|
||||
{room.actors.map((actor) => <TreeItem itemId={actor.name} label={actor.name}>
|
||||
{actor.items.map((item) => <TreeItem itemId={item.name} label={item.name} />
|
||||
)}
|
||||
</TreeItem>
|
||||
)}
|
||||
{room.items.map((item) => <TreeItem itemId={item.name} label={item.name} />
|
||||
)}
|
||||
</TreeItem>
|
||||
)}
|
||||
</SimpleTreeView>
|
||||
</Stack>;
|
||||
}
|
||||
|
||||
export function App(props: AppProps) {
|
||||
const [ world, setWorld ] = useState<Maybe<World>>(undefined);
|
||||
const [ themeMode, setThemeMode ] = useState('light');
|
||||
const [ history, setHistory ] = useState<Array<string>>([]);
|
||||
const { lastMessage, readyState } = useWebSocket(props.socketUrl);
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
mode: themeMode as PaletteMode,
|
||||
},
|
||||
});
|
||||
|
||||
const connectionStatus = statusStrings[readyState as ReadyState];
|
||||
|
||||
useEffect(() => {
|
||||
if (doesExist(lastMessage)) {
|
||||
const data = JSON.parse(lastMessage.data);
|
||||
setHistory((prev) => prev.concat(data));
|
||||
|
||||
// if we get a world event, update the last world state
|
||||
if (data.type === 'world') {
|
||||
setWorld(data.world);
|
||||
}
|
||||
}
|
||||
}, [lastMessage]);
|
||||
|
||||
const items = history.map((item, index) => <EventItem key={`item-${index}`} event={item} />);
|
||||
|
||||
return <ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Container maxWidth='lg'>
|
||||
<Stack direction="row">
|
||||
<WorldPanel world={world} />
|
||||
<Stack direction="column">
|
||||
<Alert icon={false} severity="success">
|
||||
<Stack direction="row" alignItems="center" gap={4}>
|
||||
<Typography>
|
||||
Status: {connectionStatus}
|
||||
</Typography>
|
||||
<Switch
|
||||
checked={themeMode === 'dark'}
|
||||
onChange={() => setThemeMode(themeMode === 'dark' ? 'light' : 'dark')}
|
||||
inputProps={{ 'aria-label': 'controlled' }}
|
||||
sx={{ marginLeft: 'auto' }}
|
||||
/>
|
||||
</Stack>
|
||||
</Alert>
|
||||
<List sx={{ width: '100%', bgcolor: 'background.paper' }}>
|
||||
{interleave(items)}
|
||||
</List>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Container>
|
||||
</ThemeProvider>;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export function formatActionName(name: string) {
|
||||
const shortName = name.replace('action_', '');
|
||||
return shortName[0].toUpperCase() + shortName.substring(1).toLowerCase();
|
||||
}
|
||||
|
||||
export function formatAction(data: any) {
|
||||
const actionName = formatActionName(data.function);
|
||||
const actionParameters = data.parameters;
|
||||
|
||||
return `Action: ${actionName} - ${Object.entries(actionParameters).map(([key, value]) => `${key}: ${value}`).join(', ')}`;
|
||||
}
|
||||
|
||||
export function formatInput(data: any) {
|
||||
const action = formatAction(JSON.parse(data.input));
|
||||
return `Starting turn: ${action}`;
|
||||
}
|
||||
|
||||
export function formatResult(data: any) {
|
||||
return `Turn result: ${data.result}`;
|
||||
}
|
||||
|
||||
export function formatWorld(data: any) {
|
||||
return `${data.world.theme} - ${data.step}`;
|
||||
}
|
||||
|
||||
export const formatters: Record<string, any> = {
|
||||
input: formatInput,
|
||||
result: formatResult,
|
||||
world: formatWorld,
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Text World</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="history">
|
||||
<ol id="history"></ol>
|
||||
</div>
|
||||
<script src="./bundle/main.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
import { doesExist } from '@apextoaster/js-utils';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import React from 'react';
|
||||
|
||||
import { App } from './app.js';
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const history = document.querySelector('#history');
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
if (!doesExist(history)) {
|
||||
throw new Error('History element not found');
|
||||
}
|
||||
|
||||
const hostname = window.location.hostname;
|
||||
const root = createRoot(history);
|
||||
root.render(<App socketUrl={`ws://${hostname}:8001/`} />);
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
/// <reference types="node" />
|
||||
|
||||
import chai from 'chai';
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
import sinonChai from 'sinon-chai';
|
||||
|
||||
export function setupTests(): void {
|
||||
/**
|
||||
* This will break the whole test run if any test leaks an unhandled rejection.
|
||||
*/
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
/* c8 ignore next 3 */
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('unhandled error during tests', reason);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
chai.use(sinonChai);
|
||||
}
|
||||
|
||||
setupTests();
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"allowJs": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"jsx": "react",
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ES2018"
|
||||
],
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"outDir": "out",
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"target": "es2017",
|
||||
"types": [
|
||||
"chai-as-promised",
|
||||
"mocha",
|
||||
"sinon-chai"
|
||||
],
|
||||
"typeRoots": [
|
||||
"node_modules/@types",
|
||||
"node_modules",
|
||||
"vendor"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"test/**/*"
|
||||
]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"version": 1,
|
||||
"formatters": {
|
||||
"simple": {
|
||||
"format": "[%(asctime)s] %(levelname)s: %(processName)s %(threadName)s %(name)s: %(message)s"
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"level": "INFO",
|
||||
"formatter": "simple",
|
||||
"stream": "ext://sys.stdout"
|
||||
}
|
||||
},
|
||||
"loggers": {
|
||||
"": {
|
||||
"level": "INFO",
|
||||
"handlers": [
|
||||
"console"
|
||||
],
|
||||
"propagate": true
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"level": "INFO",
|
||||
"handlers": [
|
||||
"console"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
langchain-core==0.1.50
|
||||
packit-llm==0.1.0
|
||||
pydantic==2.7.1
|
||||
pydantic_core==2.18.2
|
||||
python-dotenv==1.0.1
|
||||
PyYAML==6.0.1
|
|
@ -0,0 +1,27 @@
|
|||
# build
|
||||
build
|
||||
twine
|
||||
wheel
|
||||
|
||||
# debug
|
||||
debugpy
|
||||
|
||||
# docs
|
||||
mkdocs
|
||||
mdx-truly-sane-lists
|
||||
|
||||
# lint
|
||||
black
|
||||
flake8
|
||||
isort
|
||||
mypy
|
||||
|
||||
# testing
|
||||
coverage
|
||||
hypothesis
|
||||
|
||||
# types
|
||||
types-Flask-Cors
|
||||
types-jsonschema
|
||||
types-Pillow
|
||||
types-PyYAML
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": "./client/tsconfig.json",
|
||||
}
|
Loading…
Reference in New Issue