openlearn-next 0.3.19 → 0.3.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{BatchPickerModal-MnNnNwFT.js → BatchPickerModal-CPmu_uF8.js} +1 -1
- package/dist/assets/{ClassesView-CpblTKWg.js → ClassesView-TVobZ_Vs.js} +1 -1
- package/dist/assets/{CloudDriveModal-BMjXCoju.js → CloudDriveModal-ul9v6wHK.js} +1 -1
- package/dist/assets/{CourseManagement-BqlboC1S.js → CourseManagement-DFQsUxE1.js} +1 -1
- package/dist/assets/{CourseWizardModal-DO1n4T2T.js → CourseWizardModal-D92teHMD.js} +1 -1
- package/dist/assets/{Dashboard-CfgOPb6z.js → Dashboard-CY138ODd.js} +1 -1
- package/dist/assets/{ExportWeightModal-d9A2R6Ur.js → ExportWeightModal-BvoODxwp.js} +1 -1
- package/dist/assets/{HelpView-bHc3tQPD.js → HelpView-CHkXbtfB.js} +1 -1
- package/dist/assets/{ImportLessonsModal-C25NubUA.js → ImportLessonsModal-YuTV3mnv.js} +1 -1
- package/dist/assets/{InteractiveCoursewareViewer-BcXVoPUb.js → InteractiveCoursewareViewer-BBeuViik.js} +1 -1
- package/dist/assets/{InteractiveWhiteboard-BxvOZE3l.js → InteractiveWhiteboard-BASy3Ak8.js} +1 -1
- package/dist/assets/{LazyCourseware-D3_YrYGR.js → LazyCourseware-Du9MgZyH.js} +2 -2
- package/dist/assets/{LazyWhiteboard-CFPDpdYV.js → LazyWhiteboard-CpSzdON0.js} +2 -2
- package/dist/assets/{LessonEditorView-CYHcC9EA.js → LessonEditorView-D9YYZQyP.js} +1 -1
- package/dist/assets/{LiveClassroomView-BSPW9gVe.js → LiveClassroomView-B2lKKiZK.js} +1 -1
- package/dist/assets/{NotificationDetailModal-D4w-m1D-.js → NotificationDetailModal-BthQNwyd.js} +1 -1
- package/dist/assets/PluginView-CQLEVURc.js +65 -0
- package/dist/assets/{QuizGeneratorModal-bCASRfsi.js → QuizGeneratorModal-Bp4pUx-H.js} +1 -1
- package/dist/assets/{StudentAssignmentView-B0b4spYC.js → StudentAssignmentView-B-jN_m_B.js} +1 -1
- package/dist/assets/{StudentLessonView-SLcpNqtY.js → StudentLessonView-8-zC7BLx.js} +1 -1
- package/dist/assets/{StudentPreviewModal-nO5C3nq5.js → StudentPreviewModal-DZdcKy1x.js} +1 -1
- package/dist/assets/{StudentView-BtleJCxa.js → StudentView-Cg5G4Guz.js} +2 -2
- package/dist/assets/{SystemResourceLibraryModal-D00BK5Xh.js → SystemResourceLibraryModal-CJ16MGxn.js} +1 -1
- package/dist/assets/{TeacherView-BP04D9dn.js → TeacherView-_n9QWf7y.js} +2 -2
- package/dist/assets/{index-hTc1h9s2.js → index-BApV4fuy.js} +3 -3
- package/dist/assets/index-Ch_mD6B0.css +1 -0
- package/dist/index.html +2 -2
- package/dist/server.cjs +126 -49
- package/package.json +1 -1
- package/dist/assets/PluginView-DogON4Dd.js +0 -65
- package/dist/assets/index-CM3Tw-Rg.css +0 -1
package/dist/server.cjs
CHANGED
|
@@ -873,9 +873,9 @@ var init_http_router = __esm({
|
|
|
873
873
|
/**
|
|
874
874
|
* 注册指定动词与路径的 Handler
|
|
875
875
|
*/
|
|
876
|
-
route(method,
|
|
876
|
+
route(method, path22, handler) {
|
|
877
877
|
const upperMethod = method.toUpperCase();
|
|
878
|
-
const normalized =
|
|
878
|
+
const normalized = path22.startsWith("/") ? path22 : "/" + path22;
|
|
879
879
|
const { regex, paramNames } = compileRoutePattern(normalized);
|
|
880
880
|
this.routes.push({
|
|
881
881
|
method: upperMethod,
|
|
@@ -886,36 +886,36 @@ var init_http_router = __esm({
|
|
|
886
886
|
isStream: false
|
|
887
887
|
});
|
|
888
888
|
}
|
|
889
|
-
get(
|
|
890
|
-
this.route("GET",
|
|
889
|
+
get(path22, handler) {
|
|
890
|
+
this.route("GET", path22, handler);
|
|
891
891
|
}
|
|
892
|
-
post(
|
|
893
|
-
this.route("POST",
|
|
892
|
+
post(path22, handler) {
|
|
893
|
+
this.route("POST", path22, handler);
|
|
894
894
|
}
|
|
895
|
-
put(
|
|
896
|
-
this.route("PUT",
|
|
895
|
+
put(path22, handler) {
|
|
896
|
+
this.route("PUT", path22, handler);
|
|
897
897
|
}
|
|
898
|
-
patch(
|
|
899
|
-
this.route("PATCH",
|
|
898
|
+
patch(path22, handler) {
|
|
899
|
+
this.route("PATCH", path22, handler);
|
|
900
900
|
}
|
|
901
|
-
delete(
|
|
902
|
-
this.route("DELETE",
|
|
901
|
+
delete(path22, handler) {
|
|
902
|
+
this.route("DELETE", path22, handler);
|
|
903
903
|
}
|
|
904
904
|
stream(methodOrPath, pathOrHandler, maybeHandler) {
|
|
905
905
|
if (typeof pathOrHandler === "function") {
|
|
906
|
-
const
|
|
906
|
+
const path22 = methodOrPath;
|
|
907
907
|
const handler = pathOrHandler;
|
|
908
|
-
this.registerStream("GET",
|
|
909
|
-
this.registerStream("POST",
|
|
908
|
+
this.registerStream("GET", path22, handler);
|
|
909
|
+
this.registerStream("POST", path22, handler);
|
|
910
910
|
} else {
|
|
911
911
|
const method = methodOrPath.toUpperCase();
|
|
912
|
-
const
|
|
912
|
+
const path22 = pathOrHandler;
|
|
913
913
|
const handler = maybeHandler;
|
|
914
|
-
this.registerStream(method,
|
|
914
|
+
this.registerStream(method, path22, handler);
|
|
915
915
|
}
|
|
916
916
|
}
|
|
917
|
-
registerStream(method,
|
|
918
|
-
const normalized =
|
|
917
|
+
registerStream(method, path22, handler) {
|
|
918
|
+
const normalized = path22.startsWith("/") ? path22 : "/" + path22;
|
|
919
919
|
const { regex, paramNames } = compileRoutePattern(normalized);
|
|
920
920
|
this.routes.push({
|
|
921
921
|
method: method.toUpperCase(),
|
|
@@ -929,9 +929,9 @@ var init_http_router = __esm({
|
|
|
929
929
|
/**
|
|
930
930
|
* 检查指定动词与路径是否为流式路由
|
|
931
931
|
*/
|
|
932
|
-
isStream(method,
|
|
932
|
+
isStream(method, path22) {
|
|
933
933
|
const upperMethod = method.toUpperCase();
|
|
934
|
-
const normalized =
|
|
934
|
+
const normalized = path22.startsWith("/") ? path22 : "/" + path22;
|
|
935
935
|
for (const entry of this.routes) {
|
|
936
936
|
if (entry.method !== upperMethod) continue;
|
|
937
937
|
if (entry.regex.test(normalized)) {
|
|
@@ -943,9 +943,9 @@ var init_http_router = __esm({
|
|
|
943
943
|
/**
|
|
944
944
|
* 匹配路由并提取路由参数
|
|
945
945
|
*/
|
|
946
|
-
match(method,
|
|
946
|
+
match(method, path22) {
|
|
947
947
|
const upperMethod = method.toUpperCase();
|
|
948
|
-
const normalized =
|
|
948
|
+
const normalized = path22.startsWith("/") ? path22 : "/" + path22;
|
|
949
949
|
for (const entry of this.routes) {
|
|
950
950
|
if (entry.method !== upperMethod) continue;
|
|
951
951
|
const match = normalized.match(entry.regex);
|
|
@@ -3592,12 +3592,12 @@ parentPort.on('message', async function(msg) {
|
|
|
3592
3592
|
});
|
|
3593
3593
|
`;
|
|
3594
3594
|
}
|
|
3595
|
-
var import_node_worker_threads, import_fs4,
|
|
3595
|
+
var import_node_worker_threads, import_fs4, import_node_path4, import_uuid12, ALL_SERVICE_TOKENS, MAX_WORKERS, ACTIVATE_TIMEOUT_MS, ACTIVATE_PROGRESS_SLIDE_MS, WorkerRegistry, WorkerManager;
|
|
3596
3596
|
var init_worker_manager = __esm({
|
|
3597
3597
|
"packages/core/worker-runtime/worker-manager.ts"() {
|
|
3598
3598
|
import_node_worker_threads = require("node:worker_threads");
|
|
3599
3599
|
import_fs4 = __toESM(require("fs"), 1);
|
|
3600
|
-
|
|
3600
|
+
import_node_path4 = __toESM(require("node:path"), 1);
|
|
3601
3601
|
init_transport();
|
|
3602
3602
|
init_service_host();
|
|
3603
3603
|
init_logger();
|
|
@@ -3885,7 +3885,7 @@ var init_worker_manager = __esm({
|
|
|
3885
3885
|
const bootstrapDataUrl = `data:text/javascript;base64,${encodedBootstrap}`;
|
|
3886
3886
|
let worker;
|
|
3887
3887
|
try {
|
|
3888
|
-
const resolvedPluginDir = pluginDir && import_fs4.default.existsSync(
|
|
3888
|
+
const resolvedPluginDir = pluginDir && import_fs4.default.existsSync(import_node_path4.default.join(pluginDir, "index.js")) ? pluginDir : void 0;
|
|
3889
3889
|
worker = new import_node_worker_threads.Worker(new URL(bootstrapDataUrl), {
|
|
3890
3890
|
// Pass both the DB id (`pluginId` — used as the actor/registry key)
|
|
3891
3891
|
// and `manifestId` (used as the namespace prefix for command types).
|
|
@@ -8781,7 +8781,8 @@ var import_fs5 = __toESM(require("fs"), 1);
|
|
|
8781
8781
|
var import_jszip2 = __toESM(require("jszip"), 1);
|
|
8782
8782
|
var import_path4 = __toESM(require("path"), 1);
|
|
8783
8783
|
var import_express = __toESM(require("express"), 1);
|
|
8784
|
-
var
|
|
8784
|
+
var import_node_module = require("node:module");
|
|
8785
|
+
var import_node_url2 = require("node:url");
|
|
8785
8786
|
|
|
8786
8787
|
// packages/core/esm-loader/install-utils.ts
|
|
8787
8788
|
var import_jszip = __toESM(require("jszip"), 1);
|
|
@@ -9349,7 +9350,34 @@ var HotReloadActivationError = class extends HotReloadError {
|
|
|
9349
9350
|
init_interfaces();
|
|
9350
9351
|
|
|
9351
9352
|
// packages/core/version.ts
|
|
9352
|
-
var
|
|
9353
|
+
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
9354
|
+
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
9355
|
+
var import_node_url = require("node:url");
|
|
9356
|
+
var import_meta2 = {};
|
|
9357
|
+
var FALLBACK_VERSION = "0.3.20";
|
|
9358
|
+
function readPlatformVersion() {
|
|
9359
|
+
const bases = [];
|
|
9360
|
+
if (typeof __dirname !== "undefined") bases.push(__dirname);
|
|
9361
|
+
try {
|
|
9362
|
+
bases.push(import_node_path3.default.dirname((0, import_node_url.fileURLToPath)(import_meta2.url)));
|
|
9363
|
+
} catch {
|
|
9364
|
+
}
|
|
9365
|
+
for (const base of bases) {
|
|
9366
|
+
let dir = base;
|
|
9367
|
+
for (; ; ) {
|
|
9368
|
+
try {
|
|
9369
|
+
const pkg = JSON.parse(import_node_fs3.default.readFileSync(import_node_path3.default.join(dir, "package.json"), "utf-8"));
|
|
9370
|
+
if (pkg.name === "openlearn-next" && typeof pkg.version === "string") return pkg.version;
|
|
9371
|
+
} catch {
|
|
9372
|
+
}
|
|
9373
|
+
const parent = import_node_path3.default.dirname(dir);
|
|
9374
|
+
if (parent === dir) break;
|
|
9375
|
+
dir = parent;
|
|
9376
|
+
}
|
|
9377
|
+
}
|
|
9378
|
+
return FALLBACK_VERSION;
|
|
9379
|
+
}
|
|
9380
|
+
var PLATFORM_VERSION = readPlatformVersion();
|
|
9353
9381
|
var OPENLEARN_VERSION = PLATFORM_VERSION;
|
|
9354
9382
|
|
|
9355
9383
|
// packages/core/plugin-host/plugin-runtime-composition.ts
|
|
@@ -9654,11 +9682,11 @@ var PluginDistributionManager = class {
|
|
|
9654
9682
|
return;
|
|
9655
9683
|
}
|
|
9656
9684
|
const filePath = this.pluginHost.getPluginFilePath(this.pluginHost.resolvePluginUuid(pluginId));
|
|
9657
|
-
const
|
|
9658
|
-
if (!
|
|
9685
|
+
const fs17 = await import("node:fs");
|
|
9686
|
+
if (!fs17.existsSync(filePath)) {
|
|
9659
9687
|
throw new Error(`Cannot reload plugin "${pluginId}": source file missing at ${filePath}`);
|
|
9660
9688
|
}
|
|
9661
|
-
const code =
|
|
9689
|
+
const code = fs17.readFileSync(filePath, "utf-8");
|
|
9662
9690
|
await this.pluginHost.reloadPlugin(pluginId, code);
|
|
9663
9691
|
}
|
|
9664
9692
|
async updateFromZip(zipBuffer, options = {}) {
|
|
@@ -9688,6 +9716,7 @@ var PluginDistributionManager = class {
|
|
|
9688
9716
|
|
|
9689
9717
|
// packages/core/plugin-host/index.ts
|
|
9690
9718
|
init_http_router();
|
|
9719
|
+
var import_meta3 = {};
|
|
9691
9720
|
var VALID_TRANSITIONS = {
|
|
9692
9721
|
["installed" /* INSTALLED */]: ["activating" /* ACTIVATING */, "uninstalled" /* UNINSTALLED */],
|
|
9693
9722
|
["activating" /* ACTIVATING */]: ["active" /* ACTIVE */, "error" /* ERROR */],
|
|
@@ -9718,6 +9747,22 @@ function createPluginStaticMiddleware(absDir) {
|
|
|
9718
9747
|
import_express.default.static(absDir)
|
|
9719
9748
|
];
|
|
9720
9749
|
}
|
|
9750
|
+
function resolveHostSdkDir() {
|
|
9751
|
+
const bases = [];
|
|
9752
|
+
if (typeof __dirname !== "undefined") bases.push(__dirname);
|
|
9753
|
+
try {
|
|
9754
|
+
bases.push(import_path4.default.dirname((0, import_node_url2.fileURLToPath)(import_meta3.url)));
|
|
9755
|
+
} catch {
|
|
9756
|
+
}
|
|
9757
|
+
for (const base of bases) {
|
|
9758
|
+
try {
|
|
9759
|
+
const req = (0, import_node_module.createRequire)(import_path4.default.join(base, "__openlearn_resolve__.js"));
|
|
9760
|
+
return import_path4.default.dirname(req.resolve("@openlearn/plugin-sdk/package.json"));
|
|
9761
|
+
} catch {
|
|
9762
|
+
}
|
|
9763
|
+
}
|
|
9764
|
+
return null;
|
|
9765
|
+
}
|
|
9721
9766
|
var PluginHost = class {
|
|
9722
9767
|
constructor(serviceRegistry, esmLoader, db2, pluginsDir) {
|
|
9723
9768
|
this.serviceRegistry = serviceRegistry;
|
|
@@ -9804,6 +9849,33 @@ var PluginHost = class {
|
|
|
9804
9849
|
getPluginManifestPath(pluginId) {
|
|
9805
9850
|
return import_path4.default.join(this.getPluginDir(pluginId), "manifest.json");
|
|
9806
9851
|
}
|
|
9852
|
+
/**
|
|
9853
|
+
* 确保宿主的 @openlearn/plugin-sdk 能被插件解析到。
|
|
9854
|
+
*
|
|
9855
|
+
* 插件 index.js 位于 `<pluginsDir>/<id>/`,Node 只从该文件所在目录向上查找
|
|
9856
|
+
* node_modules。宿主的 node_modules(npx 缓存、仓库目录等)通常不在其祖先链上,
|
|
9857
|
+
* 于是 external 的 `@openlearn/plugin-sdk` 会抛 ERR_MODULE_NOT_FOUND。
|
|
9858
|
+
* 这里在 `<pluginsDir>/node_modules/@openlearn/plugin-sdk` 建立指向宿主 SDK 的
|
|
9859
|
+
* 链接,使该目录下所有插件共享宿主的同一份 SDK。
|
|
9860
|
+
*/
|
|
9861
|
+
ensureHostSdkResolution() {
|
|
9862
|
+
const linkPath = import_path4.default.join(this.pluginsDir, "node_modules", "@openlearn", "plugin-sdk");
|
|
9863
|
+
if (import_fs5.default.existsSync(linkPath)) return;
|
|
9864
|
+
const sdkDir = resolveHostSdkDir();
|
|
9865
|
+
if (!sdkDir) {
|
|
9866
|
+
console.warn(
|
|
9867
|
+
"[PluginHost] \u672A\u80FD\u5B9A\u4F4D\u5BBF\u4E3B\u7684 @openlearn/plugin-sdk\uFF0C\u63D2\u4EF6\u52A0\u8F7D\u53EF\u80FD\u56E0\u7F3A\u5C11\u8FD0\u884C\u65F6\u4F9D\u8D56\u800C\u5931\u8D25"
|
|
9868
|
+
);
|
|
9869
|
+
return;
|
|
9870
|
+
}
|
|
9871
|
+
try {
|
|
9872
|
+
import_fs5.default.mkdirSync(import_path4.default.dirname(linkPath), { recursive: true });
|
|
9873
|
+
import_fs5.default.symlinkSync(sdkDir, linkPath, "junction");
|
|
9874
|
+
console.log(`[PluginHost] Linked host @openlearn/plugin-sdk: ${linkPath} -> ${sdkDir}`);
|
|
9875
|
+
} catch (err) {
|
|
9876
|
+
console.warn("[PluginHost] \u5EFA\u7ACB @openlearn/plugin-sdk \u94FE\u63A5\u5931\u8D25:", err);
|
|
9877
|
+
}
|
|
9878
|
+
}
|
|
9807
9879
|
// ── V5.2: RESTful API Gateway Dispatcher ───────────────────────────────
|
|
9808
9880
|
/**
|
|
9809
9881
|
* 向插件派发 HTTP 请求(自动路由到 Worker 隔离线程或 Inline 实例)
|
|
@@ -9838,7 +9910,7 @@ var PluginHost = class {
|
|
|
9838
9910
|
/**
|
|
9839
9911
|
* 判断指定插件的某个请求路径是否为流式路由 (SSE)
|
|
9840
9912
|
*/
|
|
9841
|
-
isStreamRoute(pluginIdOrManifestId, method,
|
|
9913
|
+
isStreamRoute(pluginIdOrManifestId, method, path22) {
|
|
9842
9914
|
const upperMethod = method.toUpperCase();
|
|
9843
9915
|
const resolvedId = this.resolvePluginUuid(pluginIdOrManifestId);
|
|
9844
9916
|
const instance = this.pluginInstances.get(resolvedId) ?? this.pluginInstances.get(pluginIdOrManifestId);
|
|
@@ -9847,7 +9919,7 @@ var PluginHost = class {
|
|
|
9847
9919
|
for (const r of manifest.api.routes) {
|
|
9848
9920
|
if (r.method.toUpperCase() === upperMethod && r.streaming === true) {
|
|
9849
9921
|
const compiled = compileRoutePattern(r.path);
|
|
9850
|
-
if (compiled.regex.test(
|
|
9922
|
+
if (compiled.regex.test(path22)) {
|
|
9851
9923
|
return true;
|
|
9852
9924
|
}
|
|
9853
9925
|
}
|
|
@@ -9855,14 +9927,14 @@ var PluginHost = class {
|
|
|
9855
9927
|
}
|
|
9856
9928
|
if (!instance) return false;
|
|
9857
9929
|
if (instance.context?.http) {
|
|
9858
|
-
return instance.context.http.isStream(upperMethod,
|
|
9930
|
+
return instance.context.http.isStream(upperMethod, path22);
|
|
9859
9931
|
}
|
|
9860
9932
|
if (instance.workerRef) {
|
|
9861
9933
|
const routes = instance.workerRef.serviceHost.getRegisteredRoutes();
|
|
9862
9934
|
for (const r of routes) {
|
|
9863
9935
|
if (r.method === upperMethod && r.isStream) {
|
|
9864
9936
|
const compiled = compileRoutePattern(r.pattern);
|
|
9865
|
-
if (compiled.regex.test(
|
|
9937
|
+
if (compiled.regex.test(path22)) {
|
|
9866
9938
|
return true;
|
|
9867
9939
|
}
|
|
9868
9940
|
}
|
|
@@ -10504,7 +10576,8 @@ var PluginHost = class {
|
|
|
10504
10576
|
try {
|
|
10505
10577
|
let mod;
|
|
10506
10578
|
if (process.env.NODE_ENV !== "test" && row.file_path && import_fs5.default.existsSync(row.file_path)) {
|
|
10507
|
-
|
|
10579
|
+
this.ensureHostSdkResolution();
|
|
10580
|
+
const fileUrl = (0, import_node_url2.pathToFileURL)(row.file_path);
|
|
10508
10581
|
mod = await import(`${fileUrl.href}?t=${Date.now()}`);
|
|
10509
10582
|
} else {
|
|
10510
10583
|
let sourceCode = "";
|
|
@@ -10926,8 +10999,14 @@ var PluginHost = class {
|
|
|
10926
10999
|
}
|
|
10927
11000
|
}
|
|
10928
11001
|
const state = this.pluginStates.get(pluginId) ?? "installed" /* INSTALLED */;
|
|
10929
|
-
this.validateTransition(pluginId, state, "uninstalled" /* UNINSTALLED */);
|
|
10930
11002
|
const row = this.db.prepare("SELECT manifest, file_path FROM plugins WHERE id = ?").get(pluginId);
|
|
11003
|
+
if (state === "uninstalled" /* UNINSTALLED */ && !row) {
|
|
11004
|
+
this.pluginStates.delete(pluginId);
|
|
11005
|
+
return;
|
|
11006
|
+
}
|
|
11007
|
+
if (state !== "uninstalled" /* UNINSTALLED */) {
|
|
11008
|
+
this.validateTransition(pluginId, state, "uninstalled" /* UNINSTALLED */);
|
|
11009
|
+
}
|
|
10931
11010
|
const manifestId = (() => {
|
|
10932
11011
|
if (!row) return pluginId;
|
|
10933
11012
|
try {
|
|
@@ -11865,8 +11944,8 @@ var HotReloadController = class {
|
|
|
11865
11944
|
const timer = setTimeout(async () => {
|
|
11866
11945
|
this.debounceTimers.delete(pluginId);
|
|
11867
11946
|
try {
|
|
11868
|
-
const
|
|
11869
|
-
const newSourceCode = await
|
|
11947
|
+
const fs17 = await import("fs/promises");
|
|
11948
|
+
const newSourceCode = await fs17.readFile(filePath, "utf-8");
|
|
11870
11949
|
console.log(`[HotReload] File changed: ${filePath} \u2192 triggering reload for "${pluginId}"`);
|
|
11871
11950
|
await this.pluginHost.reloadPlugin(pluginId, newSourceCode);
|
|
11872
11951
|
} catch (err) {
|
|
@@ -25465,7 +25544,7 @@ async function checkVersion(source, installedVersion) {
|
|
|
25465
25544
|
}
|
|
25466
25545
|
|
|
25467
25546
|
// server/routes/plugin-api-gateway.ts
|
|
25468
|
-
var
|
|
25547
|
+
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
25469
25548
|
init_http_router();
|
|
25470
25549
|
var RESERVED_ACTIONS = /* @__PURE__ */ new Set(["config", "toggle", "contributions", "check-update", "one-click-update"]);
|
|
25471
25550
|
var FORBIDDEN_RESPONSE_HEADERS = /* @__PURE__ */ new Set([
|
|
@@ -25599,7 +25678,7 @@ async function pluginApiGatewayMiddleware(req, res, next) {
|
|
|
25599
25678
|
res.status(400).json({ success: false, error: "Path traversal attempt detected" });
|
|
25600
25679
|
return;
|
|
25601
25680
|
}
|
|
25602
|
-
const normalizedSubPath =
|
|
25681
|
+
const normalizedSubPath = import_node_path5.default.posix.normalize("/" + rawSubPath);
|
|
25603
25682
|
if (normalizedSubPath.includes("..")) {
|
|
25604
25683
|
res.status(400).json({ success: false, error: "Path traversal attempt detected" });
|
|
25605
25684
|
return;
|
|
@@ -26743,24 +26822,22 @@ async function startServer() {
|
|
|
26743
26822
|
const green = "\x1B[32m";
|
|
26744
26823
|
const cyan = "\x1B[36m";
|
|
26745
26824
|
const dim = "\x1B[2m";
|
|
26825
|
+
const hyperlink = Boolean(process.stdout.isTTY) && process.env.TERM !== "dumb";
|
|
26826
|
+
const link = (url) => hyperlink ? `${OSC}${url}${ST}${url}${OSC}${ST}` : url;
|
|
26746
26827
|
console.log(`
|
|
26747
|
-
${bold}${green}
|
|
26828
|
+
${bold}${green}OpenLearn Next${reset} v${PLATFORM_VERSION} ready:
|
|
26748
26829
|
`);
|
|
26749
|
-
console.log(
|
|
26750
|
-
` ${dim}\u279C${reset} ${bold}Local:${reset} ${bold}${cyan}${OSC}${localUrl}${ST}${localUrl}${OSC}${ST}${reset}`
|
|
26751
|
-
);
|
|
26830
|
+
console.log(` ${dim}\u279C${reset} ${bold}Local:${reset} ${bold}${cyan}${link(localUrl)}${reset}`);
|
|
26752
26831
|
if (isAnyHost) {
|
|
26753
26832
|
const netIps = getNetworkIps();
|
|
26754
26833
|
for (const ip of netIps) {
|
|
26755
26834
|
const netUrl = `http://${ip}:${PORT}`;
|
|
26756
|
-
console.log(
|
|
26757
|
-
` ${dim}\u279C${reset} ${bold}Network:${reset} ${bold}${cyan}${OSC}${netUrl}${ST}${netUrl}${OSC}${ST}${reset}`
|
|
26758
|
-
);
|
|
26835
|
+
console.log(` ${dim}\u279C${reset} ${bold}Network:${reset} ${bold}${cyan}${link(netUrl)}${reset}`);
|
|
26759
26836
|
}
|
|
26760
26837
|
}
|
|
26761
26838
|
console.log("");
|
|
26762
26839
|
if (process.env.OPEN_BROWSER === "true") {
|
|
26763
|
-
console.log(` ${dim}\u279C Auto-opening browser: ${primaryUrl}${reset}
|
|
26840
|
+
console.log(` ${dim}\u279C Auto-opening browser: ${cyan}${link(primaryUrl)}${reset}
|
|
26764
26841
|
`);
|
|
26765
26842
|
openBrowser(primaryUrl);
|
|
26766
26843
|
}
|
package/package.json
CHANGED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import{r as h,j as e,R as Z}from"./vendor-react-B0t7H3hh.js";import{aJ as ue,X as ce,z as J,i as $e,S as ve,_ as pe,f as Ne,an as Pe,aK as Ue,a4 as be,aw as Te,aE as De,U as Me,aL as we,a5 as le,O as Ee,aC as me,r as de,d as ze,$ as Re,x as ke,aM as Fe,v as Ae,ap as Le}from"./vendor-icons-B2DRJfZU.js";import{n as Oe}from"./vendor-pptx-C5-U7ieS.js";import{u as ge}from"./index-hTc1h9s2.js";import"./vendor-utils-m0Urvtws.js";import"./vendor-pdf-Bw0ZQZtM.js";import"./vendor-motion-DjY2czvL.js";import"./vendor-charts-U9fLlNC_.js";function Ve({pluginId:r,pluginName:s,manifestStr:t,lang:d,onClose:p}){const[u,v]=h.useState(null),[g,k]=h.useState({}),[x,D]=h.useState(!0),[C,L]=h.useState(!1),[A,$]=h.useState(null),o=h.useCallback(async()=>{D(!0);try{const m=await fetch(`/api/plugins/${encodeURIComponent(r)}/config`);if(m.ok){const b=await m.json();if(b.success){v(b.result.schema),k(b.result.values);return}}}catch{}try{const b=JSON.parse(t).configuration?.properties??{};v(b);const l={};for(const[P,R]of Object.entries(b))l[P]=R.default;k(l)}catch{v({})}D(!1)},[r,t]);h.useEffect(()=>{o().finally(()=>D(!1))},[o]);const n=async()=>{L(!0),$(null);try{const b=await(await fetch(`/api/plugins/${encodeURIComponent(r)}/config`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(g)})).json();b.success?$(d==="zh"?"✓ 已保存":"✓ Saved"):$(`✗ ${b.error}`)}catch(m){$(`✗ ${m.message}`)}L(!1)},c=(m,b)=>{k(l=>({...l,[m]:b})),$(null)},j=(m,b)=>d==="zh"?m:b,N=(m,b)=>{const l=g[m],P=b.description??"";switch(b.type){case"boolean":return e.jsxs("div",{className:"flex items-center justify-between py-2.5 border-b border-gray-100",children:[e.jsxs("div",{className:"flex-1",children:[e.jsx("label",{className:"text-sm font-medium text-gray-700",children:m}),P&&e.jsx("p",{className:"text-xs text-gray-400 mt-0.5",children:P})]}),e.jsx("button",{type:"button",onClick:()=>c(m,!l),className:`relative w-10 h-5 rounded-full transition-colors ${l?"bg-indigo-600":"bg-gray-300"}`,children:e.jsx("span",{className:`absolute top-0.5 w-4 h-4 rounded-full bg-white shadow transition-transform ${l?"translate-x-5":"translate-x-0.5"}`})})]},m);case"number":case"integer":return e.jsxs("div",{className:"py-2.5 border-b border-gray-100",children:[e.jsx("label",{className:"text-sm font-medium text-gray-700 block mb-1",children:m}),P&&e.jsx("p",{className:"text-xs text-gray-400 mb-1.5",children:P}),e.jsx("input",{type:"number",step:b.type==="integer"?1:"any",min:b.minimum,max:b.maximum,value:l!=null?String(l):"",onChange:R=>{const U=R.target.value===""?void 0:Number(R.target.value);c(m,U)},className:"w-full px-3 py-1.5 text-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-400 outline-none",placeholder:b.default!==void 0?String(b.default):""})]},m);default:return b.enum&&b.enum.length>0?e.jsxs("div",{className:"py-2.5 border-b border-gray-100",children:[e.jsx("label",{className:"text-sm font-medium text-gray-700 block mb-1",children:m}),P&&e.jsx("p",{className:"text-xs text-gray-400 mb-1.5",children:P}),e.jsx("select",{value:String(l??b.default??""),onChange:R=>c(m,R.target.value),className:"w-full px-3 py-1.5 text-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-400 outline-none bg-white",children:b.enum.map(R=>e.jsx("option",{value:String(R),children:String(R)},String(R)))})]},m):e.jsxs("div",{className:"py-2.5 border-b border-gray-100",children:[e.jsx("label",{className:"text-sm font-medium text-gray-700 block mb-1",children:m}),P&&e.jsx("p",{className:"text-xs text-gray-400 mb-1.5",children:P}),e.jsx("input",{type:"text",value:l!=null?String(l):"",onChange:R=>c(m,R.target.value),className:"w-full px-3 py-1.5 text-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-400 outline-none",placeholder:b.default!==void 0?String(b.default):""})]},m)}},i=u&&Object.keys(u).length>0;return e.jsx("div",{className:"fixed inset-0 bg-black/40 flex items-center justify-center z-50 p-4",onClick:p,children:e.jsxs("div",{className:"bg-white rounded-xl shadow-xl w-full max-w-md flex flex-col max-h-[85vh]",onClick:m=>m.stopPropagation(),children:[e.jsxs("div",{className:"p-4 border-b border-gray-100 flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(ue,{size:18,className:"text-gray-500"}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-semibold text-gray-800 text-sm",children:j("插件设置","Plugin Settings")}),e.jsx("p",{className:"text-xs text-gray-400",children:s})]})]}),e.jsx("button",{onClick:p,className:"text-gray-400 hover:text-gray-600 p-1",children:e.jsx(ce,{size:18})})]}),e.jsx("div",{className:"p-4 flex-1 overflow-auto",children:x?e.jsx("div",{className:"flex items-center justify-center py-12",children:e.jsx(J,{size:24,className:"text-gray-400 animate-spin"})}):i?e.jsx("div",{className:"space-y-0.5",children:Object.entries(u).map(([m,b])=>N(m,b))}):e.jsxs("div",{className:"text-center py-8",children:[e.jsx(ue,{size:32,className:"text-gray-300 mx-auto mb-3"}),e.jsx("p",{className:"text-sm text-gray-400",children:j("此插件没有可配置项。","This plugin has no configurable settings.")}),e.jsx("p",{className:"text-xs text-gray-300 mt-1",children:j("在 manifest.configuration 中声明配置项即可在此处显示。","Declare settings in manifest.configuration to see them here.")})]})}),i&&e.jsxs("div",{className:"p-4 border-t border-gray-100 flex items-center justify-between",children:[A&&e.jsx("span",{className:`text-xs ${A.startsWith("✓")?"text-green-600":"text-red-500"}`,children:A}),e.jsxs("div",{className:"flex gap-2 ml-auto",children:[e.jsx("button",{onClick:p,className:"px-4 py-2 text-xs font-medium text-gray-600 hover:bg-gray-50 rounded-lg transition-colors",children:j("取消","Cancel")}),e.jsxs("button",{onClick:n,disabled:C,className:"px-4 py-2 text-xs font-medium bg-indigo-600 text-white hover:bg-indigo-700 rounded-lg transition-colors flex items-center gap-1.5 disabled:opacity-50",children:[C?e.jsx(J,{size:14,className:"animate-spin"}):e.jsx($e,{size:14}),j("保存","Save")]})]})]})]})})}function he(r){return r.replace(/\\u\{([0-9a-fA-F]+)\}/g,(s,t)=>{try{return String.fromCodePoint(parseInt(t,16))}catch{return s}}).replace(/\\u([0-9a-fA-F]{4})/g,(s,t)=>String.fromCharCode(parseInt(t,16))).replace(/\\x([0-9a-fA-F]{2})/g,(s,t)=>String.fromCharCode(parseInt(t,16))).replace(/\\n/g,`
|
|
2
|
-
`).replace(/\\t/g," ").replace(/\\r/g,"\r").replace(/\\'/g,"'").replace(/\\"/g,'"').replace(/\\\\/g,"\\")}function fe(r,s){if(!r)return s;for(const t of["label","name","title","displayName"]){const d=r[t];if(typeof d=="string"&&d.trim())return d.trim()}return s}function je(r){if(!r||typeof r!="object")return[];const s=[],t=new Set,d=(u,v,g)=>{const k=`${u}::${v}`;t.has(k)||(t.add(k),s.push({slot:u,id:v,label:g||v||u,source:"manifest"}))},p=r.contributes;if(p&&typeof p=="object"){for(const[u,v]of Object.entries(p))if(Array.isArray(v))for(const g of v){const k=g&&typeof g=="object"?g:{},x=typeof k.id=="string"?k.id:"?";d(u,x,fe(k,x))}}if(Array.isArray(r.classroomTools))for(const u of r.classroomTools){const v=u&&typeof u=="object"?u:{},g=typeof v.id=="string"?v.id:"?";d("classroom.tool",g,fe(v,g))}return s}function qe(r){if(!r)return[];const s=[],t=/registerExtensionPoint\s*\(\s*['"]([^'"]+)['"]\s*,\s*\{((?:[^{}]|\{[^{}]*\})*)\}/g;let d;for(;(d=t.exec(r))!==null;){const p=he(d[1]),u=d[2],v=u.match(/\bid\s*:\s*['"]([^'"]*)['"]/),g=u.match(/\blabel\s*:\s*['"]([^'"]*)['"]/),k=u.match(/\bname\s*:\s*['"]([^'"]*)['"]/),x=he(v?.[1]||"?"),D=g?.[1]??k?.[1]??x;s.push({slot:p,id:x,label:he(D)||x||p,source:"frontend"})}return s}function Ze(r,s){const t=new Map;for(const d of r)t.set(`${d.slot}::${d.id}`,d);for(const d of s){const p=`${d.slot}::${d.id}`,u=t.get(p);if(!u){t.set(p,d);continue}const v=u.label&&u.label!==u.id&&u.label!==u.slot?u.label:d.label;t.set(p,{...u,label:v||u.label})}return Array.from(t.values())}const Be=new Set(["teacher.tab","teacher.dashboard.widget","workspace.view","classroom.tool","command.palette"]),We=new Set(["student.view","student.dashboard.widget"]);function ye(r){if(!r||typeof r!="string")return"0.0.0";const s=r.trim().match(/(\d+)(?:\.(\d+))?(?:\.(\d+))?/);return s?`${s[1]}.${s[2]||"0"}.${s[3]||"0"}`:"0.0.0"}function _e(r,s){const t=ye(r).split(".").map(p=>parseInt(p,10)),d=ye(s).split(".").map(p=>parseInt(p,10));for(let p=0;p<3;p++){if(t[p]<d[p])return-1;if(t[p]>d[p])return 1}return 0}function Je({isOpen:r,onClose:s,lang:t,file:d,onConfirmInstall:p,lockedTargetPluginId:u=null,installedPlugins:v=[]}){const[g,k]=h.useState(0),[x,D]=h.useState(null),[C,L]=h.useState([]),[A,$]=h.useState([]),[o,n]=h.useState(null),[c,j]=h.useState(!1),[N,i]=h.useState(!1),[m,b]=h.useState("worker"),[l,P]=h.useState(null),[R,U]=h.useState(null),[ee,ie]=h.useState(!1),[G,Q]=h.useState(!1),[te,_]=h.useState(""),[se,M]=h.useState(0),[q,K]=h.useState("teacher"),[E,re]=h.useState([]),[oe,ae]=h.useState(!1),[T,H]=h.useState(null),[X,f]=h.useState(!1),[w,F]=h.useState(!1);h.useEffect(()=>{r&&d&&(k(0),D(null),L([]),n(null),j(!1),i(!1),b("worker"),P(null),U(null),re([]),$([]),K("teacher"),H(null),f(!1),F(!1),O(d))},[r,d]);const O=async a=>{ie(!0),U(null);try{const y=await Oe.loadAsync(a);P(y);const S=y.file("manifest.json");if(!S)throw new Error(t==="zh"?"ZIP 包中缺少 manifest.json 声明文件":"manifest.json missing in ZIP package");const I=await S.async("string"),V=JSON.parse(I);if(!V.id||!V.name)throw new Error(t==="zh"?"manifest.json 缺少必填字段 id 或 name":"manifest.json missing required id or name field");D(V),L(V.capabilitiesProposed||[]),n(V.contributes||null),j(!!y.file("frontend.js")),re(je(V)),V.executionMode==="inline"&&b("inline"),await B(V)}catch(y){U(y.message||"ZIP parse error")}finally{ie(!1)}},B=async a=>{if(H(null),f(!1),F(!1),!a?.id)return;if(u){const S=v.find(V=>V.id===u);let I=null;try{I=S?JSON.parse(S.manifest):null}catch{I=null}if(I?.id&&I.id!==a.id){U(t==="zh"?`ZIP 的插件 ID(${a.id})与当前卡片(${I.id})不一致,无法更新。`:`ZIP manifest.id (${a.id}) does not match this plugin (${I.id}).`);return}if(String(a.id).startsWith("@openlearn/")){U(t==="zh"?"系统核心插件禁止通过插件中心更新。":"System plugins cannot be updated here.");return}H({pluginId:u,name:S?.name||I?.name||a.name,version:S?.version||I?.version||"0.0.0",status:S?.status||"unknown",isSystem:!1,manifestId:a.id});return}const y=v.find(S=>{try{return JSON.parse(S.manifest)?.id===a.id}catch{return!1}});if(y){let S={};try{S=JSON.parse(y.manifest)}catch{S={}}if(String(a.id).startsWith("@openlearn/")||y.id.startsWith("@openlearn/")){U(t==="zh"?"系统核心插件禁止通过插件中心更新。":"System plugins cannot be updated here.");return}H({pluginId:y.id,name:y.name||S.name||a.name,version:y.version||S.version||"0.0.0",status:y.status,isSystem:!1,manifestId:a.id});return}try{const I=await(await fetch(`/api/plugins/by-manifest/${encodeURIComponent(a.id)}`)).json();if(I?.success&&I.installed){if(I.isSystem){U(t==="zh"?"系统核心插件禁止通过插件中心更新。":"System plugins cannot be updated here.");return}H({pluginId:I.pluginId,name:I.name,version:I.version||"0.0.0",status:I.status,isSystem:!!I.isSystem,manifestId:a.id})}}catch(S){console.warn("Failed to detect existing plugin install",S)}};h.useEffect(()=>{if(g!==3||!l)return;let a=!1;return(async()=>{ae(!0);try{const y=l.file("frontend.js"),S=y?await y.async("string"):"",I=je(x),V=qe(S);if(a)return;re(Ze(I,V)),$([{name:"index.js",size:l.file("index.js")?(await l.file("index.js").async("string")).length:0},{name:"frontend.js",size:S.length},{name:"manifest.json",size:l.file("manifest.json")?(await l.file("manifest.json").async("string")).length:0}])}catch(y){console.warn("Extension point preview failed:",y)}finally{a||ae(!1)}})(),()=>{a=!0}},[g,l,x,c]);const Y=h.useMemo(()=>{const a=q==="teacher"?Be:We,y=E.filter(S=>a.has(S.slot));return y.length>0?y:E},[E,q]),z=!!T,W=z&&_e(x?.version||"0.0.0",T.version)<0,ne=z&&T.status==="active"&&(E.some(a=>["classroom.tool","student.view","teacher.tab","workspace.view"].includes(a.slot))||!!(x?.classroomTools&&x.classroomTools.length>0)||!!(x?.contributes&&Object.keys(x.contributes).length>0)),xe=!(!d||!x||T?.isSystem||W&&!X||ne&&!w),Se=async()=>{if(!(!d||!xe)){Q(!0),U(null),M(10),_(t==="zh"?z?"正在上传并更新...":"正在上传并安装...":z?"Uploading and updating...":"Uploading and installing...");try{await p(d,m,{mode:z?"update":"install",targetPluginId:T?.pluginId,allowDowngrade:W?X:!1}),M(100),_(t==="zh"?z?"更新完成":"安装完成":z?"Updated":"Complete"),s()}catch(a){console.error(a),U(a?.message||(t==="zh"?z?"更新失败":"安装失败":z?"Update failed":"Installation failed")),M(0),_("")}finally{Q(!1)}}},Ce=async()=>{if(!(!d||ee||G)){i(!0),f(!0),F(!0),Q(!0),U(null),M(20),_(t==="zh"?z?"极速同意全套授权,正在更新...":"极速同意全套授权,正在安装...":z?"Express approving terms & updating...":"Express approving terms & installing...");try{await p(d,m,{mode:z?"update":"install",targetPluginId:T?.pluginId,allowDowngrade:!0}),M(100),_(t==="zh"?z?"更新完成":"安装完成":z?"Updated":"Complete"),s()}catch(a){console.error(a),U(a?.message||(t==="zh"?z?"更新失败":"安装失败":z?"Update failed":"Installation failed")),M(0),_("")}finally{Q(!1)}}};if(!r)return null;const Ie=a=>{const y=["db:schema","management:write","network:access"],S=["whiteboard:write","vfs:write","lesson:write","class:write"];return y.includes(a)?{level:"high",label:t==="zh"?"🔴 高危":"🔴 High",desc:t==="zh"?"可读写数据库、获取网络数据,存在敏感信息暴露与跨站点数据隐患。":"Can read/write DB or access network, sensitive data risk.",rowClass:"bg-rose-50/80 border-rose-200/90 hover:border-rose-300 shadow-2xs text-rose-950",badgeClass:"bg-rose-100 text-rose-700 border-rose-200 font-extrabold",codeClass:"bg-white border-rose-200 text-rose-900",descClass:"text-rose-800/90"}:S.includes(a)?{level:"medium",label:t==="zh"?"🟡 中危":"🟡 Medium",desc:t==="zh"?"可在课表、白板中进行绘制修改或写入局部业务持久化数据。":"Can write to whiteboards or control course schedules.",rowClass:"bg-amber-50/80 border-amber-200/90 hover:border-amber-300 shadow-2xs text-amber-950",badgeClass:"bg-amber-100 text-amber-800 border-amber-200 font-extrabold",codeClass:"bg-white border-amber-200 text-amber-900",descClass:"text-amber-800/90"}:{level:"low",label:t==="zh"?"🟢 低危":"🟢 Low",desc:t==="zh"?"常规的基础界面弹窗与局域隔离存储能力,安全可信。":"Basic UI elements or isolated local storage.",rowClass:"bg-emerald-50/70 border-emerald-200/90 hover:border-emerald-300 shadow-2xs text-emerald-950",badgeClass:"bg-emerald-100 text-emerald-800 border-emerald-200 font-extrabold",codeClass:"bg-white border-emerald-200 text-emerald-900",descClass:"text-emerald-800/90"}};return e.jsx("div",{className:"fixed inset-0 bg-slate-900/60 backdrop-blur-2xs flex items-center justify-center overflow-y-auto select-none",style:{zIndex:9999},children:e.jsxs("div",{className:"bg-white border border-slate-200 shadow-2xl rounded-2xl w-full max-w-2xl p-6 md:p-7 relative flex flex-col justify-between max-h-[90vh]",children:[e.jsxs("div",{className:"flex justify-between items-center border-b border-slate-100 pb-4 shrink-0",children:[e.jsxs("h2",{className:"font-extrabold text-base md:text-lg text-slate-800 flex items-center gap-2",children:[e.jsx(ve,{className:"text-indigo-600 animate-pulse",size:20}),t==="zh"?`${z?"三方插件更新向导":"三方插件安装向导"} (${g+1}/5)`:`${z?"Plugin Update Wizard":"Plugin Setup Wizard"} (${g+1}/5)`]}),e.jsx("button",{onClick:s,className:"text-gray-400 hover:text-gray-650 cursor-pointer",children:e.jsx(ce,{size:18})})]}),e.jsx("div",{className:"flex-1 overflow-y-auto py-5 min-h-[300px]",children:ee?e.jsxs("div",{className:"flex flex-col items-center justify-center py-20 text-indigo-600 gap-2",children:[e.jsx(J,{size:36,className:"animate-spin"}),e.jsx("span",{className:"text-sm font-semibold",children:t==="zh"?"正在解压并审计文件...":"Extracting and auditing package..."})]}):R?e.jsxs("div",{className:"bg-red-50 border border-red-200 rounded-xl p-4 text-red-700 flex items-start gap-2.5",children:[e.jsx(pe,{size:18,className:"shrink-0 mt-0.5"}),e.jsxs("div",{children:[e.jsx("h4",{className:"font-bold text-sm",children:t==="zh"?"校验未通过,安装中断":"Verification Failed"}),e.jsx("p",{className:"text-xs mt-1 leading-relaxed",children:R})]})]}):e.jsxs(e.Fragment,{children:[g===0&&x&&e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"bg-indigo-50/50 border border-indigo-100 rounded-xl p-4 flex gap-4",children:[e.jsx("div",{className:"w-12 h-12 bg-indigo-600 text-white rounded-lg flex items-center justify-center shrink-0 shadow-sm font-bold text-lg uppercase",children:x.name.charAt(0)}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-extrabold text-slate-900 text-base",children:x.name}),e.jsxs("p",{className:"text-xs font-mono text-gray-500 mt-1",children:["ID: ",x.id," | v",x.version||"1.0.0"]})]})]}),e.jsxs("div",{children:[e.jsx("h4",{className:"font-bold text-slate-700 text-xs mb-1.5",children:t==="zh"?"功能描述":"Description"}),e.jsx("p",{className:"text-xs text-gray-600 leading-relaxed bg-slate-50 border border-slate-100 p-3 rounded-lg font-medium",children:x.description||(t==="zh"?"该插件暂无详细描述信息。":"No description provided for this plugin.")})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4 mt-2",children:[e.jsxs("div",{className:"border border-slate-100 rounded-xl p-3 bg-slate-50/30",children:[e.jsx("span",{className:"text-xs text-gray-400 block font-semibold",children:t==="zh"?"主程序入口":"Main Entry"}),e.jsx("span",{className:"text-xs font-bold text-slate-700 font-mono mt-0.5 block",children:x.main||"index.js"})]}),e.jsxs("div",{className:"border border-slate-100 rounded-xl p-3 bg-slate-50/30",children:[e.jsx("span",{className:"text-xs text-gray-400 block font-semibold",children:t==="zh"?"前端沙箱包":"Frontend UI Asset"}),e.jsx("span",{className:`text-xs font-bold mt-0.5 block ${c?"text-indigo-600":"text-gray-500"}`,children:c?t==="zh"?"包含 (frontend.js)":"Included (frontend.js)":t==="zh"?"无":"None"})]})]}),T&&e.jsxs("div",{className:`mt-1 rounded-xl border p-4 ${W?"border-red-200 bg-red-50/50":"border-amber-200 bg-amber-50/40"}`,children:[e.jsx("h4",{className:"text-xs font-extrabold text-slate-800 mb-2",children:t==="zh"?"检测到已安装同一插件 — 将执行原地更新":"Same plugin already installed — in-place update"}),e.jsxs("div",{className:"grid grid-cols-2 gap-2 text-xs",children:[e.jsxs("div",{className:"bg-white/70 border border-slate-100 rounded-lg p-2",children:[e.jsx("div",{className:"text-slate-400 font-semibold",children:t==="zh"?"当前版本":"Installed"}),e.jsxs("div",{className:"font-bold text-slate-800 mt-0.5",children:[T.name,e.jsxs("span",{className:"font-mono text-slate-500 ml-1",children:["v",T.version]})]}),e.jsx("div",{className:"text-xs text-slate-400 mt-1 font-mono",children:T.status==="active"?t==="zh"?"状态: 运行中":"status: active":t==="zh"?"状态: 未启用":"status: inactive"})]}),e.jsxs("div",{className:"bg-white/70 border border-slate-100 rounded-lg p-2",children:[e.jsx("div",{className:"text-slate-400 font-semibold",children:t==="zh"?"ZIP 新版本":"Package"}),e.jsxs("div",{className:"font-bold text-slate-800 mt-0.5",children:[x.name,e.jsxs("span",{className:"font-mono text-indigo-600 ml-1",children:["v",x.version||"0.0.0"]})]}),e.jsx("div",{className:"text-xs text-slate-400 mt-1",children:t==="zh"?"保留配置 / 业务数据 / UUID":"Keeps config, data & UUID"})]})]}),W&&e.jsxs("label",{className:"mt-3 flex items-start gap-2 text-xs font-bold text-red-700 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:X,onChange:a=>f(a.target.checked)}),e.jsx("span",{children:t==="zh"?`确认强制降级:v${T.version} → v${x.version||"0.0.0"}(可能不兼容)`:`Force downgrade v${T.version} → v${x.version||"0.0.0"} (may be incompatible)`})]}),ne&&e.jsxs("label",{className:"mt-2 flex items-start gap-2 text-xs font-bold text-amber-800 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:w,onChange:a=>F(a.target.checked)}),e.jsx("span",{children:t==="zh"?"该插件当前为启用状态且声明了课堂/教学扩展点,热更新可能导致课中 UI 短暂异常。我已知晓并继续。":"Plugin is active with classroom UI contributions. Hot-update may briefly disrupt live class UI. I understand."})]})]})]}),g===1&&e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx("p",{className:"text-xs text-slate-500 font-medium mb-1",children:t==="zh"?"此插件申请了以下系统权限,请仔细评估安装后可能产生的风控隐患:":"This plugin requests the following capabilities, please review carefully:"}),C.length===0?e.jsx("div",{className:"text-center p-8 bg-emerald-50 border border-emerald-100 rounded-xl text-emerald-700 font-bold text-xs",children:t==="zh"?"✓ 该插件未申请任何敏感系统权限,可安全安装。":"✓ This plugin requires no system capabilities, very safe."}):e.jsx("div",{className:"space-y-3 max-h-[220px] overflow-y-auto pr-1",children:C.map(a=>{const y=Ie(a);return e.jsxs("div",{className:`border rounded-xl p-3.5 flex flex-col gap-1 transition-all ${y.rowClass}`,children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:`font-mono text-xs font-bold rounded px-2 py-0.5 shadow-2xs ${y.codeClass}`,children:a}),e.jsx("span",{className:`text-xs px-2 py-0.5 rounded-md border ${y.badgeClass}`,children:y.label})]}),e.jsx("p",{className:`text-xs leading-relaxed font-medium mt-1 ${y.descClass}`,children:y.desc})]},a)})}),C.length>0&&e.jsxs("label",{className:"flex items-start gap-2 border border-orange-100 rounded-xl p-3 bg-orange-50/30 cursor-pointer mt-2 text-xs text-orange-850 font-bold leading-normal",children:[e.jsx("input",{type:"checkbox",checked:N,onChange:a=>i(a.target.checked),className:"mt-0.5 w-3.5 h-3.5 rounded text-orange-600 border-orange-300 focus:ring-orange-500 cursor-pointer shrink-0"}),e.jsx("span",{children:t==="zh"?"我已了解并信任此插件的提供商,愿意授予上述高级系统权限。":"I trust the developer and agree to grant all requested capabilities."})]})]}),g===2&&e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx("p",{className:"text-xs text-slate-500 font-medium",children:t==="zh"?"插件将向系统注册并扩展以下槽位(Slots)与自定义命令:":"The plugin will inject UI slots or register custom commands:"}),e.jsx("div",{className:"border border-slate-150/65 rounded-xl p-4 bg-slate-50/50 space-y-4",children:o?e.jsxs(e.Fragment,{children:[o.teacher?.dashboard?.widget&&e.jsxs("div",{className:"text-xs",children:[e.jsx("span",{className:"font-bold text-slate-700 block mb-1",children:"插槽: 教师端侧栏卡片"}),e.jsxs("span",{className:"text-gray-500 font-mono",children:["teacher.dashboard.widget → ",o.teacher.dashboard.widget]})]}),o.student?.view&&e.jsxs("div",{className:"text-xs",children:[e.jsx("span",{className:"font-bold text-slate-700 block mb-1",children:"插槽: 学生答题看板"}),e.jsxs("span",{className:"text-gray-500 font-mono",children:["student.view → ",o.student.view]})]}),x.capabilitiesProposed&&x.capabilitiesProposed.length>0&&e.jsxs("div",{className:"text-xs border-t border-slate-100 pt-3",children:[e.jsx("span",{className:"font-bold text-slate-700 block mb-1",children:t==="zh"?"注册指令集":"Commands Registered"}),e.jsx("div",{className:"flex gap-1.5 flex-wrap",children:x.capabilitiesProposed.map(a=>e.jsx("span",{className:"bg-indigo-50 border border-indigo-150/60 rounded px-1.5 py-0.5 text-indigo-700 font-mono",children:a.replace(":write",".create")},a))})]})]}):e.jsx("div",{className:"text-center text-xs text-slate-400 italic py-6",children:t==="zh"?"此插件没有提供任何额外的槽位注册项。":"This plugin contributes no extension points."})})]}),g===3&&e.jsxs("div",{className:"flex flex-col gap-3 h-full",children:[e.jsxs("div",{className:"flex items-center justify-between shrink-0 gap-3",children:[e.jsx("p",{className:"text-xs text-slate-500 font-semibold",children:t==="zh"?"检测到的扩展点(静态分析预览):":"Detected extension points (static analysis):"}),e.jsxs("div",{className:"flex bg-slate-100 p-0.5 rounded-lg border border-slate-200 shrink-0",children:[e.jsx("button",{type:"button",onClick:()=>K("teacher"),className:`px-3 py-1 rounded-md text-xs font-bold transition-all cursor-pointer ${q==="teacher"?"bg-white shadow text-indigo-650":"text-slate-550 hover:text-slate-800"}`,children:t==="zh"?"教师端相关":"Teacher slots"}),e.jsx("button",{type:"button",onClick:()=>K("student"),className:`px-3 py-1 rounded-md text-xs font-bold transition-all cursor-pointer ${q==="student"?"bg-white shadow text-indigo-650":"text-slate-550 hover:text-slate-800"}`,children:t==="zh"?"学生端相关":"Student slots"})]})]}),e.jsx("div",{className:"border border-slate-200/80 rounded-xl p-4 bg-slate-50/50 flex-grow overflow-auto min-h-[220px] max-h-[300px]",children:e.jsx("div",{className:"h-full bg-white rounded-lg p-4 border border-slate-100 shadow-sm",children:oe?e.jsxs("div",{className:"flex flex-col items-center justify-center h-full gap-2 text-indigo-600",children:[e.jsx(J,{size:22,className:"animate-spin"}),e.jsx("span",{className:"text-xs font-semibold",children:t==="zh"?"正在分析扩展点…":"Analyzing extension points…"})]}):Y.length===0?e.jsx("div",{className:"flex items-center justify-center h-full text-center text-xs text-slate-400 italic px-4",children:t==="zh"?"未检测到扩展点注册(manifest.contributes / frontend.js)。":"No extension points detected in manifest or frontend.js."}):e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx("div",{className:"text-xs font-semibold text-slate-500",children:t==="zh"?`共 ${Y.length} 项(按「${q==="teacher"?"教师端":"学生端"}」筛选)`:`${Y.length} item(s) for ${q} view`}),e.jsx("ul",{className:"space-y-2",children:Y.map(a=>e.jsxs("li",{className:"flex items-start justify-between gap-3 rounded-lg border border-slate-100 bg-slate-50/60 px-3 py-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-sm font-bold text-slate-800 truncate",children:a.label}),e.jsxs("div",{className:"text-xs text-slate-500 font-mono mt-0.5 truncate",children:[a.slot,a.id&&a.id!=="?"?` · ${a.id}`:""]})]}),e.jsx("span",{className:`shrink-0 text-xs font-semibold px-1.5 py-0.5 rounded border ${a.source==="manifest"?"bg-emerald-50 text-emerald-700 border-emerald-100":"bg-indigo-50 text-indigo-700 border-indigo-100"}`,children:a.source==="manifest"?"manifest":"frontend.js"})]},`${a.source}-${a.slot}-${a.id}`))}),A.length>0&&e.jsxs("div",{className:"pt-2 border-t border-slate-100",children:[e.jsx("div",{className:"text-xs font-semibold text-slate-400 mb-1",children:t==="zh"?"包内文件":"Package files"}),e.jsx("div",{className:"flex flex-wrap gap-1.5",children:A.map(a=>e.jsxs("span",{className:"text-xs font-mono bg-white border border-slate-150 rounded px-1.5 py-0.5 text-slate-600",children:[a.name,a.size>0?` (${Math.max(1,Math.round(a.size/1024))} KB)`:""]},a.name))})]})]})})}),e.jsxs("div",{className:"flex items-center gap-1.5 text-xs text-gray-550 font-medium shrink-0 bg-indigo-50/40 p-2 border border-indigo-100/40 rounded-lg",children:[e.jsx(Ne,{size:11,className:"text-indigo-600"}),e.jsx("span",{children:t==="zh"?"此步骤为静态扫描结果,不会执行插件代码。中文标签已从 JS 转义序列还原;优先展示 manifest 声明,frontend.js 作为补充。":"Static scan only — plugin code is not executed. Unicode escapes in JS labels are decoded; manifest entries take priority over frontend.js."})]})]}),g===4&&e.jsxs("div",{className:"flex flex-col gap-4",children:[T&&e.jsxs("div",{className:`rounded-xl border p-3 text-xs ${W?"border-red-200 bg-red-50/60":"border-amber-200 bg-amber-50/50"}`,children:[e.jsx("div",{className:"font-extrabold text-slate-800 mb-1",children:t==="zh"?`更新确认:v${T.version} → v${x?.version||"0.0.0"}`:`Update: v${T.version} → v${x?.version||"0.0.0"}`}),e.jsx("div",{className:"text-slate-600",children:t==="zh"?"将原地替换代码与静态资源,保留 UUID、配置与业务数据。":"Replaces code/assets in place. UUID, config and data are preserved."}),W&&e.jsxs("label",{className:"mt-2 flex items-start gap-2 font-bold text-red-700 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:X,onChange:a=>f(a.target.checked)}),e.jsx("span",{children:t==="zh"?"确认强制降级":"Confirm forced downgrade"})]}),ne&&e.jsxs("label",{className:"mt-2 flex items-start gap-2 font-bold text-amber-800 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:w,onChange:a=>F(a.target.checked)}),e.jsx("span",{children:t==="zh"?"已知晓课中热更新风险":"I accept live-class hot-update risk"})]}),!xe&&e.jsx("p",{className:"mt-2 text-red-600 font-semibold",children:t==="zh"?"请先完成上方确认项后再更新。":"Complete the confirmation checks above before updating."})]}),e.jsx("p",{className:"text-xs text-slate-500 font-medium",children:t==="zh"?"设置该插件启动时的微前端沙箱执行模式:":"Configure micro-frontend sandbox execution model:"}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("label",{className:`border rounded-xl p-4 flex gap-3 cursor-pointer transition-all ${m==="worker"?"bg-indigo-50/40 border-indigo-300 shadow-xs":"border-slate-150 hover:border-indigo-200 bg-white"}`,children:[e.jsx("input",{type:"radio",name:"sandbox_mode",checked:m==="worker",onChange:()=>b("worker"),className:"mt-1 w-4 h-4 text-indigo-600 border-gray-300 focus:ring-indigo-500 cursor-pointer shrink-0"}),e.jsxs("div",{children:[e.jsx("h4",{className:"font-extrabold text-xs md:text-sm text-slate-800",children:t==="zh"?"Worker 强沙箱隔离模式 (推荐)":"Worker Sandbox Isolation (Recommended)"}),e.jsx("p",{className:"text-xs text-slate-550 mt-1 leading-normal font-medium",children:t==="zh"?"插件代码在独立的后台 Worker 线程中加载运行。即便发生死循环、崩溃抛错也不会株连系统主服务崩溃,安全性最高。":"Loads in isolated background worker threads. Uncaught plugin errors won't crash main system."})]})]}),e.jsxs("label",{className:`border rounded-xl p-4 flex gap-3 cursor-pointer transition-all ${m==="inline"?"bg-indigo-50/40 border-indigo-300 shadow-xs":"border-slate-150 hover:border-indigo-200 bg-white"}`,children:[e.jsx("input",{type:"radio",name:"sandbox_mode",checked:m==="inline",onChange:()=>b("inline"),className:"mt-1 w-4 h-4 text-indigo-600 border-gray-300 focus:ring-indigo-500 cursor-pointer shrink-0"}),e.jsxs("div",{children:[e.jsx("h4",{className:"font-extrabold text-xs md:text-sm text-slate-800",children:t==="zh"?"VM Inline 主进程运行模式":"VM Inline Execution"}),e.jsx("p",{className:"text-xs text-slate-550 mt-1 leading-normal font-medium",children:t==="zh"?"插件在主进程上下文中运行,执行性能极高。但如果代码发生不可捕获错误,可能会造成服务崩溃,适合受信任的代码。":"Runs in host process, higher execution speed. Uncaught plugin failures might cause host server to stop."})]})]})]})]})]})}),e.jsxs("div",{className:"flex items-center justify-between border-t border-slate-100 pt-4 shrink-0",children:[e.jsx("div",{className:"flex items-center gap-1.5",children:[0,1,2,3,4].map(a=>e.jsx("div",{className:`h-1.5 rounded-full transition-all duration-300 ${a===g?"w-4 bg-indigo-650":"w-1.5 bg-slate-200"}`},a))}),e.jsxs("div",{className:"flex items-center gap-2",children:[g>0?e.jsxs("button",{type:"button",onClick:()=>k(a=>a-1),className:"flex items-center gap-1.5 px-3.5 py-2 border border-slate-200 rounded-lg text-xs font-semibold text-slate-600 hover:bg-slate-50 transition-colors cursor-pointer",children:[e.jsx(Pe,{size:12}),t==="zh"?"上一步":"Back"]}):e.jsx("button",{type:"button",onClick:s,className:"px-3.5 py-2 border border-slate-200 rounded-lg text-xs font-semibold text-slate-400 hover:bg-slate-50 transition-colors cursor-pointer",children:t==="zh"?"取消":"Cancel"}),!G&&d&&x&&!T?.isSystem&&e.jsxs("button",{type:"button",onClick:Ce,className:"flex items-center gap-1.5 px-3.5 py-2 bg-gradient-to-r from-amber-500 to-amber-600 hover:from-amber-600 hover:to-amber-700 text-white rounded-lg text-xs font-bold shadow-xs transition-all cursor-pointer",title:t==="zh"?"同意全套权限与提示,直接安装完成":"Approve terms and install directly",children:[e.jsx(Ue,{size:13,className:"fill-amber-200"}),e.jsx("span",{children:t==="zh"?z?"一键极速更新":"一键极速安装":z?"Express Update":"Express Install"})]}),g===1&&C.length>0&&!N?e.jsxs("button",{type:"button",disabled:!0,className:"flex items-center gap-1.5 px-4 py-2 bg-gray-200 text-gray-400 rounded-lg text-xs font-semibold cursor-not-allowed",children:[t==="zh"?"请先授权同意":"Accept terms first",e.jsx(be,{size:12})]}):g<4?e.jsxs("button",{type:"button",onClick:()=>k(a=>a+1),className:"flex items-center gap-1.5 px-4 py-2 bg-slate-900 text-white rounded-lg text-xs font-semibold hover:bg-slate-800 transition-colors cursor-pointer",children:[t==="zh"?"下一步":"Next",e.jsx(be,{size:12})]}):e.jsx("button",{type:"button",onClick:Se,disabled:G||!xe,className:"flex items-center gap-1.5 px-5 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-xs font-bold shadow-sm transition-colors cursor-pointer disabled:bg-indigo-400",children:G?e.jsxs("div",{className:"flex flex-col gap-1.5 min-w-32",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx(J,{size:13,className:"animate-spin"}),e.jsx("span",{children:t==="zh"?z?"更新中...":"安装中...":z?"Updating...":"Installing..."})]}),e.jsx("div",{className:"w-full h-1 bg-indigo-400/30 rounded-full overflow-hidden",children:e.jsx("div",{className:"h-full bg-white/80 rounded-full transition-all duration-500",style:{width:`${Math.max(se,5)}%`}})}),te&&e.jsxs("div",{className:"text-xs text-indigo-200 truncate max-w-40",children:[se,"% — ",te]})]}):e.jsxs(e.Fragment,{children:[e.jsx(Te,{size:13}),t==="zh"?z?"确认更新并上传":"确认安装并上传":z?"Confirm & Update":"Confirm & Install"]})})]})]})]})})}function Ge({lang:r}){return e.jsxs("span",{className:"inline-flex items-center gap-1 bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold px-2 py-1 rounded uppercase tracking-wider",title:r==="zh"?"该插件使用旧执行格式运行。新格式版本可能可用。":"This plugin uses the old execution format. A new-format version may be available.",children:[e.jsx(pe,{size:10}),r==="zh"?"可迁移":"Migratable"]})}function He({plugins:r,lang:s,showSystemPlugins:t,marketMap:d,checkingUpdateId:p,oneClickUpdatingId:u,handleCheckUpdate:v,handleOneClickUpdate:g,setChangelogModalPlugin:k,setSettingsPlugin:x,setUpdateTargetPluginId:D,updateFileInputRef:C,onToggle:L,onDelete:A}){const $=ge(o=>o.dashboardVisibility);return e.jsx("div",{className:"flex-1 overflow-y-auto p-6 bg-gray-50/30",children:e.jsx("div",{className:"grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-4 gap-4",children:r.filter(o=>{const n=o.id.startsWith("@openlearn/");return t?!0:!n}).map(o=>{let n={description:"扩展 Openlearn Next 功能的自定义插件。",author:"Community",version:"",manifestId:"",capabilities:[],toolCount:0,studentViewCount:0,teacherWidgetCount:0,hasConfig:!1,repository:"",homepage:""};try{const l=JSON.parse(o.manifest);l.description&&(n.description=l.description),l.author&&(n.author=l.author),l.version&&(n.version=l.version),l.id&&(n.manifestId=l.id),l.repository&&(n.repository=typeof l.repository=="string"?l.repository:l.repository.url||""),l.homepage&&(n.homepage=l.homepage),l.capabilitiesProposed&&(n.capabilities=l.capabilitiesProposed),l.contributes?.["classroom.tool"]?n.toolCount=l.contributes["classroom.tool"].length:l.classroomTools&&(n.toolCount=l.classroomTools.length),l.contributes?.["student.view"]&&(n.studentViewCount=l.contributes["student.view"].length),l.contributes?.["teacher.dashboard.widget"]&&(n.teacherWidgetCount=l.contributes["teacher.dashboard.widget"].length);const P=l.configuration?.properties;P&&Object.keys(P).length>0&&(n.hasConfig=!0),l.updateSource?.type&&l.updateSource?.repo&&(n.updateSource=l.updateSource)}catch{}const c=o.created_at?new Date(o.created_at).toLocaleDateString(s==="zh"?"zh-CN":"en-US",{year:"numeric",month:"short",day:"numeric"}):"—",j=o.id.startsWith("@openlearn/"),N=$.get(o.id)??!0,i=d.get(n.manifestId||o.id),m=!!i?.hasUpdate,b=i?.error||null;return e.jsxs("div",{className:`bg-white rounded-xl border border-gray-200 p-5 shadow-sm hover:shadow-md transition-all relative overflow-hidden group flex flex-col gap-3 h-full ${o.status!=="active"?"opacity-75":""}`,children:[e.jsxs("div",{className:"absolute top-0 right-0 p-3 flex items-center gap-1.5 flex-wrap justify-end",children:[m&&e.jsx("span",{title:s==="zh"?`点击查看新特性${i.isPrerelease?"(预发布版本)":""}并升级至 v${i.latestVersion}`:`Upgradeable to v${i.latestVersion}${i.isPrerelease?" (pre-release)":""}`,className:`text-xs font-bold px-2.5 py-0.5 rounded-full shadow-sm flex items-center gap-1 shrink-0 cursor-pointer ${i.isPrerelease?"bg-gradient-to-r from-amber-500 to-orange-500 text-white animate-pulse":"bg-gradient-to-r from-blue-600 to-indigo-600 text-white animate-pulse"}`,onClick:()=>k({...o,marketItem:i}),children:e.jsxs("span",{children:[i.isPrerelease?"🔶 ":"⚡ ",s==="zh"?`${i.isPrerelease?"预发布 ":"发现新版本 "}v${i.latestVersion}`:`${i.isPrerelease?"Pre-release ":"New "}v${i.latestVersion}`]})}),b&&!m&&e.jsx("span",{title:b,className:"text-xs font-semibold bg-amber-50 text-amber-700 border border-amber-200 px-2.5 py-0.5 rounded-full flex items-center gap-1 shrink-0 cursor-default",children:e.jsxs("span",{children:["⚠️ ",s==="zh"?"检查失败":"Check failed"]})}),e.jsxs("span",{className:`text-xs font-semibold px-2.5 py-0.5 rounded-full flex items-center gap-1 border transition-all ${o.status==="active"?"bg-emerald-50 text-emerald-750 border-emerald-250":"bg-slate-100 text-slate-500 border-slate-200"}`,children:[e.jsx("span",{className:`w-1.5 h-1.5 rounded-full ${o.status==="active"?"bg-emerald-500 animate-pulse":"bg-slate-400"}`}),e.jsx("span",{children:o.status==="active"?s==="zh"?"已启用":"ACTIVE":s==="zh"?"已停用":"INACTIVE"})]}),o.execution_mode==="esm"&&e.jsx("span",{className:"text-xs font-bold bg-indigo-50 text-indigo-700 border border-indigo-150 px-1.5 py-0.5 rounded uppercase tracking-wider",children:"ESM"}),o.execution_mode==="legacy"&&e.jsx(Ge,{lang:s}),j&&e.jsx("span",{className:"text-xs font-bold bg-violet-50 text-violet-700 border border-violet-200 px-1.5 py-0.5 rounded uppercase tracking-wider",children:s==="zh"?"系统":"SYSTEM"})]}),e.jsxs("div",{className:"flex items-start gap-3 pr-24",children:[e.jsx("div",{className:"w-11 h-11 bg-indigo-50 text-indigo-600 rounded-lg flex items-center justify-center border border-indigo-100 group-hover:bg-indigo-600 group-hover:text-white transition-colors shrink-0",children:e.jsx(De,{size:22})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx("h4",{className:"font-semibold text-gray-900 line-clamp-1",children:o.name}),n.version&&e.jsxs("span",{className:"text-xs font-mono font-bold bg-gray-100 text-gray-500 border border-gray-200 px-1.5 py-0.5 rounded shrink-0",children:["v",n.version]})]}),n.manifestId&&e.jsx("p",{className:"text-xs text-gray-400 font-mono truncate mt-0.5",title:n.manifestId,children:n.manifestId})]})]}),e.jsx("p",{className:"text-xs text-gray-500 line-clamp-2 leading-relaxed",children:n.description}),(n.toolCount>0||n.studentViewCount>0||n.teacherWidgetCount>0)&&e.jsxs("div",{className:"flex flex-wrap gap-1.5",children:[n.toolCount>0&&e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs font-medium bg-indigo-50 text-indigo-600 border border-indigo-100 px-2 py-0.5 rounded-full",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-indigo-400"}),s==="zh"?`${n.toolCount} 个课堂工具`:`${n.toolCount} classroom tool${n.toolCount>1?"s":""}`]}),n.studentViewCount>0&&e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs font-medium bg-sky-50 text-sky-600 border border-sky-100 px-2 py-0.5 rounded-full",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-sky-400"}),s==="zh"?`${n.studentViewCount} 个学生视图`:`${n.studentViewCount} student view${n.studentViewCount>1?"s":""}`]}),n.teacherWidgetCount>0&&e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs font-medium bg-amber-50 text-amber-600 border border-amber-100 px-2 py-0.5 rounded-full",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-amber-400"}),s==="zh"?`${n.teacherWidgetCount} 个教师组件`:`${n.teacherWidgetCount} teacher widget${n.teacherWidgetCount>1?"s":""}`]}),n.capabilities.length>0&&e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs font-medium bg-rose-50 text-rose-600 border border-rose-100 px-2 py-0.5 rounded-full",children:[e.jsx(ve,{size:9}),s==="zh"?`${n.capabilities.length} 项权限`:`${n.capabilities.length} permission${n.capabilities.length>1?"s":""}`]})]}),e.jsxs("div",{className:"mt-auto flex flex-col gap-3 pt-1",children:[e.jsxs("div",{className:"flex items-center justify-between text-xs text-gray-400 border-t border-gray-100 pt-2 gap-2 flex-wrap",children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsxs("span",{className:"flex items-center gap-1 min-w-0",children:[e.jsx(Me,{size:10,className:"shrink-0 text-gray-400"}),e.jsx("span",{className:"truncate",children:n.author})]}),(n.repository||n.homepage||i?.repository)&&e.jsxs("a",{href:n.repository||n.homepage||i?.repository,target:"_blank",rel:"noopener noreferrer",title:s==="zh"?"查看 Git 开源仓库 (GitHub/Gitee)":"View Git Repository",className:"inline-flex items-center gap-1 text-xs font-mono text-indigo-600 hover:text-indigo-800 bg-indigo-50 hover:bg-indigo-100 px-1.5 py-0.5 rounded border border-indigo-200/60 transition-colors shrink-0",children:[e.jsx(we,{size:10}),e.jsx("span",{className:"truncate max-w-[130px]",children:(n.repository||n.homepage||i?.repository).replace(/^https?:\/\//,"")})]})]}),e.jsxs("span",{className:"flex items-center gap-1 shrink-0 ml-auto",children:[e.jsx("span",{children:s==="zh"?"安装于":"Installed"}),e.jsx("span",{className:"font-mono",children:c})]})]}),e.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap min-h-[34px]",children:[n.updateSource&&e.jsxs("button",{onClick:()=>v(o.id,n.manifestId||o.id),disabled:p===o.id,className:`px-2.5 py-1.5 text-xs font-medium rounded-lg border transition-colors flex items-center gap-1 cursor-pointer disabled:opacity-50 ${m?"border-blue-200 bg-blue-50 text-blue-700 hover:bg-blue-100":b?"border-amber-200 bg-amber-50 text-amber-700 hover:bg-amber-100":"border-gray-200 bg-gray-50 text-gray-600 hover:bg-gray-100"}`,title:s==="zh"?"手动检查远端更新":"Check for updates",children:[p===o.id?e.jsx(le,{size:12,className:"animate-spin"}):e.jsx(le,{size:12}),e.jsx("span",{children:s==="zh"?"检查更新":"Check"})]}),m?e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>g(o.id,i),disabled:u===o.id,className:"px-3 py-1.5 text-xs font-semibold rounded-lg bg-gradient-to-r from-blue-600 to-indigo-600 text-white hover:from-blue-700 hover:to-indigo-700 shadow-sm transition-all flex items-center gap-1.5 disabled:opacity-50 cursor-pointer",children:u===o.id?e.jsx(le,{size:12,className:"animate-spin"}):e.jsxs("span",{children:["🚀"," ",s==="zh"?`一键热更新 v${i.latestVersion}`:`Update v${i.latestVersion}`]})}),e.jsxs("button",{onClick:()=>k({...o,marketItem:i}),className:"px-2.5 py-1.5 text-xs font-medium rounded-lg bg-indigo-50 text-indigo-700 hover:bg-indigo-100 border border-indigo-200/60 transition-colors flex items-center gap-1 cursor-pointer",children:[e.jsx(Ee,{size:12}),e.jsx("span",{children:s==="zh"?"新特性":"Notes"})]})]}):null,e.jsx("button",{onClick:()=>L(o.id),className:`px-3 py-1.5 text-xs font-medium rounded-lg transition-colors ${o.status==="active"?"bg-amber-50 text-amber-600 hover:bg-amber-100":"bg-green-50 text-green-600 hover:bg-green-100"}`,children:o.status==="active"?s==="zh"?"禁用":"Disable":s==="zh"?"启用":"Enable"}),e.jsxs("button",{onClick:()=>{const l=!N;ge.getState().setDashboardVisibility(o.id,l)},title:N?s==="zh"?"在系统总览中隐藏":"Hide from Dashboard":s==="zh"?"在系统总览中显示":"Show in Dashboard",className:`px-3 py-1.5 text-xs font-medium rounded-lg transition-colors flex items-center gap-1.5 ${N?"bg-indigo-50 text-indigo-700 hover:bg-indigo-100 border border-indigo-200/60":"bg-gray-50 text-gray-500 hover:bg-gray-100 border border-gray-200/60"}`,children:[e.jsx("span",{children:s==="zh"?"总览":"Dash"}),e.jsx("span",{className:`w-7 h-3.5 rounded-full transition-colors flex items-center p-0.5 shrink-0 ${N?"bg-indigo-600":"bg-gray-300"}`,children:e.jsx("span",{className:`w-2.5 h-2.5 rounded-full bg-white shadow-sm transition-transform ${N?"translate-x-3.5":"translate-x-0"}`})})]}),n.hasConfig&&e.jsxs("button",{onClick:()=>x({id:o.id,name:o.name,manifest:o.manifest}),className:"px-3 py-1.5 text-xs font-medium rounded-lg bg-indigo-50 text-indigo-600 hover:bg-indigo-100 transition-colors flex items-center gap-1",children:[e.jsx(ue,{size:12}),s==="zh"?"设置":"Settings"]}),!j&&e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>{D(o.id),C.current?.click()},className:"px-3 py-1.5 text-xs font-medium rounded-lg bg-sky-50 text-sky-700 hover:bg-sky-100 transition-colors",children:s==="zh"?"更新":"Update"}),e.jsx("button",{onClick:()=>A(o.id),className:"px-3 py-1.5 text-xs font-medium rounded-lg bg-red-50 text-red-600 hover:bg-red-100 transition-colors",children:s==="zh"?"删除":"Delete"})]}),o.execution_mode==="legacy"&&e.jsx("button",{onClick:()=>document.getElementById("zip-plugin-uploader")?.click(),className:"px-3 py-1.5 text-xs font-medium rounded-lg bg-amber-600 text-white hover:bg-amber-700 transition-colors",title:s==="zh"?"上传新格式 ZIP 包以完成迁移":"Upload new-format ZIP package to migrate",children:s==="zh"?"迁移":"Migrate"})]})]})]},o.id)})})})}function Qe(r){if(!r||typeof r!="string")return null;try{const s=new Function("exports","require","module",r),t={},d={exports:t};return s(t,u=>u==="@openlearn/plugin-sdk"?{ICommandBusServiceToken:"@openlearn/core:ICommandBusService",IEventBusServiceToken:"@openlearn/core:IEventBusService",IActionRegistryServiceToken:"@openlearn/core:IActionRegistryService",ICapabilityServiceToken:"@openlearn/core:ICapabilityService",IProcessServiceToken:"@openlearn/core:IProcessService",IStorageServiceToken:"@openlearn/core:IStorageService",IAIServiceToken:"@openlearn/core:IAIService",IDatabaseToken:"@openlearn/core:IDatabase",IPluginHostToken:"@openlearn/core:IPluginHost"}:{},d),d.exports.default||t.default||d.exports}catch{return null}}const Ke=`exports.default = {
|
|
3
|
-
manifest: {
|
|
4
|
-
id: "ext-ai-quiz-gen",
|
|
5
|
-
name: "AI Course Quiz Generator (智能测验生成器)",
|
|
6
|
-
version: "1.0.0",
|
|
7
|
-
description: "自动根据教案主题或特定文本,一键生成结构化的单选/多选随堂测验,并直接分发至指定班级的学生学习终端。",
|
|
8
|
-
author: "Openlearn Next AI Team",
|
|
9
|
-
capabilitiesProposed: ["lesson:read", "whiteboard:write"]
|
|
10
|
-
},
|
|
11
|
-
activate: async (ctx) => {
|
|
12
|
-
ctx.actionRegistry.register({
|
|
13
|
-
id: 'ext-quiz-create',
|
|
14
|
-
commandType: 'quiz.create',
|
|
15
|
-
description: '为指定课时快速创建一份包含指定题目的随堂测验卷,可直接投射至白板或学生端',
|
|
16
|
-
capabilityRequired: 'lesson:read',
|
|
17
|
-
inputSchema: {
|
|
18
|
-
type: 'OBJECT',
|
|
19
|
-
properties: {
|
|
20
|
-
lessonId: { type: 'STRING', description: '关联的课时 ID' },
|
|
21
|
-
title: { type: 'STRING', description: '测验标题' },
|
|
22
|
-
questionsCount: { type: 'INTEGER', description: '自动生成的题目数量 (1-10)' }
|
|
23
|
-
},
|
|
24
|
-
required: ['lessonId', 'title']
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
ctx.commandBus.registerHandler('quiz.create', {
|
|
29
|
-
execute: async (command) => {
|
|
30
|
-
const payload = command.payload;
|
|
31
|
-
const count = payload.questionsCount || 3;
|
|
32
|
-
|
|
33
|
-
const mockQuestions = Array.from({ length: count }).map((_, i) => ({
|
|
34
|
-
id: 'q_' + (i + 1),
|
|
35
|
-
question: \`关于\${payload.title}的第 \${i + 1} 道随堂思考题?\`,
|
|
36
|
-
options: ["选项 A: 基础概念描述", "选项 B: 核心定理应用", "选项 C: 边界条件推导", "选项 D: 实验验证方法"],
|
|
37
|
-
answerIndex: i % 4
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
|
-
const quizData = {
|
|
41
|
-
quizId: 'qz_' + Math.random().toString(36).slice(2, 9),
|
|
42
|
-
lessonId: payload.lessonId,
|
|
43
|
-
title: payload.title,
|
|
44
|
-
questions: mockQuestions,
|
|
45
|
-
createdTime: new Date().toISOString()
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
await ctx.eventBus.publish({
|
|
49
|
-
id: 'evt_' + Math.random().toString(36).slice(2),
|
|
50
|
-
type: 'quiz.created',
|
|
51
|
-
source: 'plugin.ext-ai-quiz-gen',
|
|
52
|
-
payload: quizData,
|
|
53
|
-
timestamp: Date.now()
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
success: true,
|
|
58
|
-
quiz: quizData,
|
|
59
|
-
message: \`已为课时 \${payload.lessonId} 成功生成包含 \${count} 道题目的测验包!\`
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
};`;function Xe({lang:r,pluginCode:s,setPluginCode:t,plugins:d,installingPlugin:p,onInstall:u,onToggle:v,onDelete:g,setUpdateTargetPluginId:k,updateFileInputRef:x,setStoreTab:D,handleZipDrop:C,zipError:L,setZipError:A,zipPreview:$,setZipPreview:o,zipProcessing:n,hasLegacyPlugins:c,dismissMigration:j,MigrationPromptBanner:N}){return e.jsxs("div",{className:"flex-1 flex flex-col overflow-hidden bg-gray-950",children:[e.jsxs("div",{className:"p-4 bg-gray-900 border-b border-gray-800 flex justify-between items-center shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(me,{size:16,className:"text-amber-400"}),e.jsxs("div",{children:[e.jsx("p",{className:"text-xs font-semibold text-gray-200",children:r==="zh"?"开发者工具: 插件旁路加载与实时 Manifest 校验":"Developer Tools: Plugin Sideloading & Real-time Manifest Validation"}),e.jsx("p",{className:"text-xs text-gray-505",children:r==="zh"?"在安装前系统将进行解析、安全授权与注册接口预览机制":"Parse metadata, proposed permissions, and registered triggers before installation"})]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:`inline-flex items-center gap-1.5 px-2.5 py-1 text-xs uppercase font-bold rounded-lg cursor-pointer transition-all ${L?"border border-red-500/30 bg-red-500/10 text-red-400 hover:bg-red-500/20":$?"border border-emerald-500/30 bg-emerald-500/10 text-emerald-400 hover:bg-emerald-500/20":n?"border border-indigo-500/30 bg-indigo-500/10 text-indigo-400":"border border-dashed border-gray-600 text-gray-400 hover:text-white hover:border-indigo-400 hover:bg-indigo-500/10"}`,onDragOver:i=>{i.preventDefault(),i.currentTarget.classList.add("border-indigo-400","bg-indigo-50/50")},onDragLeave:i=>{i.currentTarget.classList.remove("border-indigo-400","bg-indigo-50/50")},onDrop:C,onClick:()=>{A(null),o(null),document.getElementById("zip-plugin-uploader")?.click()},children:n?e.jsxs(e.Fragment,{children:[e.jsx(J,{size:11,className:"animate-spin"}),e.jsx("span",{children:r==="zh"?"分析中...":"Analyzing..."})]}):$?e.jsxs(e.Fragment,{children:[e.jsx(de,{size:11}),e.jsx("span",{className:"max-w-[100px] truncate",children:$.name})]}):L?e.jsxs(e.Fragment,{children:[e.jsx(me,{size:11}),e.jsx("span",{children:r==="zh"?"重试":"Retry"})]}):e.jsxs(e.Fragment,{children:[e.jsx(ze,{size:11}),e.jsx("span",{children:r==="zh"?"拖拽安装":"Drop ZIP"})]})}),e.jsxs("button",{onClick:()=>t(Ke),className:"px-2.5 py-1 text-xs uppercase font-bold text-gray-400 hover:text-white bg-gray-800 hover:bg-gray-700 rounded-lg transition-all flex items-center gap-1 cursor-pointer",title:"Reset to default example",children:[e.jsx(Re,{size:11,className:"text-indigo-450"}),r==="zh"?"示例:智能测验生成器":"Quiz Sample"]})]})]}),c&&!j&&e.jsx("div",{className:"px-4 pt-4 bg-gray-950",children:e.jsx(N,{})}),e.jsxs("div",{className:"flex-1 flex overflow-hidden min-h-0 bg-gray-950",children:[e.jsxs("div",{className:"w-7/12 flex flex-col border-r border-gray-800 h-full p-4 min-h-0",children:[e.jsxs("div",{className:"flex justify-between items-center mb-1 text-xs uppercase font-bold text-gray-400 select-none shrink-0",children:[e.jsx("span",{children:r==="zh"?"⚙️ 插件主程序 JS 源代码":"⚙️ Plugin Source Code (JavaScript)"}),e.jsx("span",{className:"font-mono text-xs text-gray-500",children:"Node Sandbox Ready"})]}),e.jsx("textarea",{value:s,onChange:i=>t(i.target.value),className:"w-full flex-1 font-mono text-xs p-4 bg-gray-900 border border-gray-800 text-indigo-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none resize-none leading-relaxed overflow-y-auto"})]}),e.jsxs("div",{className:"w-5/12 flex flex-col bg-gray-900/40 p-4 h-full overflow-y-auto min-h-0",children:[e.jsxs("div",{className:"mb-3",children:[e.jsxs("div",{className:"text-xs uppercase font-bold text-gray-400 select-none mb-1.5 flex justify-between items-center",children:[e.jsx("span",{children:r==="zh"?"🔍 MANIFEST 实时解析与权限审计":"🔍 Manifest Extraction & Audit"}),e.jsx("span",{className:"text-xs px-1.5 py-0.5 rounded bg-indigo-950 text-indigo-400 border border-indigo-900 font-mono",children:"Live Static"})]}),(()=>{const i=Qe(s),m=i&&i.manifest&&i.manifest.id&&i.manifest.name;return e.jsxs("div",{className:"space-y-3.5",children:[e.jsx("div",{className:`p-3 rounded-lg border flex items-start gap-2 ${m?"bg-emerald-950/45 border-emerald-800/60 text-emerald-300":"bg-amber-955/40 border-amber-800/60 text-amber-300"}`,children:m?e.jsxs(e.Fragment,{children:[e.jsx(de,{size:16,className:"text-emerald-400 shrink-0 mt-0.5"}),e.jsxs("div",{children:[e.jsx("h5",{className:"text-xs font-bold font-sans",children:r==="zh"?"✓ Manifest 静态合法性验证通过":"✓ Manifest Validation Passed"}),e.jsx("p",{className:"text-xs text-emerald-400/80 mt-0.5 leading-tight",children:r==="zh"?"检测到完整的插件标识。可在安全白名单和命令总线中顺利完成挂载。":"Completed identifier extraction. Secure initialization is ready to deploy."})]})]}):e.jsxs(e.Fragment,{children:[e.jsx(me,{size:16,className:"text-amber-400 shrink-0 mt-0.5"}),e.jsxs("div",{children:[e.jsx("h5",{className:"text-xs font-bold font-sans",children:r==="zh"?"⚠️ 未匹配到有效 Manifest 描述符":"⚠️ Searching for valid Metadata"}),e.jsx("p",{className:"text-xs text-amber-400/80 mt-0.5 leading-tight",children:r==="zh"?"请在代码段中指定完整的 manifest 包含 id、name 属性,系统才能自动进行预览与权限挂载。":"Please provide manifest object inside exports.default with unique id/name properties to active automatic registration."})]})]})}),m&&i&&i.manifest&&e.jsxs("div",{className:"bg-gray-900 border border-gray-800 rounded-xl p-3.5 space-y-2.5",children:[e.jsxs("div",{className:"border-b border-gray-800 pb-2 flex justify-between items-center",children:[e.jsx("h6",{className:"text-xs font-bold text-gray-300 uppercase tracking-wider",children:r==="zh"?"基本描述元数据":"Metadata Details"}),e.jsxs("span",{className:"text-xs text-indigo-400 font-mono px-1 bg-indigo-950 rounded",children:["v",i.manifest.version||"1.0.0"]})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-2 text-xs font-mono",children:[e.jsx("div",{className:"text-gray-500",children:r==="zh"?"名称:":"Name:"}),e.jsx("div",{className:"col-span-2 text-gray-200 font-sans font-semibold",children:i.manifest.name}),e.jsx("div",{className:"text-gray-500",children:r==="zh"?"唯一标识:":"UUID/ID:"}),e.jsx("div",{className:"col-span-2 text-gray-305",children:i.manifest.id}),e.jsx("div",{className:"text-gray-500",children:r==="zh"?"开发者:":"Author:"}),e.jsx("div",{className:"col-span-2 text-indigo-305",children:i.manifest.author||"Community"})]})]})]})})()]}),e.jsxs("div",{className:"mt-5 bg-gray-900 border border-gray-800 rounded-xl p-3.5 space-y-3 shrink-0",children:[e.jsxs("h6",{className:"text-xs font-bold text-indigo-300 uppercase tracking-wider pb-1.5 border-b border-gray-800 flex items-center justify-between",children:[e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(ke,{size:11,className:"text-indigo-400 animate-pulse"}),e.jsx("span",{children:r==="zh"?`已加载插件管理 (${d.length})`:`Active Plugins (${d.length})`})]}),e.jsx("button",{onClick:()=>D("store"),className:"text-xs text-gray-500 hover:text-indigo-400 transition-colors uppercase tracking-wider font-semibold",children:r==="zh"?"管理大图 ➔":"View Grid ➔"})]}),e.jsx("div",{className:"space-y-2 max-h-48 overflow-y-auto pr-1",children:d.length===0?e.jsx("div",{className:"text-xs text-gray-550 italic py-2 text-center",children:r==="zh"?"暂无安装的插件":"No plugins sideloaded."}):d.map(i=>e.jsxs("div",{className:"p-2.5 bg-gray-950 border border-gray-900 rounded-lg flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0",children:[e.jsx("span",{className:"text-xs font-bold text-gray-200 truncate",children:i.name}),e.jsx("span",{className:`w-1.5 h-1.5 rounded-full shrink-0 ${i.status==="active"?"bg-emerald-500 animate-pulse":"bg-slate-500"}`})]}),e.jsx("span",{className:"text-xs text-gray-500 font-mono block truncate select-all",children:i.id})]}),e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[e.jsx("button",{onClick:()=>v(i.id),className:`px-2.5 py-1 text-xs font-bold rounded transition-colors ${i.status==="active"?"bg-amber-950/60 border border-amber-900/50 text-amber-400 hover:bg-amber-900/80":"bg-emerald-950/60 border border-emerald-900/50 text-emerald-400 hover:bg-emerald-900/80"}`,children:i.status==="active"?r==="zh"?"禁用":"Disable":r==="zh"?"启用":"Enable"}),!i.id.startsWith("@openlearn/")&&e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>{k(i.id),x.current?.click()},className:"px-2 py-1 text-xs font-bold bg-sky-950/60 border border-sky-900/50 text-sky-300 hover:bg-sky-900/80 rounded transition-colors",children:r==="zh"?"更新":"Update"}),e.jsx("button",{onClick:()=>g(i.id),className:"px-2 py-1 text-xs font-bold bg-red-950/60 border border-red-900/50 text-red-400 hover:bg-red-900/80 rounded transition-colors",children:r==="zh"?"删除":"Delete"})]})]})]},i.id))})]})]})]}),e.jsxs("div",{className:"p-4 border-t border-gray-800 bg-gray-950 flex justify-between items-center shrink-0 select-none",children:[e.jsx("span",{className:"text-xs text-gray-500 font-mono",children:"Secure Sideload Mode • Sandbox Integrity Check"}),e.jsx("div",{className:"flex justify-end gap-3",children:e.jsx("button",{onClick:u,disabled:p||!s.trim(),className:"px-4 py-2 text-xs bg-indigo-600 font-bold hover:bg-indigo-700 text-white rounded-lg transition-all flex items-center gap-2 disabled:opacity-50 hover:shadow-lg active:scale-97 cursor-pointer",children:p?e.jsxs(e.Fragment,{children:[e.jsx(J,{size:13,className:"animate-spin"}),e.jsx("span",{children:r==="zh"?"集成挂载中...":"Registering..."})]}):e.jsxs(e.Fragment,{children:[e.jsx(de,{size:13}),e.jsx("span",{children:r==="zh"?"部署并安装到课堂内核":"Deploy & Install Plugin"})]})})})]})]})}function Ye({lang:r}){const[s,t]=Z.useState([]),[d,p]=Z.useState(!1),[u,v]=Z.useState(!0),[g,k]=Z.useState(""),[x,D]=Z.useState(""),[C,L]=Z.useState(""),A=async()=>{p(!0);try{let c="/api/admin/logs?limit=300";x&&(c+=`&level=${x}`),C&&(c+=`&component=${C}`);const N=await(await fetch(c)).json();N.success&&t(N.logs||[])}catch(c){console.error("Error fetching logs:",c)}finally{p(!1)}};Z.useEffect(()=>{A()},[x,C]),Z.useEffect(()=>{if(!u)return;const c=setInterval(()=>{let j="/api/admin/logs?limit=300";x&&(j+=`&level=${x}`),C&&(j+=`&component=${C}`),fetch(j).then(N=>N.json()).then(N=>{N.success&&t(N.logs||[])}).catch(N=>console.error(N))},3e3);return()=>clearInterval(c)},[u,x,C]);const $=Z.useMemo(()=>{const c=new Set;return s.forEach(j=>{j.component&&c.add(j.component)}),Array.from(c)},[s]),o=Z.useMemo(()=>{if(!g)return s;const c=g.toLowerCase();return s.filter(j=>{const N=(j.msg||"").toLowerCase(),i=(j.component||"").toLowerCase();return N.includes(c)||i.includes(c)})},[s,g]),n=c=>{switch(c){case"error":return"text-rose-500 font-semibold";case"warn":return"text-amber-500 font-semibold";case"debug":return"text-blue-400";default:return"text-emerald-400"}};return e.jsxs("div",{className:"flex-1 flex flex-col p-6 bg-slate-900 text-slate-100 font-mono text-sm overflow-hidden h-full min-h-[500px]",children:[e.jsxs("div",{className:"flex flex-wrap gap-4 items-center justify-between border-b border-slate-800 pb-4 shrink-0 font-sans",children:[e.jsxs("div",{className:"flex flex-wrap gap-3 items-center",children:[e.jsx("input",{type:"text",placeholder:r==="zh"?"搜索日志内容...":"Search logs...",value:g,onChange:c=>k(c.target.value),className:"bg-slate-800 border border-slate-700 text-slate-200 px-3 py-1.5 rounded-lg focus:outline-none focus:border-indigo-500 placeholder-slate-500 text-xs w-48"}),e.jsxs("select",{value:x,onChange:c=>D(c.target.value),className:"bg-slate-800 border border-slate-700 text-slate-200 px-3 py-1.5 rounded-lg focus:outline-none focus:border-indigo-500 text-xs",children:[e.jsx("option",{value:"",children:r==="zh"?"所有级别":"All Levels"}),e.jsx("option",{value:"info",children:"INFO"}),e.jsx("option",{value:"warn",children:"WARN"}),e.jsx("option",{value:"error",children:"ERROR"}),e.jsx("option",{value:"debug",children:"DEBUG"})]}),e.jsxs("select",{value:C,onChange:c=>L(c.target.value),className:"bg-slate-800 border border-slate-700 text-slate-200 px-3 py-1.5 rounded-lg focus:outline-none focus:border-indigo-500 text-xs max-w-48",children:[e.jsx("option",{value:"",children:r==="zh"?"所有组件":"All Components"}),$.map(c=>e.jsx("option",{value:c,children:c},c))]})]}),e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsxs("label",{className:"flex items-center gap-2 text-xs text-slate-400 select-none cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:u,onChange:c=>v(c.target.checked),className:"rounded bg-slate-800 border-slate-700 text-indigo-500 focus:ring-0 focus:ring-offset-0"}),r==="zh"?"自动刷新 (3s)":"Auto-refresh (3s)"]}),e.jsxs("button",{onClick:A,disabled:d,className:"bg-slate-800 hover:bg-slate-700 active:bg-slate-750 text-slate-200 border border-slate-700 px-3 py-1.5 rounded-lg text-xs flex items-center gap-1.5 transition-colors disabled:opacity-50 cursor-pointer",children:[d?e.jsx(J,{size:12,className:"animate-spin"}):null,r==="zh"?"刷新":"Refresh"]})]})]}),e.jsx("div",{className:"flex-1 overflow-y-auto mt-4 p-4 bg-slate-950 border border-slate-800 rounded-xl flex flex-col gap-1.5 min-h-0 select-text",children:o.length===0?e.jsx("div",{className:"text-center py-12 text-slate-500 text-xs",children:r==="zh"?"暂无匹配的运行日志数据":"No logs found."}):o.map((c,j)=>e.jsxs("div",{className:"flex gap-3 text-xs leading-relaxed hover:bg-slate-900/60 p-1 rounded transition-colors",children:[e.jsx("span",{className:"text-slate-500 shrink-0 select-none",children:new Date(c.timestamp||Date.now()).toLocaleTimeString()}),e.jsx("span",{className:`uppercase font-bold w-12 shrink-0 ${n(c.level)}`,children:c.level||"info"}),e.jsxs("span",{className:"text-indigo-400 shrink-0 font-semibold w-24 truncate",title:c.component,children:["[",c.component||"system","]"]}),e.jsxs("span",{className:"text-slate-200 flex-1 break-all whitespace-pre-wrap",children:[c.msg,c.meta&&Object.keys(c.meta).length>0&&e.jsx("span",{className:"text-slate-500 ml-2 font-mono text-xs",children:JSON.stringify(c.meta)})]})]},j))})]})}function et({plugins:r,lang:s,storeTab:t,setStoreTab:d,pluginCode:p,setPluginCode:u,installingPlugin:v,onInstall:g,onZipUpload:k,onToggle:x,onDelete:D}){const[C,L]=h.useState(!1),[A,$]=h.useState(!1),[o,n]=h.useState(null),[c,j]=h.useState(null),N=h.useRef(null),[i,m]=h.useState(null),[b,l]=h.useState(null),[P,R]=h.useState(!1),[U,ee]=h.useState(null),[ie,G]=h.useState(new Map),[Q,te]=h.useState(null),[_,se]=h.useState(null),[M,q]=h.useState(null),[K,E]=h.useState(null);h.useEffect(()=>{let f=!0;return fetch("/api/plugins/market").then(w=>w.json()).then(w=>{if(w?.success&&Array.isArray(w.market)&&f){const F=new Map;for(const O of w.market)F.set(O.manifestId,O);G(F)}}).catch(()=>{}),()=>{f=!1}},[]);const re=async(f,w)=>{se(f);try{const F=await fetch(`/api/plugins/${encodeURIComponent(f)}/check-update`,{method:"POST"}).then(O=>O.json());F?.success&&G(O=>{const B=new Map(O);return B.set(w,{...F,manifestId:w}),B})}catch{}finally{se(null)}},oe=async(f,w)=>{te(f),E(s==="zh"?"正在连接市场执行一键无缝热更新...":"Connecting to market for one-click hot update...");try{const F=w?.downloadUrl?JSON.stringify({downloadUrl:w.downloadUrl}):void 0,O=await fetch(`/api/plugins/${encodeURIComponent(f)}/one-click-update`,{method:"POST",headers:F?{"Content-Type":"application/json"}:void 0,body:F}).then(B=>B.json());if(O?.success)E(s==="zh"?`🎉 插件已成功热更新至 v${O.newVersion||"1.2.0"}!`:`🎉 Hot updated to v${O.newVersion||"1.2.0"}!`),setTimeout(()=>window.location.reload(),1200);else if(O?.fallbackToClient&&w?.downloadUrl){E(s==="zh"?"服务端下载超时,切换至浏览器直传...":"Server download timed out, switching to browser transfer...");try{const Y=await(await fetch(w.downloadUrl)).blob(),z=new FormData;z.append("file",Y,"update.zip");const W=await fetch(`/api/plugins/${encodeURIComponent(f)}/update-zip-raw`,{method:"POST",headers:{"x-install-mode":"update"},body:z}).then(ne=>ne.json());W?.success?(E(s==="zh"?"🎉 插件已成功热更新!":"🎉 Hot updated!"),setTimeout(()=>window.location.reload(),1200)):(E(`❌ 更新失败: ${W?.error||"客户端上传失败"}`),setTimeout(()=>E(null),4e3))}catch(B){E(`❌ 客户端下载失败: ${B.message}`),setTimeout(()=>E(null),4e3)}}else E(`❌ 更新失败: ${O?.error||"受热更新包限制"}`),setTimeout(()=>E(null),4e3)}catch(F){E(`❌ 一键热更新失败: ${F.message}`),setTimeout(()=>E(null),4e3)}finally{te(null)}},ae=f=>{f.preventDefault(),f.currentTarget.classList.remove("border-indigo-400","bg-indigo-50/50");const w=f.dataTransfer.files;w.length>0&&(j(null),n(w[0]))},T=f=>{const w=f.target.files?.[0];w&&n(w)},H=r.some(f=>f.execution_mode==="legacy");function X(){return e.jsxs("div",{className:"bg-amber-50 border border-amber-200 rounded-lg p-4 flex items-start justify-between mb-4",children:[e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx(pe,{size:20,className:"text-amber-500 shrink-0 mt-0.5"}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-bold text-amber-800",children:s==="zh"?"发现可迁移的旧格式插件":"Legacy Plugin Detected"}),e.jsx("p",{className:"text-xs text-amber-700 mt-1",children:s==="zh"?"该插件使用旧格式运行。上传新格式 ZIP 包以完成迁移,迁移后旧版本可安全卸载。":"This plugin runs in legacy mode. Upload a new-format ZIP package to migrate. The old version can be safely uninstalled afterwards."})]})]}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[e.jsx("button",{onClick:()=>document.getElementById("zip-plugin-uploader")?.click(),className:"bg-amber-600 text-white hover:bg-amber-700 rounded-lg text-sm font-medium px-4 py-2 transition-colors cursor-pointer",children:s==="zh"?"迁移到新格式":"Migrate to New Format"}),e.jsx("button",{onClick:()=>$(!0),className:"text-amber-500 hover:text-amber-700 p-1 cursor-pointer",children:e.jsx(ce,{size:16})})]})]})}return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"flex-1 flex flex-col h-full overflow-y-auto",children:e.jsxs("div",{className:"bg-white border text-gray-900 border-gray-200 rounded-2xl shadow flex flex-col overflow-hidden h-full",children:[e.jsxs("div",{className:"px-6 py-4 flex items-center justify-between border-b border-gray-100 bg-gray-50/50 shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-6",children:[e.jsxs("h2",{className:"font-bold text-gray-900 flex items-center gap-2 text-lg",children:[e.jsx(ke,{size:20,className:"text-indigo-600"}),s==="zh"?"Openlearn Next 插件中心":"Openlearn Next App Store"]}),e.jsxs("div",{className:"flex bg-gray-200/50 p-1 rounded-lg",children:[e.jsx("button",{onClick:()=>d("store"),className:`px-4 py-1.5 text-sm font-medium rounded-md transition-colors cursor-pointer ${t==="store"?"bg-white shadow text-indigo-600 font-bold":"text-gray-600 hover:text-gray-900"}`,children:s==="zh"?"发现":"Discover"}),e.jsxs("button",{onClick:()=>d("dev"),className:`px-4 py-1.5 text-sm font-medium rounded-md transition-colors flex items-center gap-1 cursor-pointer ${t==="dev"?"bg-white shadow text-indigo-600 font-bold":"text-gray-600 hover:text-gray-900"}`,children:[e.jsx(Fe,{size:14})," ",s==="zh"?"开发者":"Developer"]}),e.jsxs("button",{onClick:()=>d("logs"),className:`px-4 py-1.5 text-sm font-medium rounded-md transition-colors flex items-center gap-1 cursor-pointer ${t==="logs"?"bg-white shadow text-indigo-600 font-bold":"text-gray-600 hover:text-gray-900"}`,children:[e.jsx(Ae,{size:14})," ",s==="zh"?"系统日志":"Logs"]})]})]}),t==="store"&&e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("label",{className:"flex items-center gap-2 cursor-pointer text-xs font-semibold text-gray-500 hover:text-gray-900 select-none transition-colors border border-gray-200 rounded-lg px-3 py-1.5 bg-gray-50/50 shadow-sm",children:[e.jsx("input",{type:"checkbox",checked:C,onChange:f=>L(f.target.checked),className:"w-3.5 h-3.5 rounded text-indigo-600 border-gray-300 focus:ring-indigo-500 cursor-pointer"}),e.jsx("span",{children:s==="zh"?"显示系统核心插件":"Show System Core Plugins"})]}),e.jsx("div",{className:`flex items-center gap-1.5 cursor-pointer text-xs font-semibold select-none transition-colors border rounded-lg px-3 py-1.5 shadow-sm ${o?"border-emerald-300 bg-emerald-50 text-emerald-700":"border-teal-200 bg-teal-50/50 text-teal-600 hover:border-teal-400 hover:bg-teal-50"}`,onDragOver:f=>{f.preventDefault(),f.currentTarget.classList.add("border-teal-400","bg-teal-50")},onDragLeave:f=>{f.currentTarget.classList.remove("border-teal-400","bg-teal-50")},onDrop:ae,onClick:()=>{document.getElementById("zip-plugin-uploader")?.click()},children:o?e.jsxs(e.Fragment,{children:[e.jsx(de,{size:14,className:"text-emerald-500"}),e.jsx("span",{className:"max-w-[120px] truncate",children:o.name})]}):e.jsxs(e.Fragment,{children:[e.jsx(ze,{size:14}),e.jsx("span",{children:s==="zh"?"拖拽安装 ZIP":"Drop ZIP"})]})})]})]}),e.jsx("input",{type:"file",accept:".zip",id:"zip-plugin-uploader",className:"hidden",onChange:f=>{j(null),T(f)}}),e.jsx("input",{ref:N,type:"file",accept:".zip",className:"hidden",onChange:f=>{const w=f.target.files?.[0];w&&n(w),f.target.value=""}}),t==="store"?e.jsx(He,{plugins:r,lang:s,showSystemPlugins:C,marketMap:ie,checkingUpdateId:_,oneClickUpdatingId:Q,handleCheckUpdate:re,handleOneClickUpdate:oe,setChangelogModalPlugin:q,setSettingsPlugin:ee,setUpdateTargetPluginId:j,updateFileInputRef:N,onToggle:x,onDelete:D}):t==="logs"?e.jsx(Ye,{lang:s}):e.jsx(Xe,{lang:s,pluginCode:p,setPluginCode:u,plugins:r,installingPlugin:v,onInstall:g,onToggle:x,onDelete:D,setUpdateTargetPluginId:j,updateFileInputRef:N,setStoreTab:d,handleZipDrop:ae,zipError:i,setZipError:m,zipPreview:b,setZipPreview:l,zipProcessing:P,hasLegacyPlugins:H,dismissMigration:A,MigrationPromptBanner:X})]})}),U&&e.jsx(Ve,{pluginId:U.id,pluginName:U.name,manifestStr:U.manifest,lang:s,onClose:()=>ee(null)}),e.jsx(Je,{isOpen:!!o,onClose:()=>{n(null),j(null)},lang:s,file:o,lockedTargetPluginId:c,installedPlugins:r,onConfirmInstall:k}),M&&e.jsx("div",{className:"fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white rounded-2xl max-w-lg w-full p-6 shadow-2xl border border-gray-100 flex flex-col gap-4",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-gray-100 pb-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(Ne,{className:"text-indigo-600",size:20}),e.jsxs("h3",{className:"text-base font-bold text-gray-900",children:[M.name," ",s==="zh"?"版本更新说明":"Release Notes"]})]}),e.jsx("button",{onClick:()=>q(null),className:"text-gray-400 hover:text-gray-600 p-1 rounded-lg hover:bg-gray-100 transition-colors",children:e.jsx(ce,{size:18})})]}),e.jsxs("div",{className:"flex items-center justify-between bg-indigo-50/70 border border-indigo-100 rounded-xl p-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("span",{className:"text-xs font-mono font-bold bg-white text-gray-600 px-2 py-0.5 rounded border border-gray-200",children:["v",M.version||"1.1.0"]}),e.jsx("span",{className:"text-indigo-400 font-bold",children:"➔"}),e.jsxs("span",{className:"text-xs font-mono font-bold bg-indigo-600 text-white px-2 py-0.5 rounded shadow-sm",children:["v",M.marketItem?.latestVersion||"1.2.0"]})]}),M.marketItem?.repository&&e.jsxs("a",{href:M.marketItem.repository,target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-1 text-xs font-mono text-indigo-600 hover:text-indigo-800 font-medium",children:[e.jsx(we,{size:12}),e.jsx("span",{children:"Git Repo"}),e.jsx(Le,{size:10})]})]}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx("h4",{className:"text-xs font-bold text-gray-700 uppercase tracking-wider",children:s==="zh"?"新特性与优化变更清单 (Changelog)":"What's New"}),e.jsx("div",{className:"bg-slate-50 border border-slate-200/80 rounded-xl p-3.5 text-xs text-slate-700 leading-relaxed font-mono whitespace-pre-wrap max-h-48 overflow-y-auto",children:M.marketItem?.changelog||(s==="zh"?`1. 阶段式任务逻辑与自动化测试增强
|
|
65
|
-
2. 前端轻量化与高可用平滑升级`:"1. General enhancements and bug fixes")})]}),e.jsxs("div",{className:"flex items-center justify-end gap-3 pt-2 border-t border-gray-100",children:[e.jsx("button",{onClick:()=>q(null),className:"px-4 py-2 text-xs font-medium rounded-lg text-gray-600 hover:bg-gray-100 transition-colors cursor-pointer",children:s==="zh"?"稍后再说":"Later"}),M.marketItem?.downloadUrl?e.jsx("button",{onClick:()=>{const f=M.id,w=M.marketItem;q(null),oe(f,w)},className:"px-4 py-2 text-xs font-bold rounded-lg bg-gradient-to-r from-blue-600 to-indigo-600 text-white hover:from-blue-700 hover:to-indigo-700 shadow-md transition-all flex items-center gap-1.5 cursor-pointer",children:e.jsxs("span",{children:["🚀 ",s==="zh"?"立即一键热更新":"Update Now"]})}):e.jsx("span",{className:"px-3 py-2 text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded-lg",children:s==="zh"?"本地源更新:请重新构建 ZIP 后在「开发」页上传更新":"Local source update: rebuild the ZIP and upload it in the Dev tab"})]})]})}),K&&e.jsxs("div",{className:"fixed bottom-6 right-6 z-50 bg-gray-900/90 backdrop-blur text-white text-xs font-medium px-4 py-3 rounded-xl shadow-2xl border border-gray-700/80 flex items-center gap-2",children:[e.jsx(le,{size:14,className:"text-indigo-400 animate-spin"}),e.jsx("span",{children:K})]})]})}function dt(r){return e.jsx(et,{plugins:r.plugins,lang:r.lang,storeTab:r.storeTab,setStoreTab:r.setStoreTab,pluginCode:r.pluginCode,setPluginCode:r.setPluginCode,installingPlugin:r.installingPlugin,onInstall:()=>{r.onInstall(r.pluginCode)},onZipUpload:r.onZipUpload,onToggle:r.onToggle,onDelete:r.onDelete})}export{dt as PluginView};
|