mirror of
https://git.sr.ht/~ashkeel/strimertul
synced 2024-09-20 02:00:49 +00:00
feat: extension list mostly works
This commit is contained in:
parent
b3dea0fe53
commit
196ab184e6
9 changed files with 492 additions and 180 deletions
222
frontend/package-lock.json
generated
222
frontend/package-lock.json
generated
|
@ -23,9 +23,9 @@
|
||||||
"@radix-ui/react-toggle-group": "^1.0.2",
|
"@radix-ui/react-toggle-group": "^1.0.2",
|
||||||
"@radix-ui/react-toolbar": "^1.0.2",
|
"@radix-ui/react-toolbar": "^1.0.2",
|
||||||
"@redux-devtools/extension": "^3.2.5",
|
"@redux-devtools/extension": "^3.2.5",
|
||||||
"@reduxjs/toolkit": "^1.9.1",
|
"@reduxjs/toolkit": "^1.9.2",
|
||||||
"@stitches/react": "^1.2.8",
|
"@stitches/react": "^1.2.8",
|
||||||
"@strimertul/kilovolt-client": "^7.0.1",
|
"@strimertul/kilovolt-client": "^7.1.0",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^18.11.18",
|
||||||
"@types/react": "^18.0.27",
|
"@types/react": "^18.0.27",
|
||||||
"@types/react-dom": "^18.0.10",
|
"@types/react-dom": "^18.0.10",
|
||||||
|
@ -41,14 +41,14 @@
|
||||||
"react-router-dom": "^6.8.0",
|
"react-router-dom": "^6.8.0",
|
||||||
"redux-thunk": "^2.4.2",
|
"redux-thunk": "^2.4.2",
|
||||||
"sass": "^1.57.1",
|
"sass": "^1.57.1",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^4.9.5",
|
||||||
"vite": "^4.0.4",
|
"vite": "^4.0.4",
|
||||||
"vite-tsconfig-paths": "^4.0.5"
|
"vite-tsconfig-paths": "^4.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
"@typescript-eslint/eslint-plugin": "^5.50.0",
|
||||||
"@typescript-eslint/parser": "^5.49.0",
|
"@typescript-eslint/parser": "^5.50.0",
|
||||||
"eslint": "^8.32.0",
|
"eslint": "^8.33.0",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
"eslint-config-prettier": "^8.6.0",
|
"eslint-config-prettier": "^8.6.0",
|
||||||
"eslint-import-resolver-typescript": "^3.5.3",
|
"eslint-import-resolver-typescript": "^3.5.3",
|
||||||
|
@ -1470,9 +1470,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@reduxjs/toolkit": {
|
"node_modules/@reduxjs/toolkit": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.2.tgz",
|
||||||
"integrity": "sha512-HikrdY+IDgRfRYlCTGUQaiCxxDDgM1mQrRbZ6S1HFZX5ZYuJ4o8EstNmhTwHdPl2rTmLxzwSu0b3AyeyTlR+RA==",
|
"integrity": "sha512-5ZAZ7hwAKWSii5T6NTPmgIBUqyVdlDs+6JjThz6J6dmHLDm6zCzv2OjHIFAi3Vvs1qjmXU0bm6eBojukYXjVMQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"immer": "^9.0.16",
|
"immer": "^9.0.16",
|
||||||
"redux": "^4.2.0",
|
"redux": "^4.2.0",
|
||||||
|
@ -1509,9 +1509,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@strimertul/kilovolt-client": {
|
"node_modules/@strimertul/kilovolt-client": {
|
||||||
"version": "7.0.1",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@strimertul/kilovolt-client/-/kilovolt-client-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@strimertul/kilovolt-client/-/kilovolt-client-7.1.0.tgz",
|
||||||
"integrity": "sha512-G9xLgufk1aRmrzUnxZeQHbSLewEy199WAox4zGolWf9GdlKREKcfcLYWq/idxvTacRusRuex2Z7+Gl/lXh7Swg=="
|
"integrity": "sha512-iZxCVRrJZNsnu6N2T/G83lwt3UBOHD0fCzaJRQsTBg6Ybdc8oi9v5/AqgZfA+mlif/iL0X5gkukKzSa1nAmg/w=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/hoist-non-react-statics": {
|
"node_modules/@types/hoist-non-react-statics": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
|
@ -1579,15 +1579,16 @@
|
||||||
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
|
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.50.0.tgz",
|
||||||
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
|
"integrity": "sha512-vwksQWSFZiUhgq3Kv7o1Jcj0DUNylwnIlGvKvLLYsq8pAWha6/WCnXUeaSoNNha/K7QSf2+jvmkxggC1u3pIwQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "5.49.0",
|
"@typescript-eslint/scope-manager": "5.50.0",
|
||||||
"@typescript-eslint/type-utils": "5.49.0",
|
"@typescript-eslint/type-utils": "5.50.0",
|
||||||
"@typescript-eslint/utils": "5.49.0",
|
"@typescript-eslint/utils": "5.50.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
|
"grapheme-splitter": "^1.0.4",
|
||||||
"ignore": "^5.2.0",
|
"ignore": "^5.2.0",
|
||||||
"natural-compare-lite": "^1.4.0",
|
"natural-compare-lite": "^1.4.0",
|
||||||
"regexpp": "^3.2.0",
|
"regexpp": "^3.2.0",
|
||||||
|
@ -1612,14 +1613,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.50.0.tgz",
|
||||||
"integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==",
|
"integrity": "sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "5.49.0",
|
"@typescript-eslint/scope-manager": "5.50.0",
|
||||||
"@typescript-eslint/types": "5.49.0",
|
"@typescript-eslint/types": "5.50.0",
|
||||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
"@typescript-eslint/typescript-estree": "5.50.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1639,13 +1640,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.50.0.tgz",
|
||||||
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
|
"integrity": "sha512-rt03kaX+iZrhssaT974BCmoUikYtZI24Vp/kwTSy841XhiYShlqoshRFDvN1FKKvU2S3gK+kcBW1EA7kNUrogg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "5.49.0",
|
"@typescript-eslint/types": "5.50.0",
|
||||||
"@typescript-eslint/visitor-keys": "5.49.0"
|
"@typescript-eslint/visitor-keys": "5.50.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
|
@ -1656,13 +1657,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.50.0.tgz",
|
||||||
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
|
"integrity": "sha512-dcnXfZ6OGrNCO7E5UY/i0ktHb7Yx1fV6fnQGGrlnfDhilcs6n19eIRcvLBqx6OQkrPaFlDPk3OJ0WlzQfrV0bQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
"@typescript-eslint/typescript-estree": "5.50.0",
|
||||||
"@typescript-eslint/utils": "5.49.0",
|
"@typescript-eslint/utils": "5.50.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"tsutils": "^3.21.0"
|
"tsutils": "^3.21.0"
|
||||||
},
|
},
|
||||||
|
@ -1683,9 +1684,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.50.0.tgz",
|
||||||
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==",
|
"integrity": "sha512-atruOuJpir4OtyNdKahiHZobPKFvZnBnfDiyEaBf6d9vy9visE7gDjlmhl+y29uxZ2ZDgvXijcungGFjGGex7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
|
@ -1696,13 +1697,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.50.0.tgz",
|
||||||
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
|
"integrity": "sha512-Gq4zapso+OtIZlv8YNAStFtT6d05zyVCK7Fx3h5inlLBx2hWuc/0465C2mg/EQDDU2LKe52+/jN4f0g9bd+kow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "5.49.0",
|
"@typescript-eslint/types": "5.50.0",
|
||||||
"@typescript-eslint/visitor-keys": "5.49.0",
|
"@typescript-eslint/visitor-keys": "5.50.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"globby": "^11.1.0",
|
"globby": "^11.1.0",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
|
@ -1723,16 +1724,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.50.0.tgz",
|
||||||
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
|
"integrity": "sha512-v/AnUFImmh8G4PH0NDkf6wA8hujNNcrwtecqW4vtQ1UOSNBaZl49zP1SHoZ/06e+UiwzHpgb5zP5+hwlYYWYAw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/json-schema": "^7.0.9",
|
"@types/json-schema": "^7.0.9",
|
||||||
"@types/semver": "^7.3.12",
|
"@types/semver": "^7.3.12",
|
||||||
"@typescript-eslint/scope-manager": "5.49.0",
|
"@typescript-eslint/scope-manager": "5.50.0",
|
||||||
"@typescript-eslint/types": "5.49.0",
|
"@typescript-eslint/types": "5.50.0",
|
||||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
"@typescript-eslint/typescript-estree": "5.50.0",
|
||||||
"eslint-scope": "^5.1.1",
|
"eslint-scope": "^5.1.1",
|
||||||
"eslint-utils": "^3.0.0",
|
"eslint-utils": "^3.0.0",
|
||||||
"semver": "^7.3.7"
|
"semver": "^7.3.7"
|
||||||
|
@ -1749,12 +1750,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.50.0.tgz",
|
||||||
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
|
"integrity": "sha512-cdMeD9HGu6EXIeGOh2yVW6oGf9wq8asBgZx7nsR/D36gTfQ0odE5kcRYe5M81vjEFAcPeugXrHg78Imu55F6gg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "5.49.0",
|
"@typescript-eslint/types": "5.50.0",
|
||||||
"eslint-visitor-keys": "^3.3.0"
|
"eslint-visitor-keys": "^3.3.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -2412,9 +2413,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "8.32.0",
|
"version": "8.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz",
|
||||||
"integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==",
|
"integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/eslintrc": "^1.4.1",
|
"@eslint/eslintrc": "^1.4.1",
|
||||||
|
@ -4890,9 +4891,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.9.4",
|
"version": "4.9.5",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
@ -6079,9 +6080,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@reduxjs/toolkit": {
|
"@reduxjs/toolkit": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.2.tgz",
|
||||||
"integrity": "sha512-HikrdY+IDgRfRYlCTGUQaiCxxDDgM1mQrRbZ6S1HFZX5ZYuJ4o8EstNmhTwHdPl2rTmLxzwSu0b3AyeyTlR+RA==",
|
"integrity": "sha512-5ZAZ7hwAKWSii5T6NTPmgIBUqyVdlDs+6JjThz6J6dmHLDm6zCzv2OjHIFAi3Vvs1qjmXU0bm6eBojukYXjVMQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"immer": "^9.0.16",
|
"immer": "^9.0.16",
|
||||||
"redux": "^4.2.0",
|
"redux": "^4.2.0",
|
||||||
|
@ -6101,9 +6102,9 @@
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"@strimertul/kilovolt-client": {
|
"@strimertul/kilovolt-client": {
|
||||||
"version": "7.0.1",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@strimertul/kilovolt-client/-/kilovolt-client-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@strimertul/kilovolt-client/-/kilovolt-client-7.1.0.tgz",
|
||||||
"integrity": "sha512-G9xLgufk1aRmrzUnxZeQHbSLewEy199WAox4zGolWf9GdlKREKcfcLYWq/idxvTacRusRuex2Z7+Gl/lXh7Swg=="
|
"integrity": "sha512-iZxCVRrJZNsnu6N2T/G83lwt3UBOHD0fCzaJRQsTBg6Ybdc8oi9v5/AqgZfA+mlif/iL0X5gkukKzSa1nAmg/w=="
|
||||||
},
|
},
|
||||||
"@types/hoist-non-react-statics": {
|
"@types/hoist-non-react-statics": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
|
@ -6171,15 +6172,16 @@
|
||||||
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
|
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
|
||||||
},
|
},
|
||||||
"@typescript-eslint/eslint-plugin": {
|
"@typescript-eslint/eslint-plugin": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.50.0.tgz",
|
||||||
"integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==",
|
"integrity": "sha512-vwksQWSFZiUhgq3Kv7o1Jcj0DUNylwnIlGvKvLLYsq8pAWha6/WCnXUeaSoNNha/K7QSf2+jvmkxggC1u3pIwQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/scope-manager": "5.49.0",
|
"@typescript-eslint/scope-manager": "5.50.0",
|
||||||
"@typescript-eslint/type-utils": "5.49.0",
|
"@typescript-eslint/type-utils": "5.50.0",
|
||||||
"@typescript-eslint/utils": "5.49.0",
|
"@typescript-eslint/utils": "5.50.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
|
"grapheme-splitter": "^1.0.4",
|
||||||
"ignore": "^5.2.0",
|
"ignore": "^5.2.0",
|
||||||
"natural-compare-lite": "^1.4.0",
|
"natural-compare-lite": "^1.4.0",
|
||||||
"regexpp": "^3.2.0",
|
"regexpp": "^3.2.0",
|
||||||
|
@ -6188,53 +6190,53 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/parser": {
|
"@typescript-eslint/parser": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.50.0.tgz",
|
||||||
"integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==",
|
"integrity": "sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/scope-manager": "5.49.0",
|
"@typescript-eslint/scope-manager": "5.50.0",
|
||||||
"@typescript-eslint/types": "5.49.0",
|
"@typescript-eslint/types": "5.50.0",
|
||||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
"@typescript-eslint/typescript-estree": "5.50.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/scope-manager": {
|
"@typescript-eslint/scope-manager": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.50.0.tgz",
|
||||||
"integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==",
|
"integrity": "sha512-rt03kaX+iZrhssaT974BCmoUikYtZI24Vp/kwTSy841XhiYShlqoshRFDvN1FKKvU2S3gK+kcBW1EA7kNUrogg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "5.49.0",
|
"@typescript-eslint/types": "5.50.0",
|
||||||
"@typescript-eslint/visitor-keys": "5.49.0"
|
"@typescript-eslint/visitor-keys": "5.50.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/type-utils": {
|
"@typescript-eslint/type-utils": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.50.0.tgz",
|
||||||
"integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==",
|
"integrity": "sha512-dcnXfZ6OGrNCO7E5UY/i0ktHb7Yx1fV6fnQGGrlnfDhilcs6n19eIRcvLBqx6OQkrPaFlDPk3OJ0WlzQfrV0bQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
"@typescript-eslint/typescript-estree": "5.50.0",
|
||||||
"@typescript-eslint/utils": "5.49.0",
|
"@typescript-eslint/utils": "5.50.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"tsutils": "^3.21.0"
|
"tsutils": "^3.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/types": {
|
"@typescript-eslint/types": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.50.0.tgz",
|
||||||
"integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==",
|
"integrity": "sha512-atruOuJpir4OtyNdKahiHZobPKFvZnBnfDiyEaBf6d9vy9visE7gDjlmhl+y29uxZ2ZDgvXijcungGFjGGex7w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@typescript-eslint/typescript-estree": {
|
"@typescript-eslint/typescript-estree": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.50.0.tgz",
|
||||||
"integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==",
|
"integrity": "sha512-Gq4zapso+OtIZlv8YNAStFtT6d05zyVCK7Fx3h5inlLBx2hWuc/0465C2mg/EQDDU2LKe52+/jN4f0g9bd+kow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "5.49.0",
|
"@typescript-eslint/types": "5.50.0",
|
||||||
"@typescript-eslint/visitor-keys": "5.49.0",
|
"@typescript-eslint/visitor-keys": "5.50.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"globby": "^11.1.0",
|
"globby": "^11.1.0",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
|
@ -6243,28 +6245,28 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/utils": {
|
"@typescript-eslint/utils": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.50.0.tgz",
|
||||||
"integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==",
|
"integrity": "sha512-v/AnUFImmh8G4PH0NDkf6wA8hujNNcrwtecqW4vtQ1UOSNBaZl49zP1SHoZ/06e+UiwzHpgb5zP5+hwlYYWYAw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/json-schema": "^7.0.9",
|
"@types/json-schema": "^7.0.9",
|
||||||
"@types/semver": "^7.3.12",
|
"@types/semver": "^7.3.12",
|
||||||
"@typescript-eslint/scope-manager": "5.49.0",
|
"@typescript-eslint/scope-manager": "5.50.0",
|
||||||
"@typescript-eslint/types": "5.49.0",
|
"@typescript-eslint/types": "5.50.0",
|
||||||
"@typescript-eslint/typescript-estree": "5.49.0",
|
"@typescript-eslint/typescript-estree": "5.50.0",
|
||||||
"eslint-scope": "^5.1.1",
|
"eslint-scope": "^5.1.1",
|
||||||
"eslint-utils": "^3.0.0",
|
"eslint-utils": "^3.0.0",
|
||||||
"semver": "^7.3.7"
|
"semver": "^7.3.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@typescript-eslint/visitor-keys": {
|
"@typescript-eslint/visitor-keys": {
|
||||||
"version": "5.49.0",
|
"version": "5.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.50.0.tgz",
|
||||||
"integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==",
|
"integrity": "sha512-cdMeD9HGu6EXIeGOh2yVW6oGf9wq8asBgZx7nsR/D36gTfQ0odE5kcRYe5M81vjEFAcPeugXrHg78Imu55F6gg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/types": "5.49.0",
|
"@typescript-eslint/types": "5.50.0",
|
||||||
"eslint-visitor-keys": "^3.3.0"
|
"eslint-visitor-keys": "^3.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -6729,9 +6731,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"eslint": {
|
"eslint": {
|
||||||
"version": "8.32.0",
|
"version": "8.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz",
|
||||||
"integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==",
|
"integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@eslint/eslintrc": "^1.4.1",
|
"@eslint/eslintrc": "^1.4.1",
|
||||||
|
@ -8449,9 +8451,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.9.4",
|
"version": "4.9.5",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg=="
|
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="
|
||||||
},
|
},
|
||||||
"unbox-primitive": {
|
"unbox-primitive": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
"@radix-ui/react-toggle-group": "^1.0.2",
|
"@radix-ui/react-toggle-group": "^1.0.2",
|
||||||
"@radix-ui/react-toolbar": "^1.0.2",
|
"@radix-ui/react-toolbar": "^1.0.2",
|
||||||
"@redux-devtools/extension": "^3.2.5",
|
"@redux-devtools/extension": "^3.2.5",
|
||||||
"@reduxjs/toolkit": "^1.9.1",
|
"@reduxjs/toolkit": "^1.9.2",
|
||||||
"@stitches/react": "^1.2.8",
|
"@stitches/react": "^1.2.8",
|
||||||
"@strimertul/kilovolt-client": "^7.0.1",
|
"@strimertul/kilovolt-client": "^7.1.0",
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^18.11.18",
|
||||||
"@types/react": "^18.0.27",
|
"@types/react": "^18.0.27",
|
||||||
"@types/react-dom": "^18.0.10",
|
"@types/react-dom": "^18.0.10",
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
"react-router-dom": "^6.8.0",
|
"react-router-dom": "^6.8.0",
|
||||||
"redux-thunk": "^2.4.2",
|
"redux-thunk": "^2.4.2",
|
||||||
"sass": "^1.57.1",
|
"sass": "^1.57.1",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^4.9.5",
|
||||||
"vite": "^4.0.4",
|
"vite": "^4.0.4",
|
||||||
"vite-tsconfig-paths": "^4.0.5"
|
"vite-tsconfig-paths": "^4.0.5"
|
||||||
},
|
},
|
||||||
|
@ -49,9 +49,9 @@
|
||||||
"last 1 Chrome version"
|
"last 1 Chrome version"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
"@typescript-eslint/eslint-plugin": "^5.50.0",
|
||||||
"@typescript-eslint/parser": "^5.49.0",
|
"@typescript-eslint/parser": "^5.50.0",
|
||||||
"eslint": "^8.32.0",
|
"eslint": "^8.33.0",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
"eslint-config-prettier": "^8.6.0",
|
"eslint-config-prettier": "^8.6.0",
|
||||||
"eslint-import-resolver-typescript": "^3.5.3",
|
"eslint-import-resolver-typescript": "^3.5.3",
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
66a652086f52f01615c5cbe3a6b5474e
|
ac94996b5cd655ce090a6545016d312c
|
|
@ -1,9 +1,10 @@
|
||||||
|
import { ExtensionEntry } from '~/store/extensions/reducer';
|
||||||
import {
|
import {
|
||||||
ExtensionStatus,
|
ExtensionStatus,
|
||||||
ExtensionOptions,
|
|
||||||
ExtensionDependencies,
|
ExtensionDependencies,
|
||||||
ExtensionHostMessage,
|
ExtensionHostMessage,
|
||||||
ExtensionHostCommand,
|
ExtensionHostCommand,
|
||||||
|
ExtensionRunOptions,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export const blankTemplate = (slug: string) => `// ==Extension==
|
export const blankTemplate = (slug: string) => `// ==Extension==
|
||||||
|
@ -22,10 +23,9 @@ export class Extension extends EventTarget {
|
||||||
private workerError?: ErrorEvent;
|
private workerError?: ErrorEvent;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly name: string,
|
public readonly info: ExtensionEntry,
|
||||||
public readonly source: string,
|
|
||||||
public readonly options: ExtensionOptions,
|
|
||||||
dependencies: ExtensionDependencies,
|
dependencies: ExtensionDependencies,
|
||||||
|
runOptions: ExtensionRunOptions = { autostart: false },
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -43,14 +43,13 @@ export class Extension extends EventTarget {
|
||||||
// Initialize ext host
|
// Initialize ext host
|
||||||
this.send({
|
this.send({
|
||||||
kind: 'arguments',
|
kind: 'arguments',
|
||||||
source,
|
source: info.source,
|
||||||
options,
|
options: runOptions,
|
||||||
dependencies,
|
dependencies,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private send(cmd: ExtensionHostCommand) {
|
private send(cmd: ExtensionHostCommand) {
|
||||||
console.log(cmd);
|
|
||||||
this.worker.postMessage(cmd);
|
this.worker.postMessage(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +73,13 @@ export class Extension extends EventTarget {
|
||||||
return this.workerError;
|
return this.workerError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get running() {
|
||||||
|
return (
|
||||||
|
this.status === ExtensionStatus.Running ||
|
||||||
|
this.status === ExtensionStatus.Finished
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
switch (this.status) {
|
switch (this.status) {
|
||||||
case ExtensionStatus.Ready:
|
case ExtensionStatus.Ready:
|
||||||
|
@ -94,6 +100,9 @@ export class Extension extends EventTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
|
if (this.workerStatus === ExtensionStatus.Terminated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.worker.terminate();
|
this.worker.terminate();
|
||||||
this.workerStatus = ExtensionStatus.Terminated;
|
this.workerStatus = ExtensionStatus.Terminated;
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
|
|
|
@ -6,9 +6,12 @@ export interface ExtensionDependencies {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtensionOptions {
|
export interface ExtensionOptions {
|
||||||
autostart: boolean;
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface ExtensionRunOptions {}
|
||||||
|
|
||||||
export enum ExtensionStatus {
|
export enum ExtensionStatus {
|
||||||
GettingReady = 'not-ready',
|
GettingReady = 'not-ready',
|
||||||
Ready = 'ready',
|
Ready = 'ready',
|
||||||
|
@ -22,7 +25,7 @@ export type ExtensionHostCommand = EHParamMessage | EHStartMessage;
|
||||||
export type ExtensionHostMessage = EHStatusChangeMessage;
|
export type ExtensionHostMessage = EHStatusChangeMessage;
|
||||||
interface EHParamMessage {
|
interface EHParamMessage {
|
||||||
kind: 'arguments';
|
kind: 'arguments';
|
||||||
options: ExtensionOptions;
|
options: ExtensionRunOptions;
|
||||||
dependencies: ExtensionDependencies;
|
dependencies: ExtensionDependencies;
|
||||||
source: string;
|
source: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,7 @@ onmessage = async (ev: MessageEvent<ExtensionHostCommand>) => {
|
||||||
extFn = ExtensionFunction.constructor('kv', cmd.source);
|
extFn = ExtensionFunction.constructor('kv', cmd.source);
|
||||||
setStatus(ExtensionStatus.Ready);
|
setStatus(ExtensionStatus.Ready);
|
||||||
|
|
||||||
if (cmd.options.autostart) {
|
start();
|
||||||
start();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'start':
|
case 'start':
|
||||||
|
|
|
@ -317,7 +317,8 @@
|
||||||
"create": "Create new",
|
"create": "Create new",
|
||||||
"search": "Search by name",
|
"search": "Search by name",
|
||||||
"tab-manage": "Manage",
|
"tab-manage": "Manage",
|
||||||
"tab-editor": "Editor"
|
"tab-editor": "Editor",
|
||||||
|
"remove-alert": "Remove extension \"{{name}}\"?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"form-actions": {
|
"form-actions": {
|
||||||
|
@ -336,7 +337,9 @@
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"password-reveal": "Reveal",
|
"password-reveal": "Reveal",
|
||||||
"password-hide": "Hide"
|
"password-hide": "Hide",
|
||||||
|
"start": "Start",
|
||||||
|
"stop": "Stop"
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"dev-build": "Development build"
|
"dev-build": "Development build"
|
||||||
|
|
|
@ -4,14 +4,18 @@ import { Extension } from '~/lib/extensions/extension';
|
||||||
import {
|
import {
|
||||||
ExtensionDependencies,
|
ExtensionDependencies,
|
||||||
ExtensionOptions,
|
ExtensionOptions,
|
||||||
|
ExtensionRunOptions,
|
||||||
|
ExtensionStatus,
|
||||||
} from '~/lib/extensions/types';
|
} from '~/lib/extensions/types';
|
||||||
import { RootState } from '..';
|
import { RootState } from '..';
|
||||||
import { HTTPConfig } from '../api/types';
|
import { HTTPConfig } from '../api/types';
|
||||||
|
|
||||||
interface ExtensionsState {
|
interface ExtensionsState {
|
||||||
ready: boolean;
|
ready: boolean;
|
||||||
installed: Record<string, Extension>;
|
installed: Record<string, ExtensionEntry>;
|
||||||
|
running: Record<string, Extension>;
|
||||||
unsaved: Record<string, string>;
|
unsaved: Record<string, string>;
|
||||||
|
status: Record<string, ExtensionStatus>;
|
||||||
editorCurrentFile: string;
|
editorCurrentFile: string;
|
||||||
dependencies: ExtensionDependencies;
|
dependencies: ExtensionDependencies;
|
||||||
}
|
}
|
||||||
|
@ -25,8 +29,10 @@ export interface ExtensionEntry {
|
||||||
const initialState: ExtensionsState = {
|
const initialState: ExtensionsState = {
|
||||||
ready: false,
|
ready: false,
|
||||||
installed: {},
|
installed: {},
|
||||||
|
running: {},
|
||||||
unsaved: {},
|
unsaved: {},
|
||||||
editorCurrentFile: null,
|
editorCurrentFile: null,
|
||||||
|
status: {},
|
||||||
dependencies: {
|
dependencies: {
|
||||||
kilovolt: { address: '' },
|
kilovolt: { address: '' },
|
||||||
},
|
},
|
||||||
|
@ -54,37 +60,111 @@ const extensionsReducer = createSlice({
|
||||||
extensionSourceChanged(state, { payload }: PayloadAction<string>) {
|
extensionSourceChanged(state, { payload }: PayloadAction<string>) {
|
||||||
state.unsaved[state.editorCurrentFile] = payload;
|
state.unsaved[state.editorCurrentFile] = payload;
|
||||||
},
|
},
|
||||||
|
extensionStatusChanged(
|
||||||
|
state,
|
||||||
|
{ payload }: PayloadAction<{ name: string; status: ExtensionStatus }>,
|
||||||
|
) {
|
||||||
|
state.status[payload.name] = payload.status;
|
||||||
|
},
|
||||||
extensionAdded(state, { payload }: PayloadAction<ExtensionEntry>) {
|
extensionAdded(state, { payload }: PayloadAction<ExtensionEntry>) {
|
||||||
// Remove from unsaved
|
// Remove from unsaved
|
||||||
if (payload.name in state.unsaved) {
|
if (payload.name in state.unsaved) {
|
||||||
delete state.unsaved[payload.name];
|
delete state.unsaved[payload.name];
|
||||||
}
|
}
|
||||||
|
|
||||||
// If running, terminate running instance
|
|
||||||
if (payload.name in state.installed) {
|
|
||||||
state.installed[payload.name]?.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't have a file selected in the editor, set a default as soon as possible
|
// If we don't have a file selected in the editor, set a default as soon as possible
|
||||||
if (!state.editorCurrentFile) {
|
if (!state.editorCurrentFile) {
|
||||||
state.editorCurrentFile = payload.name;
|
state.editorCurrentFile = payload.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.installed[payload.name] = payload;
|
||||||
|
},
|
||||||
|
extensionInstanceAdded(state, { payload }: PayloadAction<Extension>) {
|
||||||
|
// If running, terminate running instance
|
||||||
|
if (payload.info.name in state.running) {
|
||||||
|
state.running[payload.info.name]?.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
// Create new instance with stored code
|
// Create new instance with stored code
|
||||||
state.installed[payload.name] = new Extension(
|
state.status[payload.info.name] = ExtensionStatus.GettingReady;
|
||||||
payload.name,
|
state.running[payload.info.name] = payload;
|
||||||
payload.source,
|
},
|
||||||
payload.options,
|
extensionRemoved(state, { payload }: PayloadAction<string>) {
|
||||||
{
|
// If running, terminate running instance
|
||||||
kilovolt: { ...state.dependencies.kilovolt },
|
if (payload in state.running) {
|
||||||
},
|
state.running[payload]?.dispose();
|
||||||
);
|
}
|
||||||
|
|
||||||
|
// Remove from other lists
|
||||||
|
delete state.installed[payload];
|
||||||
|
delete state.running[payload];
|
||||||
|
delete state.unsaved[payload];
|
||||||
|
delete state.status[payload];
|
||||||
|
|
||||||
|
// If it's the currently selected file in the editor, select another or none
|
||||||
|
if (state.editorCurrentFile === payload) {
|
||||||
|
const others = Object.keys(state.installed);
|
||||||
|
state.editorCurrentFile = others.length > 0 ? others[0] : null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const extensionPrefix = 'ui/extensions/installed/';
|
const extensionPrefix = 'ui/extensions/installed/';
|
||||||
|
|
||||||
|
export const createExtensionInstance = createAsyncThunk(
|
||||||
|
'extensions/new-instance',
|
||||||
|
(
|
||||||
|
payload: {
|
||||||
|
entry: ExtensionEntry;
|
||||||
|
dependencies: ExtensionDependencies;
|
||||||
|
runOptions?: ExtensionRunOptions;
|
||||||
|
},
|
||||||
|
{ dispatch },
|
||||||
|
) => {
|
||||||
|
const ext = new Extension(
|
||||||
|
payload.entry,
|
||||||
|
payload.dependencies,
|
||||||
|
payload.runOptions,
|
||||||
|
);
|
||||||
|
ext.addEventListener(
|
||||||
|
'statusChanged',
|
||||||
|
(ev: CustomEvent<ExtensionStatus>) => {
|
||||||
|
dispatch(
|
||||||
|
extensionsReducer.actions.extensionStatusChanged({
|
||||||
|
name: payload.entry.name,
|
||||||
|
status: ev.detail,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
dispatch(extensionsReducer.actions.extensionAdded(payload.entry));
|
||||||
|
dispatch(extensionsReducer.actions.extensionInstanceAdded(ext));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const refreshExtensionInstance = createAsyncThunk(
|
||||||
|
'extensions/refresh-instance',
|
||||||
|
async (payload: ExtensionEntry, { dispatch, getState }) => {
|
||||||
|
const { extensions } = getState() as RootState;
|
||||||
|
if (payload.options.enabled) {
|
||||||
|
await dispatch(
|
||||||
|
createExtensionInstance({
|
||||||
|
entry: payload,
|
||||||
|
dependencies: extensions.dependencies,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// If running, terminate running instance
|
||||||
|
if (payload.name in extensions.running) {
|
||||||
|
extensions.running[payload.name]?.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(extensionsReducer.actions.extensionAdded(payload));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const initializeExtensions = createAsyncThunk(
|
export const initializeExtensions = createAsyncThunk(
|
||||||
'extensions/initialize',
|
'extensions/initialize',
|
||||||
async (_: void, { getState, dispatch }) => {
|
async (_: void, { getState, dispatch }) => {
|
||||||
|
@ -95,19 +175,18 @@ export const initializeExtensions = createAsyncThunk(
|
||||||
const httpConfig = await api.client.getJSON<HTTPConfig>('http/config');
|
const httpConfig = await api.client.getJSON<HTTPConfig>('http/config');
|
||||||
|
|
||||||
// Set dependencies
|
// Set dependencies
|
||||||
dispatch(
|
const deps = {
|
||||||
extensionsReducer.actions.initialized({
|
kilovolt: {
|
||||||
kilovolt: {
|
address: `ws://${httpConfig.bind}/ws`,
|
||||||
address: `ws://${httpConfig.bind}/ws`,
|
password: httpConfig.kv_password,
|
||||||
password: httpConfig.kv_password,
|
},
|
||||||
},
|
};
|
||||||
}),
|
dispatch(extensionsReducer.actions.initialized(deps));
|
||||||
);
|
|
||||||
|
|
||||||
// Become reactive to extension changes
|
// Become reactive to extension changes
|
||||||
await api.client.subscribePrefix(extensionPrefix, (newValue, newKey) => {
|
await api.client.subscribePrefix(extensionPrefix, (newValue, newKey) => {
|
||||||
dispatch(
|
void dispatch(
|
||||||
extensionsReducer.actions.extensionAdded({
|
refreshExtensionInstance({
|
||||||
...(JSON.parse(newValue) as ExtensionEntry),
|
...(JSON.parse(newValue) as ExtensionEntry),
|
||||||
name: newKey.substring(extensionPrefix.length),
|
name: newKey.substring(extensionPrefix.length),
|
||||||
}),
|
}),
|
||||||
|
@ -115,18 +194,57 @@ export const initializeExtensions = createAsyncThunk(
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get installed extensions
|
// Get installed extensions
|
||||||
const extensions = await api.client.getKeysByPrefix(extensionPrefix);
|
const installed = await api.client.getKeysByPrefix(extensionPrefix);
|
||||||
Object.entries(extensions).forEach(([extName, extContent]) =>
|
await Promise.all(
|
||||||
dispatch(
|
Object.entries(installed).map(async ([extName, extContent]) => {
|
||||||
extensionsReducer.actions.extensionAdded({
|
const entry = {
|
||||||
...(JSON.parse(extContent) as ExtensionEntry),
|
...(JSON.parse(extContent) as ExtensionEntry),
|
||||||
name: extName.substring(extensionPrefix.length),
|
name: extName.substring(extensionPrefix.length),
|
||||||
}),
|
};
|
||||||
),
|
if (entry.options.enabled) {
|
||||||
|
await dispatch(
|
||||||
|
createExtensionInstance({
|
||||||
|
entry,
|
||||||
|
dependencies: deps,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
dispatch(extensionsReducer.actions.extensionAdded(entry));
|
||||||
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const startExtension = createAsyncThunk(
|
||||||
|
'extensions/start',
|
||||||
|
async (name: string, { getState, dispatch }) => {
|
||||||
|
const { extensions } = getState() as RootState;
|
||||||
|
|
||||||
|
// If terminated, re-create extension
|
||||||
|
if (extensions.running[name].status === ExtensionStatus.Terminated) {
|
||||||
|
await dispatch(
|
||||||
|
createExtensionInstance({
|
||||||
|
entry: extensions.installed[name],
|
||||||
|
dependencies: extensions.dependencies,
|
||||||
|
runOptions: { autostart: true },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions.running[name].start();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const stopExtension = createAsyncThunk(
|
||||||
|
'extensions/stop',
|
||||||
|
(name: string, { getState }) => {
|
||||||
|
const { extensions } = getState() as RootState;
|
||||||
|
extensions.running[name].stop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const saveExtension = createAsyncThunk(
|
export const saveExtension = createAsyncThunk(
|
||||||
'extensions/save',
|
'extensions/save',
|
||||||
async (entry: ExtensionEntry, { getState }) => {
|
async (entry: ExtensionEntry, { getState }) => {
|
||||||
|
@ -136,4 +254,14 @@ export const saveExtension = createAsyncThunk(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const removeExtension = createAsyncThunk(
|
||||||
|
'extensions/remove',
|
||||||
|
async (name: string, { getState, dispatch }) => {
|
||||||
|
// Get kv client
|
||||||
|
const { api } = getState() as RootState;
|
||||||
|
dispatch(extensionsReducer.actions.extensionRemoved(name));
|
||||||
|
await api.client.deleteKey(extensionPrefix + name);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default extensionsReducer;
|
export default extensionsReducer;
|
||||||
|
|
|
@ -2,10 +2,18 @@ import Editor from '@monaco-editor/react';
|
||||||
import { InputIcon, PlusIcon } from '@radix-ui/react-icons';
|
import { InputIcon, PlusIcon } from '@radix-ui/react-icons';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { blankTemplate, Extension } from '~/lib/extensions/extension';
|
import { blankTemplate } from '~/lib/extensions/extension';
|
||||||
|
import { ExtensionStatus } from '~/lib/extensions/types';
|
||||||
import slug from '~/lib/slug';
|
import slug from '~/lib/slug';
|
||||||
import { useAppDispatch, useAppSelector } from '~/store';
|
import { useAppDispatch, useAppSelector } from '~/store';
|
||||||
import extensionsReducer, { saveExtension } from '~/store/extensions/reducer';
|
import extensionsReducer, {
|
||||||
|
ExtensionEntry,
|
||||||
|
removeExtension,
|
||||||
|
saveExtension,
|
||||||
|
startExtension,
|
||||||
|
stopExtension,
|
||||||
|
} from '~/store/extensions/reducer';
|
||||||
|
import AlertContent from '../components/AlertContent';
|
||||||
import Loading from '../components/Loading';
|
import Loading from '../components/Loading';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
@ -13,6 +21,7 @@ import {
|
||||||
Field,
|
Field,
|
||||||
FlexRow,
|
FlexRow,
|
||||||
InputBox,
|
InputBox,
|
||||||
|
MultiButton,
|
||||||
PageContainer,
|
PageContainer,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
PageTitle,
|
PageTitle,
|
||||||
|
@ -22,13 +31,129 @@ import {
|
||||||
TabContent,
|
TabContent,
|
||||||
TabList,
|
TabList,
|
||||||
} from '../theme';
|
} from '../theme';
|
||||||
|
import { Alert, AlertTrigger } from '../theme/alert';
|
||||||
|
|
||||||
interface ExtensionListProps {
|
const ExtensionRow = styled('article', {
|
||||||
extensions: Record<string, Extension>;
|
marginBottom: '0.4rem',
|
||||||
onNewClicked: () => void;
|
backgroundColor: '$gray2',
|
||||||
|
margin: '0.5rem 0',
|
||||||
|
padding: '0.3rem 0.5rem',
|
||||||
|
borderLeft: '5px solid $teal8',
|
||||||
|
borderRadius: '0.25rem',
|
||||||
|
borderBottom: '1px solid $gray4',
|
||||||
|
transition: 'all 50ms',
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: '$gray3',
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
status: {
|
||||||
|
enabled: {},
|
||||||
|
disabled: {
|
||||||
|
borderLeftColor: '$red6',
|
||||||
|
backgroundColor: '$gray3',
|
||||||
|
color: '$gray10',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const ExtensionName = styled('div', {
|
||||||
|
flex: '1',
|
||||||
|
});
|
||||||
|
const ExtensionActions = styled('div', {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '0.25rem',
|
||||||
|
});
|
||||||
|
|
||||||
|
const isRunning = (status: ExtensionStatus) =>
|
||||||
|
status === ExtensionStatus.Running || status === ExtensionStatus.Finished;
|
||||||
|
|
||||||
|
type ExtensionListItemProps = {
|
||||||
|
enabled: boolean;
|
||||||
|
entry: ExtensionEntry;
|
||||||
|
status: ExtensionStatus;
|
||||||
|
onEdit: () => void;
|
||||||
|
onRemove: () => void;
|
||||||
|
onToggleEnable: () => void;
|
||||||
|
onToggleStatus: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
function ExtensionListItem(props: ExtensionListItemProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<ExtensionRow
|
||||||
|
status={props.enabled && isRunning(props.status) ? 'enabled' : 'disabled'}
|
||||||
|
>
|
||||||
|
<FlexRow>
|
||||||
|
<ExtensionName>
|
||||||
|
{props.entry.name} {props.enabled ? `(${props.status})` : null}
|
||||||
|
</ExtensionName>
|
||||||
|
<ExtensionActions>
|
||||||
|
<MultiButton>
|
||||||
|
<Button
|
||||||
|
styling="multi"
|
||||||
|
size="small"
|
||||||
|
onClick={() => props.onToggleEnable()}
|
||||||
|
>
|
||||||
|
{t(
|
||||||
|
props.entry.options.enabled
|
||||||
|
? 'form-actions.disable'
|
||||||
|
: 'form-actions.enable',
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
{props.enabled ? (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
styling="multi"
|
||||||
|
size="small"
|
||||||
|
onClick={() => props.onToggleStatus()}
|
||||||
|
>
|
||||||
|
{t(
|
||||||
|
isRunning(props.status)
|
||||||
|
? 'form-actions.stop'
|
||||||
|
: 'form-actions.start',
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<Button styling="multi" size="small" onClick={() => props.onEdit()}>
|
||||||
|
{t('form-actions.edit')}
|
||||||
|
</Button>
|
||||||
|
<Alert>
|
||||||
|
<AlertTrigger asChild>
|
||||||
|
<Button styling="multi" size="small">
|
||||||
|
{t('form-actions.delete')}
|
||||||
|
</Button>
|
||||||
|
</AlertTrigger>
|
||||||
|
<AlertContent
|
||||||
|
variation="danger"
|
||||||
|
title={t('pages.extensions.remove-alert', {
|
||||||
|
name: props.entry.name,
|
||||||
|
})}
|
||||||
|
description={t('form-actions.warning-delete')}
|
||||||
|
actionText={t('form-actions.delete')}
|
||||||
|
actionButtonProps={{ variation: 'danger' }}
|
||||||
|
showCancel={true}
|
||||||
|
onAction={() => props.onRemove()}
|
||||||
|
/>
|
||||||
|
</Alert>
|
||||||
|
</MultiButton>
|
||||||
|
</ExtensionActions>
|
||||||
|
</FlexRow>
|
||||||
|
</ExtensionRow>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ExtensionList({ extensions, onNewClicked }: ExtensionListProps) {
|
interface ExtensionListProps {
|
||||||
|
onNew: () => void;
|
||||||
|
onEdit: (name: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ExtensionList({ onNew, onEdit }: ExtensionListProps) {
|
||||||
|
const extensions = useAppSelector((state) => state.extensions);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [filter, setFilter] = useState('');
|
const [filter, setFilter] = useState('');
|
||||||
const filterLC = filter.toLowerCase();
|
const filterLC = filter.toLowerCase();
|
||||||
|
@ -40,7 +165,7 @@ function ExtensionList({ extensions, onNewClicked }: ExtensionListProps) {
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<Field size="fullWidth" spacing="none">
|
<Field size="fullWidth" spacing="none">
|
||||||
<FlexRow css={{ flex: 1, alignItems: 'stretch' }} spacing="1">
|
<FlexRow css={{ flex: 1, alignItems: 'stretch' }} spacing="1">
|
||||||
<Button variation="primary" onClick={() => onNewClicked()}>
|
<Button variation="primary" onClick={() => onNew()}>
|
||||||
<PlusIcon /> {t('pages.extensions.create')}
|
<PlusIcon /> {t('pages.extensions.create')}
|
||||||
</Button>
|
</Button>
|
||||||
<InputBox
|
<InputBox
|
||||||
|
@ -51,10 +176,39 @@ function ExtensionList({ extensions, onNewClicked }: ExtensionListProps) {
|
||||||
/>
|
/>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
</Field>
|
</Field>
|
||||||
{Object.values(extensions)
|
{Object.values(extensions.installed)
|
||||||
?.filter((r) => r.name.toLowerCase().includes(filterLC))
|
?.filter((r) => r.name.toLowerCase().includes(filterLC))
|
||||||
.map((e) => (
|
.map((e) => (
|
||||||
<div key={e.name}>{e.name}</div>
|
<ExtensionListItem
|
||||||
|
key={e.name}
|
||||||
|
entry={e}
|
||||||
|
enabled={e.options.enabled}
|
||||||
|
status={extensions.status[e.name]}
|
||||||
|
onEdit={() => onEdit(e.name)}
|
||||||
|
onRemove={() => {
|
||||||
|
// Toggle enabled status
|
||||||
|
void dispatch(removeExtension(e.name));
|
||||||
|
}}
|
||||||
|
onToggleEnable={() => {
|
||||||
|
// Toggle enabled status
|
||||||
|
void dispatch(
|
||||||
|
saveExtension({
|
||||||
|
...e,
|
||||||
|
options: {
|
||||||
|
...e.options,
|
||||||
|
enabled: !e.options.enabled,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
onToggleStatus={() => {
|
||||||
|
if (isRunning(extensions.status[e.name])) {
|
||||||
|
void dispatch(stopExtension(e.name));
|
||||||
|
} else {
|
||||||
|
void dispatch(startExtension(e.name));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
|
@ -80,10 +234,13 @@ const EditorDropdown = styled(ComboBox, {
|
||||||
function ExtensionEditor() {
|
function ExtensionEditor() {
|
||||||
const extensions = useAppSelector((state) => state.extensions);
|
const extensions = useAppSelector((state) => state.extensions);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const currentFile =
|
const isUnsaved =
|
||||||
extensions.editorCurrentFile in extensions.unsaved
|
extensions.editorCurrentFile in extensions.unsaved &&
|
||||||
? extensions.unsaved[extensions.editorCurrentFile]
|
extensions.unsaved[extensions.editorCurrentFile] !==
|
||||||
: extensions.installed[extensions.editorCurrentFile].source;
|
extensions.installed[extensions.editorCurrentFile]?.source;
|
||||||
|
const currentFile = isUnsaved
|
||||||
|
? extensions.unsaved[extensions.editorCurrentFile]
|
||||||
|
: extensions.installed[extensions.editorCurrentFile].source;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
@ -98,6 +255,11 @@ function ExtensionEditor() {
|
||||||
>
|
>
|
||||||
<EditorDropdown
|
<EditorDropdown
|
||||||
value={extensions.editorCurrentFile}
|
value={extensions.editorCurrentFile}
|
||||||
|
onChange={(ev) => {
|
||||||
|
void dispatch(
|
||||||
|
extensionsReducer.actions.editorSelectedFile(ev.target.value),
|
||||||
|
);
|
||||||
|
}}
|
||||||
css={{ flex: '1' }}
|
css={{ flex: '1' }}
|
||||||
>
|
>
|
||||||
{Object.values(extensions.installed)
|
{Object.values(extensions.installed)
|
||||||
|
@ -109,7 +271,8 @@ function ExtensionEditor() {
|
||||||
))}
|
))}
|
||||||
{Object.keys(extensions.unsaved).map((ext) => (
|
{Object.keys(extensions.unsaved).map((ext) => (
|
||||||
<option key={ext} value={ext}>
|
<option key={ext} value={ext}>
|
||||||
{ext}*
|
{ext}
|
||||||
|
{isUnsaved ? '*' : ''}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</EditorDropdown>
|
</EditorDropdown>
|
||||||
|
@ -118,7 +281,7 @@ function ExtensionEditor() {
|
||||||
</EditorButton>
|
</EditorButton>
|
||||||
<EditorButton
|
<EditorButton
|
||||||
size="small"
|
size="small"
|
||||||
disabled={!(extensions.editorCurrentFile in extensions.unsaved)}
|
disabled={!isUnsaved}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
void dispatch(
|
void dispatch(
|
||||||
saveExtension({
|
saveExtension({
|
||||||
|
@ -127,7 +290,7 @@ function ExtensionEditor() {
|
||||||
options:
|
options:
|
||||||
extensions.editorCurrentFile in extensions.installed
|
extensions.editorCurrentFile in extensions.installed
|
||||||
? extensions.installed[extensions.editorCurrentFile].options
|
? extensions.installed[extensions.editorCurrentFile].options
|
||||||
: { autostart: false },
|
: { enabled: false },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -169,7 +332,7 @@ export default function ExtensionsPage(): React.ReactElement {
|
||||||
extensionsReducer.actions.extensionDrafted({
|
extensionsReducer.actions.extensionDrafted({
|
||||||
name: defaultName,
|
name: defaultName,
|
||||||
source: blankTemplate(defaultName),
|
source: blankTemplate(defaultName),
|
||||||
options: { autostart: false },
|
options: { enabled: false },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -178,6 +341,12 @@ export default function ExtensionsPage(): React.ReactElement {
|
||||||
setCurrentTab('editor');
|
setCurrentTab('editor');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const editClicked = (name: string) => {
|
||||||
|
// Set it as current file in editor
|
||||||
|
dispatch(extensionsReducer.actions.editorSelectedFile(name));
|
||||||
|
setCurrentTab('editor');
|
||||||
|
};
|
||||||
|
|
||||||
if (!extensions.ready) {
|
if (!extensions.ready) {
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
|
@ -202,8 +371,8 @@ export default function ExtensionsPage(): React.ReactElement {
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabContent css={{ paddingTop: '1rem' }} value="list">
|
<TabContent css={{ paddingTop: '1rem' }} value="list">
|
||||||
<ExtensionList
|
<ExtensionList
|
||||||
extensions={extensions.installed}
|
onNew={() => newClicked()}
|
||||||
onNewClicked={() => newClicked()}
|
onEdit={(name) => editClicked(name)}
|
||||||
/>
|
/>
|
||||||
</TabContent>
|
</TabContent>
|
||||||
<TabContent css={{ paddingTop: '0' }} value="editor">
|
<TabContent css={{ paddingTop: '0' }} value="editor">
|
||||||
|
|
Loading…
Reference in a new issue