minecraft-dev-starter 0.0.0-stage → 0.1.1

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/lib/client.js ADDED
@@ -0,0 +1,1521 @@
1
+ window.__ModuleLoader__.load({
2
+ id: 'minecraft-dev-starter',
3
+ factory: (require) => {
4
+ try {
5
+ var module = { exports: {} };
6
+ var exports = module.exports;
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
11
+ var __export = (target, all) => {
12
+ for (var name2 in all)
13
+ __defProp(target, name2, { get: all[name2], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
+
25
+ // src/client/index.js
26
+ var index_exports = {};
27
+ __export(index_exports, {
28
+ apply: () => apply,
29
+ inject: () => inject,
30
+ name: () => name
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/client/Capsule.js
35
+ var import_react3 = require("react");
36
+
37
+ // src/client/h.js
38
+ var import_react = require("react");
39
+ var h = import_react.createElement;
40
+
41
+ // src/client/icons.js
42
+ function svg(size, children) {
43
+ return h(
44
+ "svg",
45
+ {
46
+ width: size,
47
+ height: size,
48
+ viewBox: "0 0 16 16",
49
+ fill: "none",
50
+ "aria-hidden": "true",
51
+ focusable: "false"
52
+ },
53
+ ...children
54
+ );
55
+ }
56
+ function IconPlay({ size = 13 } = {}) {
57
+ return svg(size, [h("path", { key: "p", d: "M5.5 3.4v9.2l7.2-4.6z", fill: "currentColor" })]);
58
+ }
59
+ function IconStop({ size = 13 } = {}) {
60
+ return svg(size, [h("rect", { key: "r", x: 4.2, y: 4.2, width: 7.6, height: 7.6, rx: 1.4, fill: "currentColor" })]);
61
+ }
62
+ function IconRefresh({ size = 13 } = {}) {
63
+ return svg(size, [
64
+ h("path", {
65
+ key: "a",
66
+ d: "M13 8a5 5 0 1 1-1.46-3.54",
67
+ stroke: "currentColor",
68
+ strokeWidth: 1.5,
69
+ strokeLinecap: "round"
70
+ }),
71
+ h("path", {
72
+ key: "b",
73
+ d: "M13 2.9v3.3h-3.3",
74
+ stroke: "currentColor",
75
+ strokeWidth: 1.5,
76
+ strokeLinecap: "round",
77
+ strokeLinejoin: "round"
78
+ })
79
+ ]);
80
+ }
81
+ function IconWarning({ size = 13 } = {}) {
82
+ return svg(size, [
83
+ h("path", {
84
+ key: "a",
85
+ d: "M8 2.4 14 13H2z",
86
+ stroke: "currentColor",
87
+ strokeWidth: 1.4,
88
+ strokeLinejoin: "round"
89
+ }),
90
+ h("path", { key: "b", d: "M8 6.4v3.1", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round" }),
91
+ h("circle", { key: "c", cx: 8, cy: 11.5, r: 0.85, fill: "currentColor" })
92
+ ]);
93
+ }
94
+ function IconChevronRight({ size = 13 } = {}) {
95
+ return svg(size, [
96
+ h("path", {
97
+ key: "a",
98
+ d: "m6.5 4.5 3.5 3.5-3.5 3.5",
99
+ stroke: "currentColor",
100
+ strokeWidth: 1.5,
101
+ strokeLinecap: "round",
102
+ strokeLinejoin: "round"
103
+ })
104
+ ]);
105
+ }
106
+ function IconCube({ size = 14 } = {}) {
107
+ return svg(size, [
108
+ h("path", {
109
+ key: "a",
110
+ d: "M8 1.9 13.6 4.7v6.6L8 14.1 2.4 11.3V4.7z",
111
+ stroke: "currentColor",
112
+ strokeWidth: 1.3,
113
+ strokeLinejoin: "round"
114
+ }),
115
+ h("path", { key: "b", d: "M2.6 4.8 8 7.5l5.4-2.7M8 7.5v6.4", stroke: "currentColor", strokeWidth: 1.3, strokeLinejoin: "round" })
116
+ ]);
117
+ }
118
+ function IconTerminal({ size = 14 } = {}) {
119
+ return svg(size, [
120
+ h("rect", { key: "a", x: 1.9, y: 2.9, width: 12.2, height: 10.2, rx: 2, stroke: "currentColor", strokeWidth: 1.3 }),
121
+ h("path", {
122
+ key: "b",
123
+ d: "m4.6 6.4 1.9 1.9-1.9 1.9M8.6 10.4h2.9",
124
+ stroke: "currentColor",
125
+ strokeWidth: 1.3,
126
+ strokeLinecap: "round",
127
+ strokeLinejoin: "round"
128
+ })
129
+ ]);
130
+ }
131
+
132
+ // src/client/debug.js
133
+ var reported = /* @__PURE__ */ new Set();
134
+ function report(event, detail = {}) {
135
+ if (reported.has(event)) return;
136
+ reported.add(event);
137
+ try {
138
+ fetch("/mcdev/api/client.report", {
139
+ method: "POST",
140
+ cache: "no-store",
141
+ headers: { "content-type": "application/json" },
142
+ body: JSON.stringify({ event, detail })
143
+ }).catch(() => {
144
+ });
145
+ } catch {
146
+ }
147
+ }
148
+
149
+ // src/client/selection.js
150
+ var STORAGE_KEY = "mcdev.selection";
151
+ function targetKey(target) {
152
+ return `${target.module ?? "."}:${target.taskPath}`;
153
+ }
154
+ function groupByModule(targets) {
155
+ const groups = [];
156
+ for (const target of targets) {
157
+ const module2 = String(target.module ?? ".");
158
+ const group = groups.find((entry) => entry.module === module2);
159
+ if (group === void 0) groups.push({ module: module2, targets: [target] });
160
+ else group.targets.push(target);
161
+ }
162
+ return groups;
163
+ }
164
+ function selectedTarget(project, selection) {
165
+ const targets = (
166
+ /** @type {Array<Record<string, any>>} */
167
+ project?.runTargets ?? []
168
+ );
169
+ if (targets.length === 0) return void 0;
170
+ const remembered = selection[String(project?.root ?? "")];
171
+ return targets.find((target) => targetKey(target) === remembered) ?? targets[0];
172
+ }
173
+ function capsuleText(project, target) {
174
+ if (project === void 0) return "\u672A\u68C0\u6D4B\u5230 MC \u5DE5\u7A0B";
175
+ if (target === void 0) return "\u65E0\u53EF\u8FD0\u884C\u76EE\u6807";
176
+ const module2 = String(target.module ?? ".");
177
+ return module2 === "." ? String(target.label) : `${module2} \xB7 ${target.label}`;
178
+ }
179
+ function readSelection() {
180
+ try {
181
+ const raw = globalThis.localStorage?.getItem(STORAGE_KEY);
182
+ if (typeof raw !== "string") return {};
183
+ const parsed = JSON.parse(raw);
184
+ return parsed !== null && typeof parsed === "object" ? parsed : {};
185
+ } catch {
186
+ return {};
187
+ }
188
+ }
189
+ function writeSelection(selection) {
190
+ try {
191
+ globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(selection));
192
+ } catch {
193
+ }
194
+ }
195
+ function pickLogTarget(history, currentId, pinned) {
196
+ const active = history.find((entry) => entry.status === "starting" || entry.status === "running");
197
+ if (active !== void 0) return active.id;
198
+ if (pinned && currentId !== void 0) return currentId;
199
+ if (currentId !== void 0 && history.some((entry) => entry.id === currentId)) return currentId;
200
+ return history[0]?.id;
201
+ }
202
+ function launchStatusText(entry) {
203
+ if (entry.status === "starting") return "\u542F\u52A8\u4E2D";
204
+ if (entry.status === "running") return "\u8FD0\u884C\u4E2D";
205
+ if (entry.status === "unknown") return "\u72B6\u6001\u672A\u77E5";
206
+ if (entry.outcome === "killed") return "\u5DF2\u505C\u6B62";
207
+ if (entry.outcome === "normal") return "\u5DF2\u9000\u51FA";
208
+ return entry.exitCode === null || entry.exitCode === void 0 ? "\u5DF2\u9000\u51FA" : `\u5DF2\u9000\u51FA(${entry.exitCode})`;
209
+ }
210
+ function capsuleParts(project, target, options = {}) {
211
+ const targetText = capsuleText(project, target);
212
+ if (project === void 0 || target === void 0 || options.multipleProjects !== true) {
213
+ return { project: void 0, target: targetText };
214
+ }
215
+ return { project: String(project.modName ?? project.name ?? project.root), target: targetText };
216
+ }
217
+
218
+ // src/client/state.js
219
+ var import_react2 = require("react");
220
+
221
+ // src/client/api.js
222
+ async function call(method, payload) {
223
+ const response = await fetch(`/mcdev/api/${method}`, {
224
+ method: "POST",
225
+ cache: "no-store",
226
+ headers: { "content-type": "application/json" },
227
+ body: JSON.stringify(payload ?? {})
228
+ });
229
+ let body;
230
+ try {
231
+ body = await response.json();
232
+ } catch {
233
+ throw new Error(`HTTP ${response.status}\uFF1A\u8FD4\u56DE\u4E0D\u662F JSON`);
234
+ }
235
+ if (body?.ok !== true) {
236
+ throw new Error(body?.error?.message ?? `HTTP ${response.status}`);
237
+ }
238
+ return body.result;
239
+ }
240
+
241
+ // src/client/runtime.js
242
+ var DETAIL_KIND = "mcdev.detail";
243
+ var LOG_KIND = "mcdev.logs";
244
+ var runtime = {};
245
+ function open(kind) {
246
+ const sidebar = runtime.ctx?.sidebarRight;
247
+ if (sidebar === void 0) return false;
248
+ try {
249
+ sidebar.openTab(kind);
250
+ return true;
251
+ } catch {
252
+ return false;
253
+ }
254
+ }
255
+ function openDetailTab() {
256
+ return open(DETAIL_KIND);
257
+ }
258
+ function openLogTab() {
259
+ return open(LOG_KIND);
260
+ }
261
+
262
+ // src/client/store.js
263
+ function createStore(initial) {
264
+ let value = initial;
265
+ const listeners = /* @__PURE__ */ new Set();
266
+ return {
267
+ /** @returns {T} */
268
+ get() {
269
+ return value;
270
+ },
271
+ /** @param {Partial<T>} next */
272
+ set(next) {
273
+ value = { ...value, ...next };
274
+ for (const listener of listeners) listener(value);
275
+ },
276
+ /**
277
+ * @param {(value: T) => void} listener
278
+ * @returns {() => void}
279
+ */
280
+ subscribe(listener) {
281
+ listeners.add(listener);
282
+ return () => listeners.delete(listener);
283
+ }
284
+ };
285
+ }
286
+
287
+ // src/client/state.js
288
+ var store = createStore({
289
+ loading: false,
290
+ /** @type {string | undefined} */
291
+ error: void 0,
292
+ /** @type {{ root: string, projects: Array<Record<string, any>>, truncated: boolean, scannedAt: string } | undefined} */
293
+ scan: void 0,
294
+ /** @type {string | undefined} */
295
+ selectedRoot: void 0,
296
+ flyoutOpen: false,
297
+ /** @type {Record<string, string>} 每个工程记住的目标 key */
298
+ selection: readSelection(),
299
+ /** @type {{ autoOpenLogTab?: boolean }} 宿主下发的部署配置 */
300
+ config: { autoOpenLogTab: true },
301
+ /** @type {string | undefined} 当前会话的工作区(由胶囊从会话上下文注入) */
302
+ workspaceRoot: void 0
303
+ });
304
+ async function loadStateInfo() {
305
+ try {
306
+ const info = await call("state.info", {});
307
+ store.set({ config: { autoOpenLogTab: true, ...info?.config ?? {} } });
308
+ } catch {
309
+ }
310
+ }
311
+ function selectTarget(root, key) {
312
+ const selection = { ...store.get().selection, [root]: key };
313
+ writeSelection(selection);
314
+ store.set({ selection });
315
+ }
316
+ var inFlight;
317
+ var inFlightRoot;
318
+ async function loadScan(options = {}) {
319
+ const root = options.root ?? store.get().workspaceRoot;
320
+ if (inFlight !== void 0) {
321
+ if (root === inFlightRoot) return inFlight;
322
+ await inFlight.catch(() => {
323
+ });
324
+ }
325
+ inFlightRoot = root;
326
+ store.set({ loading: true, error: void 0 });
327
+ inFlight = call("projects.scan", {
328
+ ...root === void 0 ? {} : { root },
329
+ ...options.refresh === true ? { refresh: true } : {}
330
+ }).then((result) => {
331
+ const scan = (
332
+ /** @type {NonNullable<ReturnType<typeof store.get>['scan']>} */
333
+ result
334
+ );
335
+ const current = store.get().selectedRoot;
336
+ const stillThere = scan.projects.some((project) => project.root === current);
337
+ store.set({
338
+ loading: false,
339
+ scan,
340
+ workspaceRoot: scan.root,
341
+ error: void 0,
342
+ selectedRoot: stillThere ? current : scan.projects[0]?.root
343
+ });
344
+ }).catch((error) => {
345
+ store.set({ loading: false, error: error instanceof Error ? error.message : String(error) });
346
+ }).finally(() => {
347
+ inFlight = void 0;
348
+ inFlightRoot = void 0;
349
+ });
350
+ return inFlight;
351
+ }
352
+ function selectedProject() {
353
+ const state = store.get();
354
+ const projects = state.scan?.projects ?? [];
355
+ return projects.find((project) => project.root === state.selectedRoot) ?? projects[0];
356
+ }
357
+ function useStoreState() {
358
+ const [state, setState] = (0, import_react2.useState)(store.get());
359
+ (0, import_react2.useEffect)(() => store.subscribe(setState), []);
360
+ return state;
361
+ }
362
+ var launchStore = createStore({
363
+ /** @type {Array<Record<string, any>>} */
364
+ list: [],
365
+ /** @type {string | undefined} */
366
+ error: void 0,
367
+ /** @type {string | undefined} 正在启停的目标 key,用来禁用按钮 */
368
+ busy: void 0,
369
+ /** @type {number} 每次拉取递增,供 tail 刷新用 */
370
+ rev: 0
371
+ });
372
+ async function loadLaunches(root) {
373
+ try {
374
+ const list = await call("launch.list", root === void 0 ? {} : { root });
375
+ launchStore.set({ list: (
376
+ /** @type {Array<Record<string, any>>} */
377
+ list
378
+ ), error: void 0, rev: Date.now() });
379
+ } catch (error) {
380
+ launchStore.set({ error: error instanceof Error ? error.message : String(error) });
381
+ }
382
+ }
383
+ function activeLaunchFor(root) {
384
+ return launchStore.get().list.find(
385
+ (entry) => String(entry.root).toLowerCase() === root.toLowerCase() && (entry.status === "starting" || entry.status === "running")
386
+ );
387
+ }
388
+ async function startTarget(project, target) {
389
+ const key = `${project.root}:${target.taskPath}`;
390
+ launchStore.set({ busy: key, error: void 0 });
391
+ try {
392
+ await call("launch.start", {
393
+ root: project.root,
394
+ taskPath: target.taskPath,
395
+ label: target.label,
396
+ kind: target.kind,
397
+ module: target.module
398
+ });
399
+ if (store.get().config.autoOpenLogTab !== false) openLogTab();
400
+ } catch (error) {
401
+ launchStore.set({ error: error instanceof Error ? error.message : String(error) });
402
+ } finally {
403
+ launchStore.set({ busy: void 0 });
404
+ await loadLaunches();
405
+ }
406
+ }
407
+ async function stopLaunch(id) {
408
+ launchStore.set({ busy: id, error: void 0 });
409
+ try {
410
+ await call("launch.stop", { id });
411
+ } catch (error) {
412
+ launchStore.set({ error: error instanceof Error ? error.message : String(error) });
413
+ } finally {
414
+ launchStore.set({ busy: void 0 });
415
+ await loadLaunches();
416
+ }
417
+ }
418
+ function useLaunches(intervalMs = 1500) {
419
+ const [state, setState] = (0, import_react2.useState)(launchStore.get());
420
+ (0, import_react2.useEffect)(() => {
421
+ const unsubscribe = launchStore.subscribe(setState);
422
+ void loadLaunches();
423
+ let timer;
424
+ const tick = () => {
425
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
426
+ void loadLaunches();
427
+ };
428
+ timer = setInterval(tick, intervalMs);
429
+ return () => {
430
+ unsubscribe();
431
+ clearInterval(timer);
432
+ };
433
+ }, [intervalMs]);
434
+ return state;
435
+ }
436
+
437
+ // src/client/format.js
438
+ var PANEL_TITLE = "Mod Dev";
439
+ var LOADER_LABELS = {
440
+ fabric: "Fabric",
441
+ forge: "Forge",
442
+ neoforge: "NeoForge",
443
+ paper: "Paper",
444
+ spigot: "Spigot",
445
+ architectury: "Architectury",
446
+ unknown: "\u672A\u8BC6\u522B"
447
+ };
448
+ var KIND_LABELS = {
449
+ mod: "\u6A21\u7EC4",
450
+ "server-plugin": "\u670D\u52A1\u7AEF\u63D2\u4EF6",
451
+ "multi-loader": "\u591A\u5E73\u53F0",
452
+ unknown: "\u672A\u77E5"
453
+ };
454
+ function loaderLabel(loader) {
455
+ return LOADER_LABELS[loader ?? "unknown"] ?? loader ?? "\u672A\u8BC6\u522B";
456
+ }
457
+ function kindLabel(kind) {
458
+ return KIND_LABELS[kind ?? "unknown"] ?? kind ?? "\u672A\u77E5";
459
+ }
460
+ function projectTitle(project) {
461
+ if (project === void 0) return "\u2014";
462
+ return project.modName ?? project.name ?? project.root;
463
+ }
464
+ function projectSubtitle(project) {
465
+ if (project === void 0) return "";
466
+ const parts = [loaderLabel(project.loader)];
467
+ if (project.minecraftVersion !== void 0) parts.push(String(project.minecraftVersion));
468
+ if (project.loaderVersion !== void 0) parts.push(`loader ${project.loaderVersion}`);
469
+ return parts.join(" \xB7 ");
470
+ }
471
+ function text(value) {
472
+ if (value === void 0 || value === null || value === "") return "\u2014";
473
+ return String(value);
474
+ }
475
+
476
+ // src/client/Skeleton.js
477
+ function Skeleton({ rows = 3, hint } = {}) {
478
+ return h(
479
+ "div",
480
+ { className: "mcdev-skeleton", "aria-busy": "true" },
481
+ Array.from({ length: rows }, (_value, index) => h("div", { key: index, className: "mcdev-skeleton-row" })),
482
+ hint === void 0 ? void 0 : h("div", { className: "mcdev-hint" }, hint)
483
+ );
484
+ }
485
+
486
+ // src/client/Flyout.js
487
+ function Flyout() {
488
+ const state = useStoreState();
489
+ const launches = useLaunches(1500);
490
+ const projects = state.scan?.projects ?? [];
491
+ const current = projects.find((project) => project.root === state.selectedRoot) ?? projects[0];
492
+ const single = projects.length === 1;
493
+ const running = launches.list.find(
494
+ (entry) => (entry.status === "starting" || entry.status === "running") && String(entry.root).toLowerCase() === String(current?.root ?? "").toLowerCase()
495
+ );
496
+ return h(
497
+ "div",
498
+ { className: "mcdev-flyout", role: "dialog", "aria-label": PANEL_TITLE },
499
+ // ① 表头:与列表之间有一条通栏分割线,滚动时不动
500
+ h(
501
+ "div",
502
+ { className: "mcdev-flyout-head" },
503
+ h(
504
+ "div",
505
+ { className: "mcdev-row", style: { justifyContent: "space-between", alignItems: "center" } },
506
+ h("span", { className: "mcdev-title" }, PANEL_TITLE),
507
+ h(
508
+ "button",
509
+ {
510
+ type: "button",
511
+ className: "mcdev-mini mcdev-mini-icon",
512
+ title: state.loading ? "\u626B\u63CF\u4E2D\u2026" : "\u91CD\u65B0\u626B\u63CF\u5DE5\u4F5C\u533A",
513
+ disabled: state.loading,
514
+ onClick: () => loadScan({ refresh: true })
515
+ },
516
+ h("span", { className: "mcdev-icon" }, h(IconRefresh, { size: 12 }))
517
+ )
518
+ ),
519
+ // 单工程:表头直接承担原来列表行的工作,省掉一层选择
520
+ single && current !== void 0 ? h(
521
+ "div",
522
+ { style: { display: "flex", flexDirection: "column", gap: 2 } },
523
+ h("div", { className: "mcdev-title", title: projectTitle(current) }, projectTitle(current)),
524
+ h("div", { className: "mcdev-sub" }, projectSubtitle(current)),
525
+ h("div", { className: "mcdev-muted mcdev-code mcdev-ellipsis", title: current.root }, current.root)
526
+ ) : h("div", { className: "mcdev-muted mcdev-code mcdev-ellipsis", title: state.scan?.root ?? "" }, state.scan?.root ?? "\u2014"),
527
+ state.error === void 0 ? void 0 : h("div", { className: "mcdev-error" }, state.error),
528
+ launches.error === void 0 ? void 0 : h("div", { className: "mcdev-error" }, launches.error)
529
+ ),
530
+ // ② 列表:只在多工程时出现;每行只有名字与 loader/版本,高度恒定
531
+ single ? void 0 : h(
532
+ "div",
533
+ { className: "mcdev-flyout-list" },
534
+ projects.length === 0 ? state.loading ? h(Skeleton, { rows: 3, hint: "\u6B63\u5728\u626B\u63CF\u5DE5\u4F5C\u533A\u2026" }) : h("div", { className: "mcdev-hint", style: { padding: "10px 4px" } }, "\u6CA1\u6709\u627E\u5230 Gradle \u5DE5\u7A0B") : projects.map(
535
+ (project) => h(
536
+ "button",
537
+ {
538
+ key: project.root,
539
+ type: "button",
540
+ className: "mcdev-list-row",
541
+ "data-current": project.root === current?.root ? "true" : "false",
542
+ title: `${project.root}
543
+ ${kindLabel(project.kind)}`,
544
+ onClick: () => store.set({ selectedRoot: project.root })
545
+ },
546
+ h("span", { className: "mcdev-list-name" }, project.modName ?? project.name ?? project.root),
547
+ h("span", { className: "mcdev-list-meta" }, projectSubtitle(project))
548
+ )
549
+ )
550
+ ),
551
+ // ③ 选择区:选中工程的目标,位置固定
552
+ h(
553
+ "div",
554
+ { className: "mcdev-flyout-pane" },
555
+ current === void 0 ? void 0 : [
556
+ h(
557
+ "div",
558
+ { className: "mcdev-row", style: { justifyContent: "space-between", alignItems: "center" } },
559
+ h(
560
+ "span",
561
+ { className: "mcdev-ellipsis", style: { minWidth: 0 } },
562
+ h("span", { className: "mcdev-muted" }, "\u8FD0\u884C\u76EE\u6807"),
563
+ running === void 0 ? void 0 : h(
564
+ "span",
565
+ { className: "mcdev-inline-status" },
566
+ h("span", {
567
+ className: "mcdev-dot",
568
+ "data-state": running.status === "starting" ? "warn" : "ok",
569
+ "data-pulse": "true"
570
+ }),
571
+ `${running.label} ${running.status === "starting" ? "\u542F\u52A8\u4E2D" : "\u8FD0\u884C\u4E2D"} \xB7 ${elapsed(running.startedAt)}`
572
+ )
573
+ ),
574
+ h(
575
+ "button",
576
+ { type: "button", className: "mcdev-mini", onClick: () => openDetailTab() },
577
+ "\u8BE6\u60C5",
578
+ h("span", { className: "mcdev-icon" }, h(IconChevronRight, { size: 12 }))
579
+ )
580
+ ),
581
+ renderTargets(current, state.selection, running !== void 0)
582
+ ]
583
+ )
584
+ );
585
+ }
586
+ function renderTargets(project, selection, locked) {
587
+ const targets = (
588
+ /** @type {Array<Record<string, any>>} */
589
+ project.runTargets ?? []
590
+ );
591
+ if (targets.length === 0) return h("div", { className: "mcdev-hint", style: { marginTop: 6 } }, "\u6CA1\u6709\u627E\u5230\u53EF\u8FD0\u884C\u76EE\u6807");
592
+ const chosen = selectedTarget(project, selection);
593
+ const groups = groupByModule(targets);
594
+ return h(
595
+ "div",
596
+ { className: "mcdev-options" },
597
+ groups.map(
598
+ (group) => h(
599
+ "div",
600
+ { key: group.module, className: "mcdev-option-group" },
601
+ groups.length > 1 ? h("div", { className: "mcdev-option-module" }, group.module) : void 0,
602
+ group.targets.map(
603
+ (target) => h(
604
+ "label",
605
+ { key: targetKey(target), className: `mcdev-option${locked ? " is-disabled" : ""}`, title: target.taskPath },
606
+ h("input", {
607
+ type: "radio",
608
+ className: "mcdev-radio",
609
+ name: `mcdev-${project.root}`,
610
+ checked: chosen !== void 0 && targetKey(chosen) === targetKey(target),
611
+ disabled: locked,
612
+ onChange: () => selectTarget(project.root, targetKey(target))
613
+ }),
614
+ h("span", { className: "mcdev-option-label" }, target.label),
615
+ target.guessed === true ? h("span", { className: "mcdev-badge", "data-guessed": "true", title: "\u6309 loader \u60EF\u4F8B\u63A8\u6D4B\uFF0C\u5C1A\u672A\u9A8C\u8BC1" }, "\u672A\u9A8C\u8BC1") : void 0
616
+ )
617
+ )
618
+ )
619
+ )
620
+ );
621
+ }
622
+ function elapsed(startedAt) {
623
+ const started = Date.parse(startedAt);
624
+ if (Number.isNaN(started)) return "\u2014";
625
+ const seconds = Math.max(0, Math.round((Date.now() - started) / 1e3));
626
+ if (seconds < 60) return `${seconds}s`;
627
+ const minutes = Math.floor(seconds / 60);
628
+ return `${minutes}m${String(seconds % 60).padStart(2, "0")}s`;
629
+ }
630
+
631
+ // src/client/Capsule.js
632
+ function Capsule(props) {
633
+ if (props?.useSessions === void 0) return h(CapsuleBody, { cwd: void 0 });
634
+ return h(SessionCapsule, props);
635
+ }
636
+ function SessionCapsule(props) {
637
+ const cwd = props.useSessions((state) => state.byId?.[props.sessionId]?.cwd);
638
+ return h(CapsuleBody, { cwd: typeof cwd === "string" && cwd !== "" ? cwd : void 0 });
639
+ }
640
+ function CapsuleBody({ cwd }) {
641
+ const state = useStoreState();
642
+ const launches = useLaunches(5e3);
643
+ const anchor = (0, import_react3.useRef)(
644
+ /** @type {HTMLDivElement | null} */
645
+ null
646
+ );
647
+ const lastRoot = (0, import_react3.useRef)(
648
+ /** @type {string | undefined} */
649
+ void 0
650
+ );
651
+ (0, import_react3.useEffect)(() => {
652
+ report("capsule.mounted");
653
+ void loadStateInfo();
654
+ }, []);
655
+ (0, import_react3.useEffect)(() => {
656
+ if (cwd === void 0) {
657
+ if (lastRoot.current === void 0) void loadScan();
658
+ return;
659
+ }
660
+ if (lastRoot.current === cwd) return;
661
+ lastRoot.current = cwd;
662
+ store.set({ workspaceRoot: cwd, scan: void 0, selectedRoot: void 0, error: void 0 });
663
+ report("workspace.changed", { root: cwd });
664
+ void loadScan({ root: cwd, refresh: true });
665
+ }, [cwd]);
666
+ (0, import_react3.useEffect)(() => {
667
+ if (!state.flyoutOpen) return void 0;
668
+ const onPointerDown = (event) => {
669
+ if (anchor.current !== null && anchor.current.contains(
670
+ /** @type {Node} */
671
+ event.target
672
+ )) return;
673
+ store.set({ flyoutOpen: false });
674
+ };
675
+ document.addEventListener("pointerdown", onPointerDown);
676
+ return () => document.removeEventListener("pointerdown", onPointerDown);
677
+ }, [state.flyoutOpen]);
678
+ const project = selectedProject();
679
+ const target = selectedTarget(project, state.selection);
680
+ const running = project === void 0 ? void 0 : activeLaunchFor(project.root);
681
+ const busy = launches.busy !== void 0;
682
+ const multipleProjects = (state.scan?.projects?.length ?? 0) > 1;
683
+ const tone = state.error !== void 0 || launches.error !== void 0 ? "error" : running === void 0 ? project === void 0 ? "idle" : "ok" : running.status === "starting" ? "warn" : "ok";
684
+ const parts = capsuleParts(project, target, { multipleProjects });
685
+ const runningLabel = running === void 0 ? void 0 : running.label;
686
+ const title = launches.error ?? state.error ?? (project === void 0 ? `\u6CA1\u6709\u8BC6\u522B\u5230 MC \u5DE5\u7A0B
687
+ ${state.workspaceRoot ?? ""}` : `${project.modName ?? project.name}
688
+ ${project.root}
689
+ ${target === void 0 ? "\u6CA1\u6709\u53EF\u8FD0\u884C\u76EE\u6807" : target.taskPath}`);
690
+ return h(
691
+ "div",
692
+ { className: "mcdev-anchor", ref: anchor },
693
+ h(
694
+ "div",
695
+ { className: "mcdev-capsule" },
696
+ h(
697
+ "button",
698
+ { type: "button", className: "mcdev-pill", title, onClick: () => store.set({ flyoutOpen: !state.flyoutOpen }) },
699
+ h("span", { className: "mcdev-dot", "data-state": tone, "data-pulse": running === void 0 ? "false" : "true" }),
700
+ h(
701
+ "span",
702
+ { className: "mcdev-pill-text" },
703
+ runningLabel !== void 0 || parts.project === void 0 ? void 0 : [
704
+ h("span", { key: "project", className: "mcdev-pill-project", title: parts.project }, parts.project),
705
+ h("span", { key: "sep", className: "mcdev-pill-sep" }, "\xB7")
706
+ ],
707
+ h("span", { key: "target", className: "mcdev-pill-target" }, runningLabel ?? parts.target)
708
+ )
709
+ ),
710
+ running === void 0 ? h(
711
+ "button",
712
+ {
713
+ type: "button",
714
+ className: "mcdev-run mcdev-run-icon",
715
+ disabled: target === void 0 || busy,
716
+ title: target === void 0 ? "\u6CA1\u6709\u53EF\u8FD0\u884C\u76EE\u6807" : `\u542F\u52A8 ${target.taskPath}`,
717
+ onClick: () => {
718
+ if (project !== void 0 && target !== void 0) void startTarget(project, target);
719
+ }
720
+ },
721
+ h("span", { className: "mcdev-icon" }, h(IconPlay, void 0))
722
+ ) : h(
723
+ "button",
724
+ {
725
+ type: "button",
726
+ className: "mcdev-run mcdev-run-icon",
727
+ "data-tone": "stop",
728
+ disabled: busy,
729
+ title: `\u505C\u6B62 ${running.taskPath}`,
730
+ onClick: () => void stopLaunch(running.id)
731
+ },
732
+ h("span", { className: "mcdev-icon" }, h(IconStop, void 0))
733
+ )
734
+ ),
735
+ state.flyoutOpen ? h(Flyout) : void 0
736
+ );
737
+ }
738
+
739
+ // src/client/DetailTab.js
740
+ var import_react4 = require("react");
741
+ function DetailTab() {
742
+ const state = useStoreState();
743
+ const launches = useLaunches(1500);
744
+ (0, import_react4.useEffect)(() => {
745
+ if (state.scan === void 0) loadScan();
746
+ }, [state.scan]);
747
+ const project = selectedProject();
748
+ const chosen = selectedTarget(project, state.selection);
749
+ const running = project === void 0 ? void 0 : launches.list.find(
750
+ (entry) => String(entry.root).toLowerCase() === String(project.root).toLowerCase() && (entry.status === "starting" || entry.status === "running")
751
+ );
752
+ if (project === void 0) {
753
+ return h(
754
+ "div",
755
+ { className: "mcdev-detail" },
756
+ h("div", { className: "mcdev-title" }, PANEL_TITLE),
757
+ state.loading ? h(Skeleton, { rows: 4, hint: "\u6B63\u5728\u626B\u63CF\u5DE5\u4F5C\u533A\u2026" }) : h("div", { className: "mcdev-muted", style: { marginTop: 8 } }, "\u5F53\u524D\u5DE5\u4F5C\u533A\u6CA1\u6709\u8BC6\u522B\u5230\u6A21\u7EC4\u5DE5\u7A0B"),
758
+ state.error === void 0 ? void 0 : h("div", { className: "mcdev-error", style: { marginTop: 8 } }, state.error)
759
+ );
760
+ }
761
+ const java = (
762
+ /** @type {Record<string, any>} */
763
+ project.java ?? {}
764
+ );
765
+ const modules = (
766
+ /** @type {Array<Record<string, any>>} */
767
+ project.modules ?? []
768
+ );
769
+ const evidence = (
770
+ /** @type {Array<Record<string, any>>} */
771
+ project.evidence ?? []
772
+ );
773
+ const warnings = (
774
+ /** @type {string[]} */
775
+ project.warnings ?? []
776
+ );
777
+ const conflicts = (
778
+ /** @type {string[]} */
779
+ project.conflicts ?? []
780
+ );
781
+ return h(
782
+ "div",
783
+ { className: "mcdev-detail" },
784
+ h("div", { className: "mcdev-title" }, projectTitle(project)),
785
+ h("div", { className: "mcdev-sub" }, projectSubtitle(project)),
786
+ h("div", { className: "mcdev-muted mcdev-code" }, project.root),
787
+ h(
788
+ "div",
789
+ { className: "mcdev-muted", style: { marginTop: 2 } },
790
+ `${kindLabel(project.kind)} \xB7 \u7F6E\u4FE1\u5EA6 ${Math.round(Number(project.confidence ?? 0) * 100)}%`
791
+ ),
792
+ section("\u57FA\u672C\u4FE1\u606F", [
793
+ row("loader", loaderLabel(project.loader)),
794
+ row("MC \u7248\u672C", text(project.minecraftVersion)),
795
+ row("loader \u7248\u672C", text(project.loaderVersion)),
796
+ row("mod id", text(project.modId)),
797
+ row("mod \u7248\u672C", text(project.modVersion))
798
+ ]),
799
+ section("\u6784\u5EFA\u73AF\u5883", [
800
+ row("Gradle", `${text(project.gradleVersion)}${project.wrapper === true ? "\uFF08wrapper\uFF09" : "\uFF08\u65E0 wrapper\uFF09"}`),
801
+ row("JDK \u9700\u6C42", text(java.required)),
802
+ row("JDK \u9009\u4E2D", `${text(java.selected)}${java.exact === true ? "" : "\uFF08\u4E0D\u5B8C\u5168\u5339\u914D\uFF09"}`),
803
+ row("\u672C\u673A JDK", java.available?.length > 0 ? java.available.map((jdk) => `${jdk.major}`).join(" / ") : "\u672A\u53D1\u73B0")
804
+ ]),
805
+ section("\u8FD0\u884C", [
806
+ ...running === void 0 ? [
807
+ row("\u76EE\u6807", capsuleText(project, chosen)),
808
+ h(
809
+ "div",
810
+ { className: "mcdev-actions", style: { marginTop: 6 } },
811
+ h(
812
+ "button",
813
+ {
814
+ type: "button",
815
+ className: "mcdev-run",
816
+ disabled: chosen === void 0 || launches.busy !== void 0,
817
+ title: chosen?.taskPath ?? "",
818
+ onClick: () => {
819
+ if (chosen !== void 0) void startTarget(project, chosen);
820
+ }
821
+ },
822
+ "\u25B6 \u542F\u52A8"
823
+ ),
824
+ h("span", { className: "mcdev-hint" }, "\u5728\u6807\u9898\u680F\u80F6\u56CA\u7684\u6D6E\u5C42\u91CC\u5207\u6362\u76EE\u6807")
825
+ )
826
+ ] : [
827
+ row("\u5B9E\u4F8B", `${running.id} \xB7 ${running.taskPath}`),
828
+ row("\u72B6\u6001", `${running.status === "starting" ? "\u542F\u52A8\u4E2D" : "\u8FD0\u884C\u4E2D"}${running.readyTimedOut === true ? "\uFF08\u8FD8\u6CA1\u770B\u5230\u542F\u52A8\u4FE1\u53F7\uFF0C\u53EF\u80FD\u4ECD\u5728\u6784\u5EFA\uFF09" : ""}`),
829
+ row("PID", text(running.pid)),
830
+ h(
831
+ "div",
832
+ { className: "mcdev-actions", style: { marginTop: 6 } },
833
+ h(
834
+ "button",
835
+ { type: "button", className: "mcdev-run", "data-tone": "stop", disabled: launches.busy === running.id, onClick: () => stopLaunch(running.id) },
836
+ "\u23F9 \u505C\u6B62"
837
+ ),
838
+ h(
839
+ "button",
840
+ { type: "button", className: "mcdev-run", onClick: () => openLogTab() },
841
+ "\u6253\u5F00\u65E5\u5FD7"
842
+ )
843
+ )
844
+ ]
845
+ ]),
846
+ section("\u6A21\u5757\u4E0E\u8FD0\u884C\u76EE\u6807", [
847
+ ...modules.length === 0 ? [h("div", { className: "mcdev-muted" }, "\u6CA1\u6709\u6A21\u5757\u4FE1\u606F")] : [
848
+ h(
849
+ "table",
850
+ { className: "mcdev-table" },
851
+ h("thead", void 0, h("tr", void 0, h("th", void 0, "\u6A21\u5757"), h("th", void 0, "\u5E73\u53F0"), h("th", void 0, "\u4EFB\u52A1"))),
852
+ h(
853
+ "tbody",
854
+ void 0,
855
+ modules.map(
856
+ (module2) => h(
857
+ "tr",
858
+ { key: module2.path },
859
+ h("td", void 0, module2.path),
860
+ h("td", void 0, module2.platform),
861
+ h(
862
+ "td",
863
+ { className: "mcdev-code" },
864
+ module2.targets?.length > 0 ? module2.targets.map((target) => `${target.taskPath}${target.guessed === true ? " ?" : ""}`).join(" ") : "\u2014"
865
+ )
866
+ )
867
+ )
868
+ )
869
+ )
870
+ ],
871
+ h("div", { className: "mcdev-hint", style: { marginTop: 6 } }, "\u5E26 ? \u7684\u4EFB\u52A1\u662F\u6309 loader \u60EF\u4F8B\u63A8\u6D4B\u7684\uFF0C\u672A\u7ECF\u9A8C\u8BC1\uFF1B\u542F\u52A8\u80FD\u529B\u5728 M2 \u63A5\u5165\u3002")
872
+ ]),
873
+ section(
874
+ "\u8BC1\u636E",
875
+ evidence.length === 0 ? [h("div", { className: "mcdev-muted" }, "\u6CA1\u6709\u8BC1\u636E")] : [
876
+ h(
877
+ "div",
878
+ { className: "mcdev-stack" },
879
+ evidence.map(
880
+ (entry, index) => h(
881
+ "div",
882
+ { key: `${entry.file}:${index}` },
883
+ h("span", { className: "mcdev-code mcdev-sub" }, entry.file),
884
+ h("span", { className: "mcdev-muted" }, ` \u2014 ${entry.detail}`)
885
+ )
886
+ )
887
+ )
888
+ ]
889
+ ),
890
+ warnings.length === 0 && conflicts.length === 0 ? void 0 : section("\u63D0\u793A", [
891
+ ...warnings.map(
892
+ (warning, index) => h(
893
+ "div",
894
+ { key: `w${index}`, className: "mcdev-warn mcdev-icon-text" },
895
+ h("span", { className: "mcdev-icon" }, h(IconWarning, void 0)),
896
+ warning
897
+ )
898
+ ),
899
+ ...conflicts.map((conflict, index) => h("div", { key: `c${index}`, className: "mcdev-error" }, `\xB7 ${conflict}`))
900
+ ]),
901
+ h(
902
+ "div",
903
+ { style: { marginTop: 14 } },
904
+ h(
905
+ "button",
906
+ { type: "button", className: "mcdev-mini", onClick: () => loadScan({ refresh: true }) },
907
+ h("span", { className: "mcdev-icon" }, h(IconRefresh, { size: 12 })),
908
+ state.loading ? "\u91CD\u65B0\u626B\u63CF\u4E2D\u2026" : "\u91CD\u65B0\u626B\u63CF"
909
+ )
910
+ ),
911
+ state.scan?.scannedAt === void 0 ? void 0 : h("div", { className: "mcdev-muted mcdev-hint", style: { marginTop: 4 } }, `\u626B\u63CF\u4E8E ${state.scan.scannedAt}`)
912
+ );
913
+ }
914
+ function section(title, children) {
915
+ return h("div", { className: "mcdev-section" }, h("h4", void 0, title), ...children);
916
+ }
917
+ function row(key, value) {
918
+ return h("div", { className: "mcdev-row", key }, h("span", { className: "mcdev-key" }, key), h("span", { className: "mcdev-code" }, value));
919
+ }
920
+
921
+ // src/client/LogTab.js
922
+ var import_react5 = require("react");
923
+ var lastLogId;
924
+ function LogTab() {
925
+ const launches = useLaunches(1500);
926
+ const project = selectedProject();
927
+ const [logId, setLogId] = (0, import_react5.useState)(
928
+ /** @type {string | undefined} */
929
+ lastLogId
930
+ );
931
+ const [log, setLog] = (0, import_react5.useState)(
932
+ /** @type {any} */
933
+ void 0
934
+ );
935
+ const [follow, setFollow] = (0, import_react5.useState)(true);
936
+ const [pinned, setPinned] = (0, import_react5.useState)(false);
937
+ const body = (0, import_react5.useRef)(
938
+ /** @type {HTMLPreElement | null} */
939
+ null
940
+ );
941
+ const projectRoot = project === void 0 ? void 0 : String(project.root).toLowerCase();
942
+ const scoped = launches.list.filter((entry) => projectRoot === void 0 || String(entry.root).toLowerCase() === projectRoot);
943
+ const history = scoped.length > 0 ? scoped : launches.list;
944
+ const target = pickLogTarget(history, logId, pinned);
945
+ (0, import_react5.useEffect)(() => {
946
+ if (target === void 0 || target === logId) return;
947
+ lastLogId = target;
948
+ setLogId(target);
949
+ setLog(void 0);
950
+ if (history.some((entry) => entry.id === target && (entry.status === "starting" || entry.status === "running"))) {
951
+ setPinned(false);
952
+ }
953
+ }, [target, logId, history]);
954
+ (0, import_react5.useEffect)(() => {
955
+ if (logId === void 0) {
956
+ setLog(void 0);
957
+ return void 0;
958
+ }
959
+ let cancelled = false;
960
+ const pull = async () => {
961
+ try {
962
+ const result = await call("launch.logs", { id: logId, offset: 0, limit: 800 });
963
+ if (!cancelled) setLog(result);
964
+ } catch (error) {
965
+ if (!cancelled) setLog({ error: error instanceof Error ? error.message : String(error) });
966
+ }
967
+ };
968
+ void pull();
969
+ const timer = setInterval(() => {
970
+ if (log?.status === "starting" || log?.status === "running" || log === void 0) void pull();
971
+ }, 1500);
972
+ return () => {
973
+ cancelled = true;
974
+ clearInterval(timer);
975
+ };
976
+ }, [logId, log?.status]);
977
+ (0, import_react5.useEffect)(() => {
978
+ if (!follow) return;
979
+ const element = body.current;
980
+ if (element !== null) element.scrollTop = element.scrollHeight;
981
+ }, [log?.lines?.length, follow]);
982
+ if (history.length === 0) {
983
+ return h(
984
+ "div",
985
+ { className: "mcdev-detail" },
986
+ h("div", { className: "mcdev-title" }, "\u8FD0\u884C\u65E5\u5FD7"),
987
+ h("div", { className: "mcdev-muted", style: { marginTop: 8 } }, "\u8FD8\u6CA1\u6709\u8FD0\u884C\u8BB0\u5F55\u3002\u7528\u6807\u9898\u680F\u80F6\u56CA\u7684 \u25B6 \u542F\u52A8\u4E00\u6B21\u3002")
988
+ );
989
+ }
990
+ const running = log?.status === "starting" || log?.status === "running";
991
+ const tone = log?.error !== void 0 ? "error" : running ? log?.status === "starting" ? "warn" : "ok" : log?.outcome === "normal" ? "idle" : "error";
992
+ return h(
993
+ "div",
994
+ { className: "mcdev-logtab" },
995
+ h(
996
+ "div",
997
+ { className: "mcdev-log-head" },
998
+ h(
999
+ "select",
1000
+ {
1001
+ className: "mcdev-select",
1002
+ value: logId ?? "",
1003
+ onChange: (event) => {
1004
+ setPinned(true);
1005
+ lastLogId = event.target.value;
1006
+ setLogId(event.target.value);
1007
+ setLog(void 0);
1008
+ }
1009
+ },
1010
+ history.map((entry) => h("option", { key: entry.id, value: entry.id }, optionLabel(entry)))
1011
+ ),
1012
+ h("span", { className: "mcdev-dot", "data-state": tone }),
1013
+ h("span", { className: "mcdev-hint" }, statusText(log, running)),
1014
+ h(
1015
+ "label",
1016
+ { className: "mcdev-follow" },
1017
+ h("input", { type: "checkbox", checked: follow, onChange: (event) => setFollow(event.target.checked) }),
1018
+ "\u8DDF\u968F"
1019
+ )
1020
+ ),
1021
+ log?.diagnosis === void 0 ? void 0 : h(
1022
+ "div",
1023
+ { className: "mcdev-diagnosis" },
1024
+ h(
1025
+ "div",
1026
+ { className: "mcdev-diagnosis-hint mcdev-icon-text" },
1027
+ h("span", { className: "mcdev-icon" }, h(IconWarning, void 0)),
1028
+ log.diagnosis.hint
1029
+ ),
1030
+ h("div", { className: "mcdev-hint mcdev-code" }, log.diagnosis.line)
1031
+ ),
1032
+ log?.error === void 0 ? log === void 0 && running ? h(Skeleton, { rows: 4, hint: "\u6B63\u5728\u8BFB\u53D6\u65E5\u5FD7\u2026" }) : h("pre", { ref: body, className: "mcdev-logs mcdev-logs-full" }, (log?.lines ?? []).join("\n") || "\uFF08\u8FD8\u6CA1\u6709\u8F93\u51FA\uFF09") : h("div", { className: "mcdev-error", style: { marginTop: 8 } }, log.error),
1033
+ h(
1034
+ "div",
1035
+ { style: { marginTop: 8 } },
1036
+ h(
1037
+ "button",
1038
+ { type: "button", className: "mcdev-mini", onClick: () => void loadLaunches() },
1039
+ h("span", { className: "mcdev-icon" }, h(IconRefresh, { size: 12 })),
1040
+ "\u5237\u65B0\u5B9E\u4F8B\u5217\u8868"
1041
+ )
1042
+ )
1043
+ );
1044
+ }
1045
+ function optionLabel(entry) {
1046
+ const time = new Date(entry.startedAt);
1047
+ const clock = Number.isNaN(time.getTime()) ? "--:--" : `${String(time.getHours()).padStart(2, "0")}:${String(time.getMinutes()).padStart(2, "0")}`;
1048
+ const mark = entry.status === "running" || entry.status === "starting" ? "\u25CF" : entry.outcome === "normal" ? "\u25CB" : "\u2715";
1049
+ return `${mark} ${entry.label} \xB7 ${clock} \xB7 ${launchStatusText(entry)}`;
1050
+ }
1051
+ function statusText(log, running) {
1052
+ if (log === void 0) return "\u52A0\u8F7D\u4E2D\u2026";
1053
+ void running;
1054
+ return launchStatusText({ status: log.status, outcome: log.outcome, exitCode: log.exitCode });
1055
+ }
1056
+
1057
+ // src/client/styles.js
1058
+ var STYLE_ID = "mcdev-styles";
1059
+ var CSS = `
1060
+ .mcdev-anchor { position: relative; display: inline-flex; }
1061
+ .mcdev-capsule { display: inline-flex; align-items: center; gap: 4px; }
1062
+
1063
+ /* \u6807\u9898\u680F\u80F6\u56CA\uFF1A\u4E0E\u7CFB\u7EDF\u63A7\u4EF6\u540C\u9AD8\u540C\u5706\u89D2\uFF0C\u9ED8\u8BA4\u65E0\u5E95\u8272 */
1064
+ .mcdev-pill {
1065
+ display: inline-flex;
1066
+ align-items: center;
1067
+ gap: 6px;
1068
+ height: 24px;
1069
+ max-width: 220px;
1070
+ padding: 0 8px;
1071
+ border: 1px solid var(--dsw-alias-border-l2);
1072
+ border-radius: 999px;
1073
+ background: transparent;
1074
+ color: var(--dsw-alias-label-primary);
1075
+ font: var(--dsw-font-xxs-12);
1076
+ cursor: pointer;
1077
+ }
1078
+ .mcdev-pill:hover { background: var(--dsw-alias-interactive-bg-hover); }
1079
+ /* \u7A84\u7684\u65F6\u5019\u8BA9\u5DE5\u7A0B\u540D\u8BA9\u6B65\uFF1A\u76EE\u6807\u540D\u6C38\u8FDC\u5B8C\u6574\u663E\u793A */
1080
+ .mcdev-pill-text { display: flex; align-items: center; min-width: 0; }
1081
+ .mcdev-pill-project {
1082
+ min-width: 0;
1083
+ overflow: hidden;
1084
+ text-overflow: ellipsis;
1085
+ white-space: nowrap;
1086
+ color: var(--dsw-alias-label-secondary);
1087
+ }
1088
+ .mcdev-pill-sep { flex: none; padding: 0 4px; color: var(--dsw-alias-label-tertiary); }
1089
+ .mcdev-pill-target { flex: none; white-space: nowrap; }
1090
+
1091
+ .mcdev-dot {
1092
+ width: 7px;
1093
+ height: 7px;
1094
+ flex: none;
1095
+ border-radius: 50%;
1096
+ background: var(--dsw-alias-label-tertiary);
1097
+ }
1098
+ .mcdev-dot[data-state="ok"] { background: var(--dsw-alias-state-success-primary); }
1099
+ .mcdev-dot[data-state="warn"] { background: var(--dsw-alias-state-warn-primary); }
1100
+ .mcdev-dot[data-state="error"] { background: var(--dsw-alias-state-error-primary); }
1101
+
1102
+ /* \u6D6E\u5C42\uFF1A\u4E09\u5C42\u7ED3\u6784\uFF08\u8868\u5934 / \u5217\u8868 / \u9009\u62E9\u533A\uFF09\uFF0C\u9009\u5DE5\u7A0B\u65F6\u5217\u8868\u9AD8\u5EA6\u4E0D\u53D8 */
1103
+ .mcdev-flyout {
1104
+ position: absolute;
1105
+ top: 30px;
1106
+ right: 0;
1107
+ z-index: 60;
1108
+ display: flex;
1109
+ flex-direction: column;
1110
+ width: 340px;
1111
+ max-height: 60vh;
1112
+ padding: 0;
1113
+ border: 1px solid var(--dsw-alias-border-l2);
1114
+ border-radius: 8px;
1115
+ background: var(--dsw-alias-bg-layer-2);
1116
+ color: var(--dsw-alias-label-primary);
1117
+ font: var(--dsw-font-xxs-12);
1118
+ box-shadow: 0 8px 24px var(--dsw-alias-bg-mask-1);
1119
+ overflow: hidden;
1120
+ }
1121
+ .mcdev-flyout-head {
1122
+ display: flex;
1123
+ flex-direction: column;
1124
+ gap: 4px;
1125
+ flex: none;
1126
+ padding: 10px 10px 8px;
1127
+ border-bottom: 1px solid var(--dsw-alias-hairline);
1128
+ }
1129
+ .mcdev-flyout-list {
1130
+ flex: 1 1 auto;
1131
+ min-height: 0;
1132
+ overflow: auto;
1133
+ padding: 6px;
1134
+ }
1135
+ .mcdev-flyout-pane {
1136
+ display: flex;
1137
+ flex-direction: column;
1138
+ gap: 4px;
1139
+ flex: none;
1140
+ padding: 8px 10px 10px;
1141
+ border-top: 1px solid var(--dsw-alias-hairline);
1142
+ }
1143
+
1144
+ /* \u5217\u8868\u884C\uFF1A\u4E00\u884C\u4E00\u4E2A\u5DE5\u7A0B\uFF0C\u9AD8\u5EA6\u6052\u5B9A */
1145
+ .mcdev-list-row {
1146
+ display: flex;
1147
+ align-items: center;
1148
+ justify-content: space-between;
1149
+ gap: 8px;
1150
+ width: 100%;
1151
+ height: 30px;
1152
+ padding: 0 8px;
1153
+ border: 0;
1154
+ border-radius: 4px;
1155
+ background: transparent;
1156
+ color: var(--dsw-alias-label-primary);
1157
+ font: var(--dsw-font-xxs-12);
1158
+ text-align: left;
1159
+ cursor: pointer;
1160
+ }
1161
+ .mcdev-list-row:hover { background: var(--dsw-alias-interactive-bg-hover); }
1162
+ .mcdev-list-row[data-current="true"] { background: var(--dsw-alias-interactive-bg-hover); }
1163
+ .mcdev-list-row[data-current="true"] .mcdev-list-name { font: var(--dsw-font-xxs-strong-12); }
1164
+ .mcdev-list-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1165
+ .mcdev-list-meta { flex: none; color: var(--dsw-alias-label-tertiary); font: var(--dsw-font-xxxs-11); }
1166
+ .mcdev-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1167
+
1168
+ .mcdev-title { font: var(--dsw-font-xxs-strong-12); }
1169
+ .mcdev-sub { color: var(--dsw-alias-label-secondary); }
1170
+ .mcdev-muted { color: var(--dsw-alias-label-tertiary); }
1171
+ .mcdev-code { font-family: var(--dsw-font-mono); font-size: 11px; }
1172
+ .mcdev-warn { color: var(--dsw-alias-state-warn-primary); }
1173
+ .mcdev-error { color: var(--dsw-alias-state-error-primary); }
1174
+
1175
+ .mcdev-row { display: flex; align-items: baseline; gap: 8px; }
1176
+ .mcdev-key { flex: none; width: 78px; color: var(--dsw-alias-label-tertiary); }
1177
+
1178
+ .mcdev-mini {
1179
+ display: inline-flex;
1180
+ align-items: center;
1181
+ gap: 3px;
1182
+ height: 22px;
1183
+ padding: 0 8px;
1184
+ border: 1px solid var(--dsw-alias-border-l2);
1185
+ border-radius: 4px;
1186
+ background: transparent;
1187
+ color: var(--dsw-alias-label-secondary);
1188
+ font: var(--dsw-font-xxxs-11);
1189
+ cursor: pointer;
1190
+ }
1191
+ .mcdev-mini:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
1192
+ .mcdev-mini-icon { width: 22px; padding: 0; justify-content: center; }
1193
+
1194
+ /* \u76EE\u6807\u6807\u7B7E\uFF1A\u4E2D\u6027\u5FBD\u6807\uFF0C\u4E0D\u662F\u6309\u94AE\u2014\u2014M2 \u4E4B\u524D\u6CA1\u6709\u53EF\u70B9\u7684\u52A8\u4F5C */
1195
+ .mcdev-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
1196
+ .mcdev-badge {
1197
+ display: inline-flex;
1198
+ align-items: center;
1199
+ gap: 4px;
1200
+ height: 20px;
1201
+ padding: 0 6px;
1202
+ border: 1px solid var(--dsw-alias-border-l1);
1203
+ border-radius: 4px;
1204
+ background: var(--dsw-alias-bg-layer-3);
1205
+ color: var(--dsw-alias-label-secondary);
1206
+ font: var(--dsw-font-xxxs-11);
1207
+ }
1208
+ .mcdev-badge[data-guessed="true"] {
1209
+ border-style: dashed;
1210
+ background: transparent;
1211
+ color: var(--dsw-alias-label-tertiary);
1212
+ }
1213
+ .mcdev-badge-tag { color: var(--dsw-alias-label-tertiary); }
1214
+
1215
+ .mcdev-hint { color: var(--dsw-alias-label-tertiary); font: var(--dsw-font-xxxs-11); }
1216
+
1217
+ /* \u8FD0\u884C\u6309\u94AE\uFF1A\u7CFB\u7EDF\u5C3A\u5BF8\uFF0824 \u9AD8\u30014 \u5706\u89D2\uFF09\uFF0C\u4E2D\u6027\u5E95\u8272 */
1218
+ .mcdev-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
1219
+ .mcdev-run {
1220
+ display: inline-flex;
1221
+ align-items: center;
1222
+ gap: 4px;
1223
+ height: 24px;
1224
+ padding: 0 8px;
1225
+ border: 1px solid var(--dsw-alias-border-l2);
1226
+ border-radius: 4px;
1227
+ background: transparent;
1228
+ color: var(--dsw-alias-label-primary);
1229
+ font: var(--dsw-font-xxs-12);
1230
+ cursor: pointer;
1231
+ }
1232
+ .mcdev-run:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
1233
+ .mcdev-run:disabled { opacity: 0.4; cursor: default; }
1234
+ .mcdev-run[data-tone="stop"] { color: var(--dsw-alias-danger); border-color: var(--dsw-alias-danger); }
1235
+ .mcdev-run-icon { width: 24px; padding: 0; justify-content: center; }
1236
+
1237
+ /* \u76EE\u6807\u5355\u9009\u5217\u8868\uFF1A\u4E00\u884C\u4E00\u4E2A\uFF0C\u6A21\u5757\u540D\u505A\u5C0F\u6807\u9898 */
1238
+ .mcdev-options { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; }
1239
+ .mcdev-option-group { display: flex; flex-direction: column; gap: 2px; }
1240
+ .mcdev-option-module { margin-top: 4px; color: var(--dsw-alias-label-tertiary); font: var(--dsw-font-xxxs-11); }
1241
+ .mcdev-option {
1242
+ display: flex;
1243
+ align-items: center;
1244
+ gap: 6px;
1245
+ height: 22px;
1246
+ padding: 0 4px;
1247
+ border-radius: 4px;
1248
+ cursor: pointer;
1249
+ }
1250
+ .mcdev-option:hover { background: var(--dsw-alias-interactive-bg-hover); }
1251
+ .mcdev-option.is-disabled { opacity: 0.5; cursor: default; }
1252
+ .mcdev-radio { margin: 0; accent-color: var(--dsw-alias-accent); }
1253
+ .mcdev-option-label { font: var(--dsw-font-xxs-12); }
1254
+
1255
+ .mcdev-status { margin-top: 6px; color: var(--dsw-alias-state-success-primary); font: var(--dsw-font-xxxs-11); }
1256
+ .mcdev-inline-status {
1257
+ display: inline-flex;
1258
+ align-items: center;
1259
+ gap: 4px;
1260
+ margin-left: 8px;
1261
+ color: var(--dsw-alias-state-success-primary);
1262
+ font: var(--dsw-font-xxxs-11);
1263
+ }
1264
+
1265
+ /* \u65E5\u5FD7\u5C3E\u90E8\uFF1A\u7B49\u5BBD\u3001\u53EF\u6EDA\uFF0C\u5E95\u8272\u7528\u7CFB\u7EDF\u5C42\u7EA7 3 */
1266
+ .mcdev-logs {
1267
+ max-height: 220px;
1268
+ margin: 8px 0 0;
1269
+ padding: 8px;
1270
+ overflow: auto;
1271
+ border: 1px solid var(--dsw-alias-border-l1);
1272
+ border-radius: 4px;
1273
+ background: var(--dsw-alias-bg-layer-3);
1274
+ color: var(--dsw-alias-label-secondary);
1275
+ font-family: var(--dsw-font-mono);
1276
+ font-size: 11px;
1277
+ line-height: 1.5;
1278
+ white-space: pre-wrap;
1279
+ }
1280
+
1281
+ /* \u8FD0\u884C\u65E5\u5FD7\u6807\u7B7E\uFF1A\u9876\u90E8\u4E00\u6761\u63A7\u5236\u5E26\uFF0C\u4E0B\u9762\u662F\u6574\u5757\u65E5\u5FD7 */
1282
+ .mcdev-logtab { display: flex; flex-direction: column; height: 100%; min-height: 0; padding: 8px 10px 10px; }
1283
+ .mcdev-log-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding-bottom: 8px; }
1284
+ .mcdev-select {
1285
+ height: 24px;
1286
+ max-width: 220px;
1287
+ padding: 0 6px;
1288
+ border: 1px solid var(--dsw-alias-border-l2);
1289
+ border-radius: 4px;
1290
+ background: var(--dsw-alias-bg-layer-2);
1291
+ color: var(--dsw-alias-label-primary);
1292
+ font: var(--dsw-font-xxxs-11);
1293
+ }
1294
+ .mcdev-follow { display: inline-flex; align-items: center; gap: 4px; color: var(--dsw-alias-label-tertiary); font: var(--dsw-font-xxxs-11); }
1295
+ .mcdev-diagnosis {
1296
+ margin-bottom: 8px;
1297
+ padding: 8px;
1298
+ border: 1px solid var(--dsw-alias-state-error-primary);
1299
+ border-radius: 4px;
1300
+ background: var(--dsw-alias-bg-layer-2);
1301
+ }
1302
+ .mcdev-diagnosis-hint { color: var(--dsw-alias-state-error-primary); font: var(--dsw-font-xxs-strong-12); }
1303
+ .mcdev-logs-full { flex: 1 1 auto; min-height: 0; max-height: none; margin: 0; }
1304
+
1305
+ .mcdev-section { margin-top: 12px; }
1306
+ .mcdev-section > h4 {
1307
+ margin: 0 0 4px;
1308
+ color: var(--dsw-alias-label-tertiary);
1309
+ font: var(--dsw-font-xxxs-strong-11);
1310
+ letter-spacing: 0.04em;
1311
+ text-transform: uppercase;
1312
+ }
1313
+
1314
+ .mcdev-table { width: 100%; border-collapse: collapse; font: var(--dsw-font-xxxs-11); }
1315
+ .mcdev-table th,
1316
+ .mcdev-table td { padding: 3px 6px 3px 0; text-align: left; vertical-align: top; }
1317
+ .mcdev-table thead th {
1318
+ color: var(--dsw-alias-label-tertiary);
1319
+ font: var(--dsw-font-xxxs-strong-11);
1320
+ border-bottom: 1px solid var(--dsw-alias-hairline);
1321
+ }
1322
+
1323
+ .mcdev-detail { padding: 12px; font: var(--dsw-font-xxs-12); color: var(--dsw-alias-label-primary); }
1324
+ .mcdev-stack { display: flex; flex-direction: column; gap: 4px; }
1325
+ .mcdev-icon { display: inline-flex; align-items: center; justify-content: center; flex: none; }
1326
+ .mcdev-icon-text { display: inline-flex; align-items: center; gap: 4px; }
1327
+
1328
+ /* \u52A8\u6548\uFF1A\u77ED\u3001\u514B\u5236\uFF1B\u7CFB\u7EDF\u5F00\u4E86"\u51CF\u5C11\u52A8\u6001\u6548\u679C"\u5C31\u5168\u90E8\u5173\u6389 */
1329
+ @keyframes mcdev-pop-in {
1330
+ from { opacity: 0; transform: translateY(-4px); }
1331
+ to { opacity: 1; transform: none; }
1332
+ }
1333
+ @keyframes mcdev-pulse {
1334
+ 0%, 100% { opacity: 1; }
1335
+ 50% { opacity: 0.45; }
1336
+ }
1337
+ .mcdev-flyout { animation: mcdev-pop-in 120ms ease-out; }
1338
+ .mcdev-dot[data-pulse="true"] { animation: mcdev-pulse 1.6s ease-in-out infinite; }
1339
+ .mcdev-pill,
1340
+ .mcdev-run,
1341
+ .mcdev-mini,
1342
+ .mcdev-option { transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease; }
1343
+
1344
+ /* \u9AA8\u67B6\uFF1A\u626B\u63CF/\u52A0\u8F7D\u4E2D\u7684\u5360\u4F4D\uFF0C\u907F\u514D\u4E00\u7247\u7A7A\u767D */
1345
+ .mcdev-skeleton { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
1346
+ .mcdev-skeleton-row {
1347
+ height: 10px;
1348
+ border-radius: 3px;
1349
+ background: var(--dsw-alias-interactive-bg-hover);
1350
+ animation: mcdev-pulse 1.4s ease-in-out infinite;
1351
+ }
1352
+ .mcdev-skeleton-row:nth-child(2) { width: 72%; }
1353
+ .mcdev-skeleton-row:nth-child(3) { width: 46%; }
1354
+
1355
+ @media (prefers-reduced-motion: reduce) {
1356
+ .mcdev-flyout,
1357
+ .mcdev-dot[data-pulse="true"],
1358
+ .mcdev-skeleton-row { animation: none; }
1359
+ .mcdev-pill,
1360
+ .mcdev-run,
1361
+ .mcdev-mini,
1362
+ .mcdev-option { transition: none; }
1363
+ }
1364
+ `;
1365
+ function ensureStyles() {
1366
+ if (typeof document === "undefined") return;
1367
+ if (document.getElementById(STYLE_ID) !== null) return;
1368
+ const style = document.createElement("style");
1369
+ style.id = STYLE_ID;
1370
+ style.textContent = CSS;
1371
+ document.head.appendChild(style);
1372
+ }
1373
+
1374
+ // src/client/index.js
1375
+ var name = "mcdev-client";
1376
+ var inject = ["slots", "sidebarRight", "sidebarRightTabs"];
1377
+ var DETAIL_ID = "minecraft-dev-starter/detail";
1378
+ var LOG_ID = "minecraft-dev-starter/logs";
1379
+ function apply(ctx) {
1380
+ runtime.ctx = ctx;
1381
+ ensureStyles();
1382
+ report("apply");
1383
+ try {
1384
+ ctx.slots.inject(
1385
+ "conversation.session.header.utilities",
1386
+ () => ctx.slots.register(
1387
+ {
1388
+ name: "conversation.session.header.utilities",
1389
+ id: "mc-dev-starter:run-capsule",
1390
+ order: 15,
1391
+ registrant: "minecraft-dev-starter"
1392
+ },
1393
+ Capsule
1394
+ )
1395
+ );
1396
+ report("capsule.registered");
1397
+ } catch (error) {
1398
+ report("capsule.failed", { message: String(error) });
1399
+ }
1400
+ try {
1401
+ ctx.effect(
1402
+ () => ctx.sidebarRightTabs.register({ id: DETAIL_ID, kind: DETAIL_KIND, title: () => PANEL_TITLE }),
1403
+ "mcdev: detail tab type"
1404
+ );
1405
+ ctx.effect(
1406
+ () => ctx.sidebarRightTabs.register({ id: LOG_ID, kind: LOG_KIND, title: () => "\u8FD0\u884C\u65E5\u5FD7" }),
1407
+ "mcdev: log tab type"
1408
+ );
1409
+ report("tab.type.registered");
1410
+ } catch (error) {
1411
+ report("tab.type.failed", { message: String(error) });
1412
+ }
1413
+ registerBody(DETAIL_ID, DetailTab, "detail");
1414
+ registerBody(LOG_ID, LogTab, "logs");
1415
+ ctx.effect(() => registerBetterSidebarTabs(ctx), "mcdev: better-sidebar tabs");
1416
+ }
1417
+ function registerBetterSidebarTabs(ctx) {
1418
+ const disposers = [];
1419
+ let injectHandle;
1420
+ const register = (sidebar) => {
1421
+ if (sidebar === void 0 || typeof sidebar.registerTab !== "function") return;
1422
+ try {
1423
+ disposers.push(
1424
+ sidebar.registerTab({
1425
+ id: "minecraft-dev-starter:project",
1426
+ title: PANEL_TITLE,
1427
+ icon: (size) => h(IconCube, { size }),
1428
+ component: () => h(DetailTab)
1429
+ })
1430
+ );
1431
+ disposers.push(
1432
+ sidebar.registerTab({
1433
+ id: "minecraft-dev-starter:logs",
1434
+ title: "\u8FD0\u884C\u65E5\u5FD7",
1435
+ icon: (size) => h(IconTerminal, { size }),
1436
+ component: () => h(LogTab)
1437
+ })
1438
+ );
1439
+ report("betterSidebar.registered");
1440
+ } catch (error) {
1441
+ report("betterSidebar.failed", { message: String(error) });
1442
+ }
1443
+ };
1444
+ if (typeof ctx.inject === "function") {
1445
+ try {
1446
+ injectHandle = ctx.inject(["betterSidebar"], (scoped) => {
1447
+ register(scoped.get("betterSidebar", false));
1448
+ });
1449
+ report("betterSidebar.waiting");
1450
+ return () => {
1451
+ try {
1452
+ injectHandle?.();
1453
+ } catch {
1454
+ }
1455
+ for (const dispose of disposers.splice(0)) {
1456
+ try {
1457
+ dispose();
1458
+ } catch {
1459
+ }
1460
+ }
1461
+ };
1462
+ } catch (error) {
1463
+ report("betterSidebar.inject.failed", { message: String(error) });
1464
+ }
1465
+ }
1466
+ let stopped = false;
1467
+ let timer;
1468
+ const startedAt = Date.now();
1469
+ const attempt = () => {
1470
+ if (stopped) return;
1471
+ let sidebar;
1472
+ try {
1473
+ sidebar = typeof ctx.get === "function" ? ctx.get("betterSidebar", false) : void 0;
1474
+ } catch {
1475
+ sidebar = void 0;
1476
+ }
1477
+ if (sidebar === void 0 || typeof sidebar.registerTab !== "function") {
1478
+ if (Date.now() - startedAt > 3e4) {
1479
+ report("betterSidebar.absent");
1480
+ return;
1481
+ }
1482
+ timer = setTimeout(attempt, 700);
1483
+ return;
1484
+ }
1485
+ register(sidebar);
1486
+ };
1487
+ attempt();
1488
+ return () => {
1489
+ stopped = true;
1490
+ if (timer !== void 0) clearTimeout(timer);
1491
+ for (const dispose of disposers.splice(0)) {
1492
+ try {
1493
+ dispose();
1494
+ } catch {
1495
+ }
1496
+ }
1497
+ };
1498
+ }
1499
+ function registerBody(id, component, tag) {
1500
+ const ctx = runtime.ctx;
1501
+ const register = () => ctx.slots.register({ name: "sidebar.right.pane.tab", key: id }, component);
1502
+ try {
1503
+ register();
1504
+ report(`${tag}.body.registered`);
1505
+ } catch {
1506
+ try {
1507
+ ctx.slots.inject("sidebar.right.pane.tab", register);
1508
+ report(`${tag}.body.registered.via-inject`);
1509
+ } catch (error) {
1510
+ report(`${tag}.body.failed`, { message: String(error) });
1511
+ }
1512
+ }
1513
+ }
1514
+
1515
+ return module.exports;
1516
+ } catch (error) {
1517
+ (function(event, error){ try { fetch('/mcdev/api/client.report', { method:'POST', headers:{'content-type':'application/json'}, body: JSON.stringify({ event: event, detail: { message: String((error && error.message) || error), stack: String((error && error.stack) || '').slice(0, 900) } }) }).catch(function(){}); } catch (ignored) {} })('factory.error', error);
1518
+ throw error;
1519
+ }
1520
+ },
1521
+ });