cortico 0.1.0
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/LICENSE +21 -0
- package/README.md +47 -0
- package/package.json +43 -0
- package/src/boot.ts +46 -0
- package/src/bot.ts +1342 -0
- package/src/config-file.ts +41 -0
- package/src/core/README.md +92 -0
- package/src/core/billing.ts +26 -0
- package/src/core/blobs.ts +106 -0
- package/src/core/bus.ts +311 -0
- package/src/core/config-schema.ts +201 -0
- package/src/core/config.ts +262 -0
- package/src/core/core.ts +699 -0
- package/src/core/cost.ts +244 -0
- package/src/core/event-store.ts +304 -0
- package/src/core/fork.ts +113 -0
- package/src/core/generation.ts +118 -0
- package/src/core/instance-lock.ts +215 -0
- package/src/core/ipc-logger.ts +105 -0
- package/src/core/language.ts +62 -0
- package/src/core/log-context.ts +38 -0
- package/src/core/loop.ts +1858 -0
- package/src/core/markers.ts +48 -0
- package/src/core/prefix.ts +121 -0
- package/src/core/run.ts +95 -0
- package/src/core/secrets.ts +18 -0
- package/src/core/session.ts +56 -0
- package/src/core/sessions.ts +261 -0
- package/src/core/state.ts +104 -0
- package/src/core/template.ts +72 -0
- package/src/core/timers.ts +129 -0
- package/src/core/tool-log.ts +135 -0
- package/src/core/transcript.ts +92 -0
- package/src/core/truncate.ts +121 -0
- package/src/core/types.ts +1140 -0
- package/src/core/usage-log.ts +79 -0
- package/src/core/util.ts +386 -0
- package/src/deploy-listing.ts +160 -0
- package/src/deploy.ts +162 -0
- package/src/extensions/README.md +72 -0
- package/src/extensions/dry-mount.ts +390 -0
- package/src/extensions/manifest.ts +153 -0
- package/src/extensions/runtime.ts +42 -0
- package/src/extensions.ts +519 -0
- package/src/launcher.ts +285 -0
- package/src/paths.ts +155 -0
- package/src/protocol/open-responses/LICENSE +201 -0
- package/src/protocol/open-responses/README.md +14 -0
- package/src/protocol/open-responses/context-helpers.ts +27 -0
- package/src/protocol/open-responses/context-log.ts +59 -0
- package/src/protocol/open-responses/context.ts +89 -0
- package/src/protocol/open-responses/generated.ts +115 -0
- package/src/protocol/open-responses/index.ts +33 -0
- package/src/protocol/open-responses/openapi.json +4230 -0
- package/src/protocol/open-responses/stream.ts +146 -0
- package/src/protocol/open-responses/tokens.ts +31 -0
- package/src/providers/README.md +86 -0
- package/src/providers/base.ts +111 -0
- package/src/providers/configuration.ts +97 -0
- package/src/providers/console/config.ts +21 -0
- package/src/providers/console/settings.ts +443 -0
- package/src/providers/console/strings.ts +74 -0
- package/src/providers/console/types.ts +11 -0
- package/src/providers/llamacpp/archive.ts +148 -0
- package/src/providers/llamacpp/catalog.ts +247 -0
- package/src/providers/llamacpp/config.ts +36 -0
- package/src/providers/llamacpp/console/client.ts +6 -0
- package/src/providers/llamacpp/console/models-panel.ts +120 -0
- package/src/providers/llamacpp/console/runtime-panel.ts +173 -0
- package/src/providers/llamacpp/console/server.ts +153 -0
- package/src/providers/llamacpp/index.ts +95 -0
- package/src/providers/llamacpp/native.ts +51 -0
- package/src/providers/llamacpp/options.ts +117 -0
- package/src/providers/llamacpp/runtime-store.ts +139 -0
- package/src/providers/llamacpp/runtime.ts +214 -0
- package/src/providers/llamacpp/server.ts +286 -0
- package/src/providers/llamacpp/strings.ts +230 -0
- package/src/providers/openai-responses-compat/config.ts +20 -0
- package/src/providers/openai-responses-compat/index.ts +89 -0
- package/src/providers/openai-responses-compat/native.ts +127 -0
- package/src/providers/openai-responses-compat/strings.ts +19 -0
- package/src/providers/pricebook.ts +75 -0
- package/src/providers/registry.ts +137 -0
- package/src/providers/strings.ts +71 -0
- package/src/providers/transport/chat.ts +156 -0
- package/src/providers/transport/errors.ts +48 -0
- package/src/providers/transport/history.ts +68 -0
- package/src/providers/transport/native-input.ts +83 -0
- package/src/providers/transport/native-types.ts +13 -0
- package/src/providers/transport/response-assembly.ts +191 -0
- package/src/providers/transport/response-http.ts +196 -0
- package/src/providers/transport/response-meters.ts +27 -0
- package/src/providers/transport/responses-input.ts +56 -0
- package/src/web/README.md +84 -0
- package/src/web/client/console-pages/builtins/llm-settings/panel.ts +625 -0
- package/src/web/client/console-pages/builtins/llm-settings/pricing-panel.ts +168 -0
- package/src/web/client/console-pages/builtins/llm-settings/strings.ts +213 -0
- package/src/web/client/console-pages/builtins.ts +10 -0
- package/src/web/client/console-pages/context.ts +135 -0
- package/src/web/client/console-pages/host.ts +633 -0
- package/src/web/client/console-pages/loader.ts +132 -0
- package/src/web/client/console-pages/strings.ts +90 -0
- package/src/web/client/console-pages/tools/strings.ts +49 -0
- package/src/web/client/console-pages/tools/view.ts +215 -0
- package/src/web/client/core/api.ts +200 -0
- package/src/web/client/core/language.ts +56 -0
- package/src/web/client/core/lifecycle.ts +132 -0
- package/src/web/client/core/router.ts +199 -0
- package/src/web/client/core/stream.ts +157 -0
- package/src/web/client/core/websocket.ts +82 -0
- package/src/web/client/features/appearance/index.ts +333 -0
- package/src/web/client/features/appearance/strings.ts +87 -0
- package/src/web/client/features/config/index.ts +11 -0
- package/src/web/client/features/config/strings.ts +41 -0
- package/src/web/client/features/config/view.ts +428 -0
- package/src/web/client/features/core/events.ts +197 -0
- package/src/web/client/features/core/index.ts +254 -0
- package/src/web/client/features/core/run.ts +56 -0
- package/src/web/client/features/core/runlog.ts +214 -0
- package/src/web/client/features/core/sessions.ts +53 -0
- package/src/web/client/features/core/strings.ts +157 -0
- package/src/web/client/features/extensions/index.ts +422 -0
- package/src/web/client/features/extensions/strings.ts +164 -0
- package/src/web/client/features/feature.ts +93 -0
- package/src/web/client/features/live/context.ts +280 -0
- package/src/web/client/features/live/fork.ts +156 -0
- package/src/web/client/features/live/index.ts +392 -0
- package/src/web/client/features/live/onboarding.ts +113 -0
- package/src/web/client/features/live/protocol.ts +122 -0
- package/src/web/client/features/live/sessions.ts +71 -0
- package/src/web/client/features/live/status.ts +68 -0
- package/src/web/client/features/live/strings.ts +252 -0
- package/src/web/client/features/live/timeline.ts +590 -0
- package/src/web/client/features/prompts/editor.ts +292 -0
- package/src/web/client/features/prompts/index.ts +346 -0
- package/src/web/client/features/prompts/strings.ts +136 -0
- package/src/web/client/features/prompts/view.ts +256 -0
- package/src/web/client/features/providers/index.ts +100 -0
- package/src/web/client/features/providers/strings.ts +23 -0
- package/src/web/client/features/settings/general.ts +28 -0
- package/src/web/client/features/settings/index.ts +82 -0
- package/src/web/client/features/settings/strings.ts +33 -0
- package/src/web/client/features/storage/index.ts +1 -0
- package/src/web/client/features/storage/strings.ts +45 -0
- package/src/web/client/features/storage/view.ts +154 -0
- package/src/web/client/features/usage/chart.ts +486 -0
- package/src/web/client/features/usage/index.ts +407 -0
- package/src/web/client/features/usage/labels.ts +46 -0
- package/src/web/client/features/usage/range.ts +36 -0
- package/src/web/client/features/usage/state.ts +37 -0
- package/src/web/client/features/usage/strings.ts +191 -0
- package/src/web/client/features/usage/tooltip.ts +86 -0
- package/src/web/client/features/usage/types.ts +96 -0
- package/src/web/client/features/worlds/index.ts +424 -0
- package/src/web/client/features/worlds/strings.ts +121 -0
- package/src/web/client/main.ts +273 -0
- package/src/web/client/shell/avatar.ts +233 -0
- package/src/web/client/shell/index.ts +549 -0
- package/src/web/client/shell/strings.ts +89 -0
- package/src/web/client/theme/handoff.ts +43 -0
- package/src/web/client/theme/palette.ts +109 -0
- package/src/web/client/theme/registry.ts +130 -0
- package/src/web/client/theme/storage.ts +72 -0
- package/src/web/client/theme/strings.ts +123 -0
- package/src/web/client/theme/studio.ts +366 -0
- package/src/web/client/ui/actions.ts +109 -0
- package/src/web/client/ui/data.ts +185 -0
- package/src/web/client/ui/dom.ts +23 -0
- package/src/web/client/ui/fields.ts +191 -0
- package/src/web/client/ui/format.ts +75 -0
- package/src/web/client/ui/icons.ts +183 -0
- package/src/web/client/ui/images.ts +91 -0
- package/src/web/client/ui/index.ts +89 -0
- package/src/web/client/ui/lamp.ts +125 -0
- package/src/web/client/ui/log.ts +98 -0
- package/src/web/client/ui/overlay.ts +184 -0
- package/src/web/client/ui/page.ts +8 -0
- package/src/web/client/ui/prompt-input.tsx +311 -0
- package/src/web/client/ui/sheet.ts +131 -0
- package/src/web/client/ui/strings.ts +71 -0
- package/src/web/console-pages.ts +367 -0
- package/src/web/files.ts +107 -0
- package/src/web/path-picker.ts +296 -0
- package/src/web/public/index.html +17 -0
- package/src/web/public/styles.css +1715 -0
- package/src/web/server.ts +1883 -0
- package/src/web/shared/client-panel.ts +603 -0
- package/src/web/shared/console-protocol.ts +560 -0
- package/src/web/shared/css.d.ts +1 -0
- package/src/web/shared/path-picker.ts +23 -0
- package/src/web/shared/theme.ts +256 -0
- package/src/web/theme-store.ts +37 -0
- package/src/world.ts +449 -0
- package/src/worlds/bilibili/ENV_PROMPT.md +5 -0
- package/src/worlds/bilibili/README.md +176 -0
- package/src/worlds/bilibili/audience-admission.ts +995 -0
- package/src/worlds/bilibili/client.ts +528 -0
- package/src/worlds/bilibili/coalescing-buffer.ts +125 -0
- package/src/worlds/bilibili/config.ts +185 -0
- package/src/worlds/bilibili/console/client.ts +202 -0
- package/src/worlds/bilibili/definition.ts +35 -0
- package/src/worlds/bilibili/gift-frame.ts +131 -0
- package/src/worlds/bilibili/normalize.ts +452 -0
- package/src/worlds/bilibili/overlay/announcement.ts +76 -0
- package/src/worlds/bilibili/overlay/assets.ts +82 -0
- package/src/worlds/bilibili/overlay/model.ts +494 -0
- package/src/worlds/bilibili/overlay/project.ts +202 -0
- package/src/worlds/bilibili/overlay/server.ts +286 -0
- package/src/worlds/bilibili/overlay/types.ts +217 -0
- package/src/worlds/bilibili/overlay/web/app.js +690 -0
- package/src/worlds/bilibili/overlay/web/editor.css +297 -0
- package/src/worlds/bilibili/overlay/web/editor.html +102 -0
- package/src/worlds/bilibili/overlay/web/editor.js +2116 -0
- package/src/worlds/bilibili/overlay/web/overlay.html +13 -0
- package/src/worlds/bilibili/overlay/web/styles.css +258 -0
- package/src/worlds/bilibili/protobuf.ts +123 -0
- package/src/worlds/bilibili/wire.ts +94 -0
- package/src/worlds/bilibili/world.ts +1389 -0
- package/src/worlds/console-fixture/ENV_PROMPT.md +1 -0
- package/src/worlds/console-fixture/console/client.ts +72 -0
- package/src/worlds/console-fixture/world.ts +73 -0
- package/src/worlds/index.ts +15 -0
- package/src/worlds/minecraft/ENV_PROMPT.md +32 -0
- package/src/worlds/minecraft/ENV_PROMPT_CAMERA.md +2 -0
- package/src/worlds/minecraft/LICENSE-mineflayer-pathfinder.txt +21 -0
- package/src/worlds/minecraft/README.md +917 -0
- package/src/worlds/minecraft/blueprint-plan.ts +915 -0
- package/src/worlds/minecraft/blueprint-registry.ts +566 -0
- package/src/worlds/minecraft/blueprint-repair.ts +236 -0
- package/src/worlds/minecraft/blueprint-resource.ts +272 -0
- package/src/worlds/minecraft/blueprint.ts +492 -0
- package/src/worlds/minecraft/body-lease.ts +340 -0
- package/src/worlds/minecraft/bridge.ts +749 -0
- package/src/worlds/minecraft/check.ts +651 -0
- package/src/worlds/minecraft/chests.ts +292 -0
- package/src/worlds/minecraft/client-launch.ts +272 -0
- package/src/worlds/minecraft/client-options.ts +85 -0
- package/src/worlds/minecraft/client-skins.ts +164 -0
- package/src/worlds/minecraft/client-window.ps1 +91 -0
- package/src/worlds/minecraft/client.ts +423 -0
- package/src/worlds/minecraft/combat-context.ts +109 -0
- package/src/worlds/minecraft/combat.ts +1552 -0
- package/src/worlds/minecraft/config.ts +499 -0
- package/src/worlds/minecraft/console/access.ts +218 -0
- package/src/worlds/minecraft/console/client.ts +244 -0
- package/src/worlds/minecraft/console/log.ts +110 -0
- package/src/worlds/minecraft/console/mount.ts +420 -0
- package/src/worlds/minecraft/console/skin.ts +257 -0
- package/src/worlds/minecraft/console/style.css +154 -0
- package/src/worlds/minecraft/console/world.ts +449 -0
- package/src/worlds/minecraft/deaths.ts +135 -0
- package/src/worlds/minecraft/definition.ts +18 -0
- package/src/worlds/minecraft/engine-child.ts +274 -0
- package/src/worlds/minecraft/engine-ipc.ts +98 -0
- package/src/worlds/minecraft/entity-facts.ts +132 -0
- package/src/worlds/minecraft/escape.ts +407 -0
- package/src/worlds/minecraft/executor.ts +14494 -0
- package/src/worlds/minecraft/explored.ts +156 -0
- package/src/worlds/minecraft/geometry.ts +205 -0
- package/src/worlds/minecraft/goal-plan.ts +647 -0
- package/src/worlds/minecraft/item-break.ts +126 -0
- package/src/worlds/minecraft/item-facts.ts +146 -0
- package/src/worlds/minecraft/item-pick.ts +67 -0
- package/src/worlds/minecraft/level-dat.ts +176 -0
- package/src/worlds/minecraft/log.ts +132 -0
- package/src/worlds/minecraft/mineflayer-fixes.ts +1180 -0
- package/src/worlds/minecraft/names.ts +377 -0
- package/src/worlds/minecraft/pathfinder-lib.d.ts +13 -0
- package/src/worlds/minecraft/pathfinder-perf.ts +796 -0
- package/src/worlds/minecraft/piglin.ts +49 -0
- package/src/worlds/minecraft/policy.ts +547 -0
- package/src/worlds/minecraft/precheck.ts +655 -0
- package/src/worlds/minecraft/proxy.ts +445 -0
- package/src/worlds/minecraft/ranged.ts +635 -0
- package/src/worlds/minecraft/readouts.ts +102 -0
- package/src/worlds/minecraft/round.ts +26 -0
- package/src/worlds/minecraft/search-observation.ts +92 -0
- package/src/worlds/minecraft/server-config.ts +467 -0
- package/src/worlds/minecraft/server.ts +658 -0
- package/src/worlds/minecraft/show.ts +84 -0
- package/src/worlds/minecraft/skills.ts +1763 -0
- package/src/worlds/minecraft/terrain.ts +1735 -0
- package/src/worlds/minecraft/window.ts +96 -0
- package/src/worlds/minecraft/works.ts +179 -0
- package/src/worlds/minecraft/world.ts +6438 -0
- package/src/worlds/qq/ENV_PROMPT.md +5 -0
- package/src/worlds/qq/config.ts +108 -0
- package/src/worlds/qq/console/client.ts +126 -0
- package/src/worlds/qq/console/events.ts +155 -0
- package/src/worlds/qq/console/gate.ts +232 -0
- package/src/worlds/qq/console/roster.ts +203 -0
- package/src/worlds/qq/console/style.css +65 -0
- package/src/worlds/qq/conversation.ts +28 -0
- package/src/worlds/qq/definition.ts +62 -0
- package/src/worlds/qq/driver.ts +375 -0
- package/src/worlds/qq/history-tools.ts +209 -0
- package/src/worlds/qq/normalize.ts +323 -0
- package/src/worlds/qq/qface-map.ts +282 -0
- package/src/worlds/qq/vision-prompt.ts +27 -0
- package/src/worlds/qq/vision.ts +642 -0
- package/src/worlds/qq/vlm.ts +142 -0
- package/src/worlds/qq/world.ts +1503 -0
- package/src/worlds/terminal/ENV_PROMPT.md +11 -0
- package/src/worlds/terminal/config.ts +9 -0
- package/src/worlds/terminal/definition.ts +14 -0
- package/src/worlds/terminal/world.ts +700 -0
- package/src/worlds/websearch/ENV_PROMPT.md +1 -0
- package/src/worlds/websearch/brave-client.ts +261 -0
- package/src/worlds/websearch/config.ts +75 -0
- package/src/worlds/websearch/definition.ts +20 -0
- package/src/worlds/websearch/world.ts +171 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 面板 `access` —— 权限与作弊。
|
|
3
|
+
*
|
|
4
|
+
* 专用服务器没有单人存档里那个「开作弊」勾选框。同一件事在这里是两半:**谁在
|
|
5
|
+
* ops.json 里**(能不能下 /tp、/gamemode、/spectate 这类指令),以及
|
|
6
|
+
* `op-permission-level`(op 拿到第几级,4 才够全部指令)。所以这一屏的主体是一份
|
|
7
|
+
* 名单,而不是一个开关。
|
|
8
|
+
*
|
|
9
|
+
* 吃这份名单的是 **bot 与人在游戏里打的命令**:mc_escape 的 /tp、把玩家传送到她
|
|
10
|
+
* 旁边、摄像机附身——服务器由 World 托管时它们走控制台 stdin(天生 4 级,不看名单),
|
|
11
|
+
* 外部起的服务器上就只剩这条退路。
|
|
12
|
+
*
|
|
13
|
+
* 时机与「存档与玩法」同一条规则,只是这里有两份都只在启动时读的文件:停机时改
|
|
14
|
+
* 文件;跑着且是托管的,名单能走控制台 op/deop 当场生效(服务端自己写回文件),
|
|
15
|
+
* server.properties 那几项一概要停机;外部起的跑着时什么都改不了。
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
ConsolePanelContext,
|
|
20
|
+
ConsolePanel,
|
|
21
|
+
} from '../../../web/shared/client-panel.ts';
|
|
22
|
+
import {
|
|
23
|
+
errText,
|
|
24
|
+
msgLine,
|
|
25
|
+
type MinecraftAccessMember,
|
|
26
|
+
type MinecraftAccessState,
|
|
27
|
+
} from './client.ts';
|
|
28
|
+
|
|
29
|
+
const DESC =
|
|
30
|
+
'专用服没有「开作弊」那个勾选框:能不能下 /tp、/gamemode 全看名字在不在管理员'
|
|
31
|
+
+ '名单(ops.json)里。她的 mc_escape、把你传送到她旁边、摄像机附身都吃这份名单'
|
|
32
|
+
+ '——服务器由本 World 托管时它们走控制台,不吃;外部起的服务器上就只剩这条路。';
|
|
33
|
+
|
|
34
|
+
/** 三个有名有姓的身份 + 名单里别的名字 */
|
|
35
|
+
const ROLE_ZH: Record<MinecraftAccessMember['role'], { label: string; note: string }> = {
|
|
36
|
+
bot: { label: '她', note: '游戏内账号(worlds.minecraft.username)' },
|
|
37
|
+
camera: { label: '摄像机', note: '观察者客户端账号' },
|
|
38
|
+
player: { label: '玩家', note: '你自己进服那份客户端' },
|
|
39
|
+
other: { label: '其他', note: '名单里已有的名字' },
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const LEVELS = [
|
|
43
|
+
{ value: '1', label: '1 级 · 绕过出生点保护' },
|
|
44
|
+
{ value: '2', label: '2 级 · 大部分单人作弊指令' },
|
|
45
|
+
{ value: '3', label: '3 级 · 加上封禁踢人' },
|
|
46
|
+
{ value: '4', label: '4 级 · 全部(含 /stop)' },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
export const accessPanel: ConsolePanel = {
|
|
50
|
+
mount(ctx: ConsolePanelContext) {
|
|
51
|
+
const { ui, root } = ctx;
|
|
52
|
+
|
|
53
|
+
// -----------------------------------------------------------------------
|
|
54
|
+
// 卡一:管理员名单
|
|
55
|
+
// -----------------------------------------------------------------------
|
|
56
|
+
const card = ui.sheet({ title: '权限与作弊', en: 'cheats & permissions', desc: DESC });
|
|
57
|
+
const s = card.body;
|
|
58
|
+
root.appendChild(card.el);
|
|
59
|
+
|
|
60
|
+
const msg = msgLine(ctx);
|
|
61
|
+
s.appendChild(msg.el);
|
|
62
|
+
|
|
63
|
+
const stateBox = ui.h('div');
|
|
64
|
+
s.append(ui.section('当前'), stateBox);
|
|
65
|
+
|
|
66
|
+
const list = ui.table({ head: ['名字', '身份', '作弊权限', ''] });
|
|
67
|
+
s.append(ui.section('管理员名单', 'ops.json;服务器只在启动时读它'), list.el);
|
|
68
|
+
|
|
69
|
+
const addName = ui.input({ placeholder: '游戏名' });
|
|
70
|
+
const btnAdd = ui.button('授权', { size: 'sm' });
|
|
71
|
+
const addBar = ui.actions();
|
|
72
|
+
addBar.append(ui.field('另外授权一个名字', addName), ui.h('span', 'grow'), btnAdd);
|
|
73
|
+
s.append(addBar);
|
|
74
|
+
|
|
75
|
+
// -----------------------------------------------------------------------
|
|
76
|
+
// 卡二:服务器权限项(server.properties)
|
|
77
|
+
// -----------------------------------------------------------------------
|
|
78
|
+
const propCard = ui.sheet({
|
|
79
|
+
title: '服务器权限项',
|
|
80
|
+
en: 'permission properties',
|
|
81
|
+
desc: '写在 server.properties 里,服务器只在启动时读一次——这几项都要停机改,下次启动生效。',
|
|
82
|
+
});
|
|
83
|
+
const p = propCard.body;
|
|
84
|
+
root.appendChild(propCard.el);
|
|
85
|
+
|
|
86
|
+
const levelSel = ui.select({ options: LEVELS });
|
|
87
|
+
const cmdBlockIn = ui.checkbox('命令方块可用');
|
|
88
|
+
const flightIn = ui.checkbox('允许飞行(不踢)');
|
|
89
|
+
const onlineIn = ui.checkbox('正版验证');
|
|
90
|
+
const whiteIn = ui.checkbox('白名单');
|
|
91
|
+
const btnApply = ui.button('应用', { size: 'sm', variant: 'primary' });
|
|
92
|
+
const propForm = ui.h('div', 'mc-form');
|
|
93
|
+
propForm.append(ui.field('op 权限等级', levelSel));
|
|
94
|
+
const propBar = ui.actions();
|
|
95
|
+
propBar.append(cmdBlockIn.el, flightIn.el, onlineIn.el, whiteIn.el, ui.h('span', 'grow'), btnApply);
|
|
96
|
+
const propNote = ui.h('div', 'pagedesc',
|
|
97
|
+
'正版验证开着时她、摄像机、玩家这三个离线账号都进不来;白名单开着则名单外的进不来。');
|
|
98
|
+
p.append(propForm, propBar, propNote);
|
|
99
|
+
|
|
100
|
+
// -----------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
let cur: MinecraftAccessState | null = null;
|
|
103
|
+
|
|
104
|
+
function memberRow(m: MinecraftAccessMember, st: MinecraftAccessState): void {
|
|
105
|
+
const role = ROLE_ZH[m.role];
|
|
106
|
+
const who = ui.h('span', null, role.label);
|
|
107
|
+
who.title = role.note;
|
|
108
|
+
const btn = ui.button(m.op ? '收回' : '授权', { size: 'sm', variant: m.op ? 'plain' : 'primary' });
|
|
109
|
+
btn.disabled = !st.configured || (st.live && !st.hosted);
|
|
110
|
+
btn.addEventListener('click', () => {
|
|
111
|
+
void act(() => ctx.invoke<MinecraftAccessState>('setOp', [m.name, !m.op]), m.op ? '收回中…' : '授权中…');
|
|
112
|
+
}, { signal: ctx.signal });
|
|
113
|
+
list.addRow([
|
|
114
|
+
{ text: m.name, cls: 'mono' },
|
|
115
|
+
who,
|
|
116
|
+
m.op ? ui.pill(`有 · ${m.level} 级`, 'on') : ui.pill('没有', 'off'),
|
|
117
|
+
btn,
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function render(st: MinecraftAccessState | null): void {
|
|
122
|
+
if (ctx.signal.aborted) return;
|
|
123
|
+
cur = st;
|
|
124
|
+
if (!st) {
|
|
125
|
+
msg.say('权限设置不可用', true);
|
|
126
|
+
stateBox.replaceChildren(ui.placeholder('读不到服务器目录里的 ops.json'));
|
|
127
|
+
list.clear('—');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
stateBox.replaceChildren(ui.kv([
|
|
131
|
+
{
|
|
132
|
+
k: '服务器',
|
|
133
|
+
v: ui.pill(
|
|
134
|
+
st.live ? (st.hosted ? '运行中(本 World 托管)' : '运行中(外部)') : '已停机',
|
|
135
|
+
st.live ? 'on' : 'plain',
|
|
136
|
+
),
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
k: '自动授权',
|
|
140
|
+
v: st.autoOp
|
|
141
|
+
? ui.pill('开 · 每次启动前补齐名单', 'on')
|
|
142
|
+
: ui.pill('关 · 只按名单现状来', 'off'),
|
|
143
|
+
},
|
|
144
|
+
{ k: '服务器目录', v: st.serverDir || '(未配置)' },
|
|
145
|
+
]));
|
|
146
|
+
|
|
147
|
+
list.clear();
|
|
148
|
+
if (!st.members.length) list.clear('名单是空的');
|
|
149
|
+
else for (const m of st.members) memberRow(m, st);
|
|
150
|
+
|
|
151
|
+
levelSel.value = String(st.settings.opPermissionLevel);
|
|
152
|
+
cmdBlockIn.setChecked(st.settings.enableCommandBlock);
|
|
153
|
+
flightIn.setChecked(st.settings.allowFlight);
|
|
154
|
+
onlineIn.setChecked(st.settings.onlineMode);
|
|
155
|
+
whiteIn.setChecked(st.settings.whiteList);
|
|
156
|
+
|
|
157
|
+
const stopped = !st.live;
|
|
158
|
+
const off = !st.configured;
|
|
159
|
+
addName.disabled = btnAdd.disabled = off || (st.live && !st.hosted);
|
|
160
|
+
levelSel.disabled = cmdBlockIn.input.disabled = off || !stopped;
|
|
161
|
+
flightIn.input.disabled = onlineIn.input.disabled = off || !stopped;
|
|
162
|
+
whiteIn.input.disabled = btnApply.disabled = off || !stopped;
|
|
163
|
+
|
|
164
|
+
msg.say(st.detail || (st.live && !st.hosted
|
|
165
|
+
? '服务器是外部起的,没有控制台可用:名单与这几项都要先停机才改得了'
|
|
166
|
+
: st.live
|
|
167
|
+
? '服务器跑着:名单走控制台当场生效,下面那几项要先停机'
|
|
168
|
+
: ''));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const refresh = (): Promise<void> =>
|
|
172
|
+
ctx.invoke<MinecraftAccessState>('state').then(render, () => render(null));
|
|
173
|
+
|
|
174
|
+
/** 一次动作:置灰两颗钮 → 调服务端 → 拿回来的新状态重画(先解锁再重画)。 */
|
|
175
|
+
async function act(fn: () => Promise<MinecraftAccessState>, working: string): Promise<void> {
|
|
176
|
+
msg.say(working);
|
|
177
|
+
const lock = ui.disable(btnAdd, btnApply);
|
|
178
|
+
let next: MinecraftAccessState | null = null;
|
|
179
|
+
try {
|
|
180
|
+
next = await fn();
|
|
181
|
+
} catch (err) {
|
|
182
|
+
if (!ctx.signal.aborted) msg.say(`操作失败: ${errText(err)}`, true);
|
|
183
|
+
} finally {
|
|
184
|
+
lock.dispose();
|
|
185
|
+
}
|
|
186
|
+
if (next) render(next);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
btnAdd.addEventListener('click', () => {
|
|
190
|
+
const name = addName.value.trim();
|
|
191
|
+
if (!name) {
|
|
192
|
+
msg.say('先写个游戏名', true);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
void act(async () => {
|
|
196
|
+
const next = await ctx.invoke<MinecraftAccessState>('setOp', [name, true]);
|
|
197
|
+
addName.value = '';
|
|
198
|
+
return next;
|
|
199
|
+
}, '授权中…');
|
|
200
|
+
}, { signal: ctx.signal });
|
|
201
|
+
|
|
202
|
+
btnApply.addEventListener('click', () => {
|
|
203
|
+
if (cur?.live) {
|
|
204
|
+
msg.say('这几项服务器只在启动时读,要先把它停下来', true);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
void act(() => ctx.invoke<MinecraftAccessState>('apply', [{
|
|
208
|
+
opPermissionLevel: Number(levelSel.value),
|
|
209
|
+
enableCommandBlock: cmdBlockIn.checked,
|
|
210
|
+
allowFlight: flightIn.checked,
|
|
211
|
+
onlineMode: onlineIn.checked,
|
|
212
|
+
whiteList: whiteIn.checked,
|
|
213
|
+
}]), '应用中…');
|
|
214
|
+
}, { signal: ctx.signal });
|
|
215
|
+
|
|
216
|
+
void refresh();
|
|
217
|
+
},
|
|
218
|
+
};
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minecraft World 的浏览器插件 —— 挂载 / 皮肤 / 存档与玩法 / 权限与作弊 /
|
|
3
|
+
* World 日志五个面板。
|
|
4
|
+
*
|
|
5
|
+
* 这个文件只做两件事:**装配**(把五个面板接到局部 id 上)与**共享 helper**
|
|
6
|
+
* (各面板都要的一行回执、错误措辞,以及服务端各方法的返回形状)。
|
|
7
|
+
* 面板本体各在自己的文件里。
|
|
8
|
+
*
|
|
9
|
+
* 与外界的依赖只有一条:`client-panel.ts` 里的类型与 `toDisposable`。没有
|
|
10
|
+
* import 控制台内部模块,没有 `fetch`,没有 `document.body`,没有 `window.__*`
|
|
11
|
+
* ——数据面一律走 `ctx.invoke` / `ctx.invokeBinary`,DOM 一律用 `ctx.ui` 的原语,
|
|
12
|
+
* 定时器一律走 `ctx.interval`,ObjectURL 一律交给 `ctx.own`。
|
|
13
|
+
*
|
|
14
|
+
* `style.css` 是本 provider 自己的样式:只放 `ctx.ui` 没有对应原语的那几块
|
|
15
|
+
* (挂载行状态词的配色、并排表单、保留换行的正文段、图片预览),用 `mc-` 前缀
|
|
16
|
+
* 避开控制台的通用 class。
|
|
17
|
+
*
|
|
18
|
+
* **服务端那边的 panel id 与方法名一个字都没改。** 挂载面板管的是三条链路
|
|
19
|
+
* (游戏服务器 / 观察者客户端 / 玩家客户端),它们各自的 `state` / `start` / `stop`
|
|
20
|
+
* 早就在那儿了;这里只是在方法名前面缀上是哪一条(`server.state`、`client.start`),
|
|
21
|
+
* 好让三条链路共处一个局部 id。
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type {
|
|
25
|
+
ConsoleClientBundle,
|
|
26
|
+
ConsolePanelContext,
|
|
27
|
+
} from '../../../web/shared/client-panel.ts';
|
|
28
|
+
import './style.css';
|
|
29
|
+
import { accessPanel } from './access.ts';
|
|
30
|
+
import { skinPanel } from './skin.ts';
|
|
31
|
+
import { mountPanel } from './mount.ts';
|
|
32
|
+
import { worldPanel } from './world.ts';
|
|
33
|
+
import { logPanel } from './log.ts';
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// 共享类型:服务端 `MinecraftWorld.invokePanel` 各方法的返回形状
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
/** 三条链路共有的那几项。托管进程没起时 `pid` 为 null,外部起的照样可能就绪。 */
|
|
40
|
+
interface MinecraftLaneCommon {
|
|
41
|
+
/** `stopped` / `starting` / `running` / `error` */
|
|
42
|
+
phase: string;
|
|
43
|
+
detail: string | null;
|
|
44
|
+
pid: number | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** `mount.server.*` */
|
|
48
|
+
export interface MinecraftServerState extends MinecraftLaneCommon {
|
|
49
|
+
address: string;
|
|
50
|
+
/** 端口当下可连(外部自己起的服务器也算) */
|
|
51
|
+
reachable: boolean;
|
|
52
|
+
serverDir: string;
|
|
53
|
+
configured: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** `mount.client.*`。客户端没有可探的端口,判据是"窗口出来了没"。 */
|
|
57
|
+
export interface MinecraftClientState extends MinecraftLaneCommon {
|
|
58
|
+
enabled: boolean;
|
|
59
|
+
windowReady: boolean;
|
|
60
|
+
gameDir: string;
|
|
61
|
+
versionId: string;
|
|
62
|
+
username: string;
|
|
63
|
+
configured: boolean;
|
|
64
|
+
command: string | null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface MinecraftGameSettings {
|
|
68
|
+
gamemode: string;
|
|
69
|
+
difficulty: string;
|
|
70
|
+
hardcore: boolean;
|
|
71
|
+
pvp: boolean;
|
|
72
|
+
spawnMonsters: boolean;
|
|
73
|
+
/** 空 = 随机 */
|
|
74
|
+
levelSeed: string;
|
|
75
|
+
levelName: string;
|
|
76
|
+
/** 世界类型;只在世界第一次生成时起作用 */
|
|
77
|
+
levelType: string;
|
|
78
|
+
/** 生成器细则 JSON(超平坦层配方 / 单群系名) */
|
|
79
|
+
generatorSettings: string;
|
|
80
|
+
generateStructures: boolean;
|
|
81
|
+
allowNether: boolean;
|
|
82
|
+
spawnProtection: number;
|
|
83
|
+
viewDistance: number;
|
|
84
|
+
simulationDistance: number;
|
|
85
|
+
maxWorldSize: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** 存档目录的 level.dat 自述;读不出来的项为 null */
|
|
89
|
+
interface MinecraftLevelDatInfo {
|
|
90
|
+
levelName: string | null;
|
|
91
|
+
lastPlayed: number | null;
|
|
92
|
+
seed: string | null;
|
|
93
|
+
gameType: number | null;
|
|
94
|
+
difficulty: number | null;
|
|
95
|
+
hardcore: boolean | null;
|
|
96
|
+
version: string | null;
|
|
97
|
+
/** flat / amplified / large_biomes / normal */
|
|
98
|
+
generator: string | null;
|
|
99
|
+
dayTime: number | null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface MinecraftWorldInfo {
|
|
103
|
+
name: string;
|
|
104
|
+
/** 目录里有 level.dat = 已经生成过 */
|
|
105
|
+
generated: boolean;
|
|
106
|
+
/** 最后活跃(ISO):优先 level.dat 的 LastPlayed,退回文件 mtime */
|
|
107
|
+
modified: string | null;
|
|
108
|
+
sizeBytes: number;
|
|
109
|
+
/** 已生成的附属维度:nether / the_end */
|
|
110
|
+
dimensions: string[];
|
|
111
|
+
info: MinecraftLevelDatInfo | null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** `world.state` */
|
|
115
|
+
export interface MinecraftWorldState {
|
|
116
|
+
configured: boolean;
|
|
117
|
+
serverDir: string;
|
|
118
|
+
/** 服务器跑着(含外部起的):只有难度与默认游戏模式能热改 */
|
|
119
|
+
live: boolean;
|
|
120
|
+
/** 由 World 托管;只有托管进程提供控制台 stdin */
|
|
121
|
+
hosted: boolean;
|
|
122
|
+
worlds: MinecraftWorldInfo[];
|
|
123
|
+
settings: MinecraftGameSettings;
|
|
124
|
+
/** 超平坦层配方的常用预设(服务端那一份词表) */
|
|
125
|
+
flatPresets: ReadonlyArray<{ id: string; label: string; note: string; json: string }>;
|
|
126
|
+
/** 世界类型的中文词表(同上) */
|
|
127
|
+
levelTypes: ReadonlyArray<{ value: string; label: string; note: string }>;
|
|
128
|
+
detail: string | null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** `access.state` 名单里的一位 */
|
|
132
|
+
export interface MinecraftAccessMember {
|
|
133
|
+
name: string;
|
|
134
|
+
/** bot=她自己 / camera=观察者摄像机 / player=人的客户端 / other=名单里别的名字 */
|
|
135
|
+
role: 'bot' | 'camera' | 'player' | 'other';
|
|
136
|
+
op: boolean;
|
|
137
|
+
/** 名单里那条的权限等级;不在名单里为 0 */
|
|
138
|
+
level: number;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** `access.state` */
|
|
142
|
+
export interface MinecraftAccessState {
|
|
143
|
+
configured: boolean;
|
|
144
|
+
serverDir: string;
|
|
145
|
+
/** 服务器跑着(含外部起的) */
|
|
146
|
+
live: boolean;
|
|
147
|
+
/** 由 World 托管:跑着的时候名单也能经控制台 op/deop 当场改 */
|
|
148
|
+
hosted: boolean;
|
|
149
|
+
/** worlds.minecraft.local.cheats:每次启动前把三个名字补进名单 */
|
|
150
|
+
autoOp: boolean;
|
|
151
|
+
members: MinecraftAccessMember[];
|
|
152
|
+
settings: {
|
|
153
|
+
opPermissionLevel: number;
|
|
154
|
+
enableCommandBlock: boolean;
|
|
155
|
+
allowFlight: boolean;
|
|
156
|
+
onlineMode: boolean;
|
|
157
|
+
whiteList: boolean;
|
|
158
|
+
};
|
|
159
|
+
detail: string | null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** `skin.state` 里某个角色选着的那张 */
|
|
163
|
+
interface MinecraftSkinInfo {
|
|
164
|
+
width: number;
|
|
165
|
+
height: number;
|
|
166
|
+
bytes: number;
|
|
167
|
+
/** 选中的时刻(ISO) */
|
|
168
|
+
at: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** `skin.state` 里的一个角色:她自己,或人那份客户端 */
|
|
172
|
+
export interface MinecraftSkinRole {
|
|
173
|
+
role: 'bot' | 'player';
|
|
174
|
+
/** 皮肤按这个账号名铺进游戏目录 */
|
|
175
|
+
username: string;
|
|
176
|
+
/** 选着的那张;null = 没选 */
|
|
177
|
+
skin: MinecraftSkinInfo | null;
|
|
178
|
+
/** 两份游戏目录里此刻铺着的正是这张 */
|
|
179
|
+
installed: { camera: boolean; player: boolean };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** `skin.state` */
|
|
183
|
+
export interface MinecraftSkinState {
|
|
184
|
+
roles: MinecraftSkinRole[];
|
|
185
|
+
dirs: { camera: string; player: string };
|
|
186
|
+
/** CustomSkinLoader 装了没:没装则皮肤铺得再对也不会被读 */
|
|
187
|
+
mod: { camera: boolean; player: boolean };
|
|
188
|
+
/** 客户端正跑着:换了皮肤要它重进一次服务器才刷新 */
|
|
189
|
+
live: { camera: boolean; player: boolean };
|
|
190
|
+
detail: string | null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** `log.entries` 的一条 */
|
|
194
|
+
export interface MinecraftLogEntry {
|
|
195
|
+
seq: number;
|
|
196
|
+
ts: string;
|
|
197
|
+
lane: string;
|
|
198
|
+
event: string;
|
|
199
|
+
msg: string;
|
|
200
|
+
taskId?: number;
|
|
201
|
+
durMs?: number;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
// 共享 helper
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
|
|
208
|
+
/** 错误 → 一句人话。`ConsoleInvokeError` 带的就是服务端的中文措辞。 */
|
|
209
|
+
export function errText(err: unknown): string {
|
|
210
|
+
return err instanceof Error ? err.message : String(err);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** 一行回执(`.msgline`),三个面板都有一条。`say(null)` 清空。 */
|
|
214
|
+
export interface MinecraftMsgLine {
|
|
215
|
+
el: HTMLDivElement;
|
|
216
|
+
say(text?: string | null, bad?: boolean): void;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function msgLine(ctx: ConsolePanelContext, cls?: string): MinecraftMsgLine {
|
|
220
|
+
const el = ctx.ui.msgline('');
|
|
221
|
+
if (cls) el.classList.add(cls);
|
|
222
|
+
return {
|
|
223
|
+
el,
|
|
224
|
+
say(text, bad) {
|
|
225
|
+
el.textContent = text ?? '';
|
|
226
|
+
el.classList.toggle('bad', bad === true);
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
const bundle: ConsoleClientBundle = {
|
|
234
|
+
// 键是**局部** panel id,与服务端 `console().panels[].id` 一一对应。
|
|
235
|
+
panels: {
|
|
236
|
+
mount: mountPanel,
|
|
237
|
+
skin: skinPanel,
|
|
238
|
+
world: worldPanel,
|
|
239
|
+
access: accessPanel,
|
|
240
|
+
log: logPanel,
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
export default bundle;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* World 日志面板,按泳道筛选增量记录。
|
|
3
|
+
* 日志滚动与裁剪由 ui.log 管理,轮询随 ctx.interval 的面板生命周期结束。
|
|
4
|
+
* WorldConsoleDecl 仅提供 invoke;按序号每两秒拉取新增条目。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
ConsolePanelContext,
|
|
9
|
+
ConsolePanel,
|
|
10
|
+
} from '../../../web/shared/client-panel.ts';
|
|
11
|
+
import type { MinecraftLogEntry } from './client.ts';
|
|
12
|
+
|
|
13
|
+
const DESC =
|
|
14
|
+
'一场试玩里下过的每一道令、投递出去的每一句、每一步的结局与耗时。'
|
|
15
|
+
+ '同一份进运行日志(data/runs/<run>/log.jsonl,区域 worlds.minecraft.<泳道>,事后 logq 查),这里只留最近 2000 条。';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 泳道表。与 `src/worlds/minecraft/log.ts` 的 `LANE_ZH` 同一份词表——那边是 Node
|
|
19
|
+
* 模块(读写文件),浏览器 bundle 不能 import 它,所以这里抄一份。
|
|
20
|
+
*/
|
|
21
|
+
const LANES: ReadonlyArray<readonly [string, string]> = [
|
|
22
|
+
['tool', '工具'], ['event', '投递'], ['task', '任务'], ['skill', '技能'], ['craft', '合成'],
|
|
23
|
+
['inventory', '物品'], ['path', '寻路'], ['body', '身体'], ['reflex', '反射'], ['combat', '战斗'],
|
|
24
|
+
['link', '连接'], ['world', '世界'],
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const LANE_ZH: Record<string, string> = Object.fromEntries(LANES);
|
|
28
|
+
|
|
29
|
+
/** 内存里留多少条(与落盘那份无关;那边是完整的)。 */
|
|
30
|
+
const KEEP = 2000;
|
|
31
|
+
const POLL_MS = 2000;
|
|
32
|
+
|
|
33
|
+
export const logPanel: ConsolePanel = {
|
|
34
|
+
mount(ctx: ConsolePanelContext) {
|
|
35
|
+
const { ui, root } = ctx;
|
|
36
|
+
const card = ui.foldSheet('log', { title: 'World 日志', en: 'module log', desc: DESC });
|
|
37
|
+
const s = card.body;
|
|
38
|
+
root.appendChild(card.el);
|
|
39
|
+
|
|
40
|
+
/** 关掉的泳道。默认一个都不关。 */
|
|
41
|
+
const off = new Set<string>();
|
|
42
|
+
const bar = ui.rowbar();
|
|
43
|
+
for (const [lane, label] of LANES) {
|
|
44
|
+
const box = ui.checkbox(label, {
|
|
45
|
+
checked: true,
|
|
46
|
+
title: `不看${label}这一类记录时把它取消勾选`,
|
|
47
|
+
onChange: (on) => {
|
|
48
|
+
if (on) off.delete(lane); else off.add(lane);
|
|
49
|
+
redraw();
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
bar.appendChild(box.el);
|
|
53
|
+
}
|
|
54
|
+
const btnEnd = ui.button('回到底部', { size: 'sm' });
|
|
55
|
+
bar.append(ui.h('span', 'grow'), btnEnd);
|
|
56
|
+
s.appendChild(bar);
|
|
57
|
+
|
|
58
|
+
const view = ui.log({ max: KEEP, maxHeight: '320px', empty: '(等待日志…)' });
|
|
59
|
+
s.appendChild(view.el);
|
|
60
|
+
btnEnd.addEventListener('click', () => view.scrollToEnd(), { signal: ctx.signal });
|
|
61
|
+
|
|
62
|
+
/** 收到过的条目(未过滤)。切泳道时按它整份重铺。 */
|
|
63
|
+
let entries: MinecraftLogEntry[] = [];
|
|
64
|
+
let lastSeq = 0;
|
|
65
|
+
|
|
66
|
+
const lineOf = (e: MinecraftLogEntry): string => {
|
|
67
|
+
const t = ctx.ui.fmt.clock(e.ts);
|
|
68
|
+
const task = e.taskId ? ` #${e.taskId}` : '';
|
|
69
|
+
const dur = e.durMs === undefined ? '' : ` (${e.durMs}ms)`;
|
|
70
|
+
return `[${t}] ${LANE_ZH[e.lane] ?? e.lane}${task} | ${e.msg}${dur}`;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const shownCount = (): number => entries.filter((e) => !off.has(e.lane)).length;
|
|
74
|
+
|
|
75
|
+
const noteCount = (): void => {
|
|
76
|
+
card.note.textContent = `${shownCount()} 条${off.size ? `(关了 ${off.size} 条泳道)` : ''}`;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** 切泳道:整份重铺(这是唯一会重画已有行的路径,日志本身只往末尾长)。 */
|
|
80
|
+
const redraw = (): void => {
|
|
81
|
+
view.clear();
|
|
82
|
+
for (const e of entries) if (!off.has(e.lane)) view.append(lineOf(e));
|
|
83
|
+
noteCount();
|
|
84
|
+
view.scrollToEnd();
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
let polling = false;
|
|
88
|
+
const poll = (): void => {
|
|
89
|
+
if (polling) return;
|
|
90
|
+
polling = true;
|
|
91
|
+
void ctx.invoke<{ entries?: MinecraftLogEntry[] }>('entries', [lastSeq]).then(
|
|
92
|
+
(out) => {
|
|
93
|
+
polling = false;
|
|
94
|
+
if (ctx.signal.aborted) return;
|
|
95
|
+
const fresh = out?.entries ?? [];
|
|
96
|
+
if (fresh.length === 0) return;
|
|
97
|
+
for (const e of fresh) lastSeq = Math.max(lastSeq, e.seq);
|
|
98
|
+
entries = entries.concat(fresh).slice(-KEEP);
|
|
99
|
+
for (const e of fresh) if (!off.has(e.lane)) view.append(lineOf(e));
|
|
100
|
+
noteCount();
|
|
101
|
+
},
|
|
102
|
+
() => { polling = false; /* 下一拍再试 */ },
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
noteCount();
|
|
107
|
+
ctx.interval(poll, POLL_MS);
|
|
108
|
+
poll();
|
|
109
|
+
},
|
|
110
|
+
};
|