minecodex 2.0.14 → 2.0.16
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/README.md +9 -0
- package/apps/dashboard/app.js +8 -5
- package/apps/dashboard/assets/data-transfer.js +2 -1
- package/apps/dashboard/transport.js +18 -0
- package/assets/brand/minecodex-icon-selected.svg +4 -0
- package/features/markdown-reading/src/adapters/codex/rich-text.mjs +12 -5
- package/features/markdown-reading/src/adapters/codex/table-actions.mjs +13 -2
- package/features/markdown-reading/src/domain/footnotes.mjs +4 -2
- package/features/markdown-reading/src/injection-source.mjs +2 -0
- package/features/markdown-reading/src/reading.css +3 -3
- package/features/notes/assets/icons/prompt-entry-selected.svg +1 -0
- package/features/notes/assets/icons/prompt-entry.svg +16 -0
- package/features/notes/codex-feature.json +3 -2
- package/features/notes/src/adapters/codex/insertion.mjs +92 -0
- package/features/notes/src/adapters/codex/requests.mjs +7 -0
- package/features/notes/src/frontend/entry.mjs +39 -137
- package/features/notes/src/frontend/icons.json +0 -2
- package/features/notes/src/injection-source.mjs +5 -0
- package/package.json +18 -5
- package/packages/cli/src/commands.mjs +10 -0
- package/packages/codex-adapter/src/renderer/environment.mjs +30 -1
- package/packages/plugin/assets/app.js +116 -0
- package/packages/plugin/assets/prompts.js +437 -0
- package/packages/plugin/plugin.json +16 -0
- package/packages/plugin/src/app.mjs +17 -0
- package/packages/plugin/src/control-client.mjs +41 -0
- package/packages/plugin/src/install.mjs +26 -0
- package/packages/plugin/src/navigation-icons.mjs +8 -0
- package/packages/plugin/src/prompts-app.mjs +45 -0
- package/packages/plugin/src/prompts-client.mjs +16 -0
- package/packages/plugin/src/server.mjs +74 -0
- package/packages/plugin/src/ui-resource.mjs +22 -0
- package/packages/runtime-host/src/codex-injection.mjs +5 -12
- package/packages/runtime-host/src/codex-runtime.mjs +3 -16
- package/packages/ui/src/install.mjs +4 -3
- package/packages/ui/src/native/mcp-app.mjs +32 -0
- package/packages/ui/src/native/mcp-navigation.mjs +52 -0
- package/packages/ui/src/native/mcp-theme.css +4 -0
- package/packages/ui/src/native/mcp-theme.mjs +20 -0
- package/packages/ui/assets/arrow-up-right.svg +0 -13
- package/packages/ui/assets/minecodex-menu-16.svg +0 -7
- package/packages/ui/assets/minecodex-menu-20.svg +0 -7
- package/packages/ui/src/components/account-menu.mjs +0 -88
- package/packages/ui/src/native/account-menu.mjs +0 -21
|
@@ -10,6 +10,7 @@ let addButton = null;
|
|
|
10
10
|
let composerRoot = null;
|
|
11
11
|
let composerThread = null;
|
|
12
12
|
let composerEditable = null;
|
|
13
|
+
let focusedEditable = null;
|
|
13
14
|
let panel = null;
|
|
14
15
|
let items = [];
|
|
15
16
|
let generation = 0;
|
|
@@ -25,6 +26,9 @@ function appearanceLabel(key) {
|
|
|
25
26
|
}
|
|
26
27
|
function errorMessage(error) {
|
|
27
28
|
const messages = {
|
|
29
|
+
"No available input in the focused window": "当前窗口没有可用输入框,请先打开聊天并确认 MCX 增强服务已开启。",
|
|
30
|
+
"Input changed before insertion": "输入框已切换,请重新选择提示词。",
|
|
31
|
+
"Enable Prompts in MineCodex first": "请先在 MineCodex 中开启提示词。",
|
|
28
32
|
"Open a chat before using a prompt.": "请先打开一个对话,再使用提示词。",
|
|
29
33
|
"Category was not found": "未找到该分类",
|
|
30
34
|
"Prompt was not found": "未找到该提示词",
|
|
@@ -281,6 +285,8 @@ function renderList() {
|
|
|
281
285
|
async () => {
|
|
282
286
|
const current = generation;
|
|
283
287
|
try {
|
|
288
|
+
if (!composerEditable?.isConnected || !config.codex.composer(composerEditable,{purpose:"insertion"}))
|
|
289
|
+
throw new Error("Input changed before insertion");
|
|
284
290
|
window.__codexPersonalRuntime.insertText(item.body, composerEditable);
|
|
285
291
|
// 插入同步成功后再记一次调用;失败和单纯浏览不计数。
|
|
286
292
|
close();
|
|
@@ -338,10 +344,15 @@ function isOpenComposerMenu(node) {
|
|
|
338
344
|
&& (node.getAttribute("data-composer-navigation-target") === "add-context"
|
|
339
345
|
|| node.getAttribute("aria-haspopup") === "menu");
|
|
340
346
|
}
|
|
347
|
+
function insertionComposer() {
|
|
348
|
+
// 官方 App 获得 iframe 焦点时,保留用户最后操作的同窗口输入框。
|
|
349
|
+
const target = document.activeElement?.tagName === "IFRAME" ? focusedEditable : null;
|
|
350
|
+
return config.codex.composer(target,{purpose:"insertion"});
|
|
351
|
+
}
|
|
341
352
|
function sync() {
|
|
342
353
|
if (disposed || signal.aborted) return;
|
|
343
|
-
|
|
344
|
-
const nativeComposer = config.codex.composer(
|
|
354
|
+
if (config.management) return;
|
|
355
|
+
const nativeComposer = panel ? config.codex.composer(composerEditable,{purpose:"insertion"}) : insertionComposer();
|
|
345
356
|
const add = nativeComposer?.add;
|
|
346
357
|
const card = nativeComposer?.root;
|
|
347
358
|
const thread = nativeComposer?.threadId ?? null;
|
|
@@ -357,6 +368,7 @@ function sync() {
|
|
|
357
368
|
composerRoot = card;
|
|
358
369
|
}
|
|
359
370
|
composerEditable = nativeComposer?.editable ?? null;
|
|
371
|
+
insertionBridge?.remember(nativeComposer);
|
|
360
372
|
if (!add) return;
|
|
361
373
|
if (
|
|
362
374
|
panel &&
|
|
@@ -402,8 +414,6 @@ function sync() {
|
|
|
402
414
|
}
|
|
403
415
|
// 页面与快捷调用面板共享数据;所有编辑只存在于页面。
|
|
404
416
|
// 管理页面与编辑弹窗
|
|
405
|
-
let navButton = null;
|
|
406
|
-
let ownNavGroup = null;
|
|
407
417
|
let page = null;
|
|
408
418
|
let pageGeneration = 0;
|
|
409
419
|
let activeCategory = "all";
|
|
@@ -414,8 +424,6 @@ let cardResizeObserver = null;
|
|
|
414
424
|
let searchText = "";
|
|
415
425
|
let editor = null;
|
|
416
426
|
let pageFocus = null;
|
|
417
|
-
const hiddenSurfaces = new Map();
|
|
418
|
-
let navigationRail = null;
|
|
419
427
|
|
|
420
428
|
function pageControl(label, handler, primary = false, tone = "secondary") {
|
|
421
429
|
return config.ui.action(control(label, handler, `pm-command${primary ? " pm-primary" : ""}`), primary ? "primary" : tone);
|
|
@@ -428,23 +436,7 @@ function pageError(error) {
|
|
|
428
436
|
(editor ?? page.querySelector(".pm-wrap") ?? page).append(message);
|
|
429
437
|
}
|
|
430
438
|
function positionPage() {
|
|
431
|
-
if (
|
|
432
|
-
const rail = config.codex.rail();
|
|
433
|
-
if (!rail) {
|
|
434
|
-
closePage();
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
const rect = rail.getBoundingClientRect();
|
|
438
|
-
page.style.left = `${rect.right}px`;
|
|
439
|
-
page.style.top = `${rect.top}px`;
|
|
440
|
-
page.style.right = "4px";
|
|
441
|
-
page.style.bottom = "4px";
|
|
442
|
-
}
|
|
443
|
-
function syncNavigationSelection() {
|
|
444
|
-
navigationRail?.select(Boolean(page));
|
|
445
|
-
}
|
|
446
|
-
function restoreNavigationSelection() {
|
|
447
|
-
navigationRail?.select(false);
|
|
439
|
+
if (page) Object.assign(page.style, {inset:"0",borderRadius:"0"});
|
|
448
440
|
}
|
|
449
441
|
function closePage(restoreFocus = false, restoreNavigation = true) {
|
|
450
442
|
dragState?.cancel();
|
|
@@ -456,12 +448,7 @@ function closePage(restoreFocus = false, restoreNavigation = true) {
|
|
|
456
448
|
if (page) config.ui.releasePage(page, { restoreNavigation });
|
|
457
449
|
page?.remove();
|
|
458
450
|
page = null;
|
|
459
|
-
restoreNavigationSelection();
|
|
460
451
|
editor = null;
|
|
461
|
-
for (const [surface, inert] of hiddenSurfaces) surface.inert = inert;
|
|
462
|
-
hiddenSurfaces.clear();
|
|
463
|
-
navButton?.removeAttribute("data-selected");
|
|
464
|
-
navButton?.removeAttribute("aria-current");
|
|
465
452
|
document.documentElement.removeAttribute("data-mcx-prompts-open");
|
|
466
453
|
if (restoreFocus && pageFocus?.isConnected)
|
|
467
454
|
pageFocus.focus({ preventScroll: true });
|
|
@@ -487,19 +474,9 @@ function openPage() {
|
|
|
487
474
|
page = element("section");
|
|
488
475
|
page.setAttribute(MARKER, "page");
|
|
489
476
|
config.ui.page(page, { sidebar:"none", onNavigate:() => closePage(false, false) });
|
|
490
|
-
page.setAttribute("aria-label", text("
|
|
477
|
+
page.setAttribute("aria-label", text("Prompt", "提示词"));
|
|
491
478
|
document.body.append(page);
|
|
492
|
-
// 只隔离主内容的焦点,保留导航栏与原生窗口控件。
|
|
493
|
-
for (const surface of document.querySelectorAll(
|
|
494
|
-
'[class*="_PageSurface_"], [class*="_Workspace_"]',
|
|
495
|
-
)) {
|
|
496
|
-
hiddenSurfaces.set(surface, surface.inert);
|
|
497
|
-
surface.inert = true;
|
|
498
|
-
}
|
|
499
479
|
document.documentElement.setAttribute("data-mcx-prompts-open", "");
|
|
500
|
-
navButton?.setAttribute("data-selected", "");
|
|
501
|
-
navButton?.setAttribute("aria-current", "page");
|
|
502
|
-
syncNavigationSelection();
|
|
503
480
|
positionPage();
|
|
504
481
|
page.append(element("div", "pm-empty", text("Loading…", "加载中…")));
|
|
505
482
|
return loadPage();
|
|
@@ -666,7 +643,7 @@ function renderPage() {
|
|
|
666
643
|
searchField,
|
|
667
644
|
pageControl(text("New prompt", "新建提示词"), () => editPrompt(), true),
|
|
668
645
|
);
|
|
669
|
-
heading.append(element("h1", "", text("
|
|
646
|
+
heading.append(element("h1", "", text("Prompt", "提示词")), actions);
|
|
670
647
|
const tabs = element("div", "pm-tabs");
|
|
671
648
|
tabs.setAttribute("role", "tablist");
|
|
672
649
|
tabs.setAttribute("aria-label", text("Categories", "分类"));
|
|
@@ -818,31 +795,9 @@ function cardPreview(item) {
|
|
|
818
795
|
}
|
|
819
796
|
async function usePagePrompt(item, trigger) {
|
|
820
797
|
trigger.disabled = true;
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
let editable = null;
|
|
825
|
-
for (let attempt = 0; attempt < 40 && !signal.aborted; attempt++) {
|
|
826
|
-
editable = config.codex.composer(null, { purpose: "insertion" })?.editable;
|
|
827
|
-
if (editable) break;
|
|
828
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
829
|
-
}
|
|
830
|
-
if (signal.aborted) return;
|
|
831
|
-
if (!editable) throw new Error("Open a chat before using a prompt.");
|
|
832
|
-
window.__codexPersonalRuntime.insertText(item.body, editable);
|
|
833
|
-
} catch (error) {
|
|
834
|
-
await openPage();
|
|
835
|
-
pageError(error);
|
|
836
|
-
return;
|
|
837
|
-
}
|
|
838
|
-
try {
|
|
839
|
-
await api(`/api/prompts/${encodeURIComponent(item.id)}/use`, {
|
|
840
|
-
method: "POST", body: { action: "insert", success: true },
|
|
841
|
-
});
|
|
842
|
-
} catch (error) {
|
|
843
|
-
// 正文已插入,计数失败不能再次插入。
|
|
844
|
-
console.warn("[Prompts] Usage could not be saved", error);
|
|
845
|
-
}
|
|
798
|
+
try { await config.insertPrompt(item.id); if (!signal.aborted) await loadPage(); }
|
|
799
|
+
catch (error) { if (!signal.aborted) pageError(error); }
|
|
800
|
+
finally { trigger.disabled = false; }
|
|
846
801
|
}
|
|
847
802
|
let imagePreview = null;
|
|
848
803
|
let imagePreviewAnchor = null;
|
|
@@ -1031,7 +986,7 @@ function emptyState(searching) {
|
|
|
1031
986
|
const empty = element("div", "pm-empty-state");
|
|
1032
987
|
const art = element("span", "pm-empty-art", searching ? "🔎" : "📖");
|
|
1033
988
|
art.setAttribute("role", "img");
|
|
1034
|
-
art.setAttribute("aria-label", text("
|
|
989
|
+
art.setAttribute("aria-label", text("Prompt", "提示词"));
|
|
1035
990
|
empty.append(
|
|
1036
991
|
art,
|
|
1037
992
|
element(
|
|
@@ -1620,70 +1575,16 @@ function editCategory(category = null) {
|
|
|
1620
1575
|
name.focus();
|
|
1621
1576
|
}
|
|
1622
1577
|
// 原生导航挂载与卸载
|
|
1623
|
-
function syncNavigation() {
|
|
1624
|
-
const rail = config.codex.rail();
|
|
1625
|
-
if (!rail) {
|
|
1626
|
-
navButton?.remove();
|
|
1627
|
-
navButton = null;
|
|
1628
|
-
if (page) closePage();
|
|
1629
|
-
return;
|
|
1630
|
-
}
|
|
1631
|
-
const slot = config.codex.railSlot(ownNavGroup);
|
|
1632
|
-
if (!slot) return;
|
|
1633
|
-
const { template:source, scroll } = slot;
|
|
1634
|
-
let group = slot.group;
|
|
1635
|
-
if (!group) {
|
|
1636
|
-
if (!ownNavGroup) {
|
|
1637
|
-
ownNavGroup = element("div", "flex shrink-0 flex-col items-center gap-2");
|
|
1638
|
-
ownNavGroup.setAttribute(MARKER, "nav-group");
|
|
1639
|
-
}
|
|
1640
|
-
if (ownNavGroup.parentElement !== scroll) scroll.append(ownNavGroup);
|
|
1641
|
-
group = ownNavGroup;
|
|
1642
|
-
}
|
|
1643
|
-
if (!navButton?.isConnected) {
|
|
1644
|
-
navigationRail?.restore();
|
|
1645
|
-
navButton = source.cloneNode(false);
|
|
1646
|
-
navButton.removeAttribute("id");
|
|
1647
|
-
navButton.removeAttribute("title");
|
|
1648
|
-
navButton.removeAttribute("aria-describedby");
|
|
1649
|
-
navButton.removeAttribute("data-sidebar-destination");
|
|
1650
|
-
navButton.removeAttribute("data-selected");
|
|
1651
|
-
navButton.removeAttribute("aria-current");
|
|
1652
|
-
navButton.removeAttribute("data-suppress-active-style");
|
|
1653
|
-
navButton.setAttribute(MARKER, "nav");
|
|
1654
|
-
navButton.setAttribute("aria-label", text("Prompts", "提示词"));
|
|
1655
|
-
navButton.setAttribute("data-minecodex-tooltip", text("Prompts", "提示词"));
|
|
1656
|
-
const inner = element("span", source.firstElementChild?.className ?? "");
|
|
1657
|
-
const glyph = icon("prompt-nav");
|
|
1658
|
-
glyph.setAttribute("aria-hidden", "true");
|
|
1659
|
-
inner.append(glyph, element("span", "sr-only", text("Prompts", "提示词")));
|
|
1660
|
-
navButton.append(inner);
|
|
1661
|
-
navigationRail = config.ui.navigationRail(navButton, {
|
|
1662
|
-
defaultIcon:glyph, selectedIcon:icon("prompt-nav-selected"),
|
|
1663
|
-
});
|
|
1664
|
-
navButton.addEventListener("click", openPage);
|
|
1665
|
-
}
|
|
1666
|
-
if (navButton.parentElement !== group || navButton !== group.lastElementChild)
|
|
1667
|
-
group.append(navButton);
|
|
1668
|
-
if (group !== ownNavGroup) ownNavGroup?.remove();
|
|
1669
|
-
syncNavigationSelection();
|
|
1670
|
-
positionPage();
|
|
1671
|
-
}
|
|
1672
|
-
document.addEventListener(
|
|
1673
|
-
"click",
|
|
1674
|
-
(event) => {
|
|
1675
|
-
if (
|
|
1676
|
-
page &&
|
|
1677
|
-
event.target.closest("[data-app-navigation-rail] button") &&
|
|
1678
|
-
!navButton?.contains(event.target)
|
|
1679
|
-
)
|
|
1680
|
-
closePage(false, false);
|
|
1681
|
-
},
|
|
1682
|
-
{ signal, capture: true },
|
|
1683
|
-
);
|
|
1684
|
-
window.addEventListener("popstate", () => closePage(false, false), { signal });
|
|
1685
1578
|
window.addEventListener("resize", positionPage, { signal });
|
|
1686
1579
|
|
|
1580
|
+
document.addEventListener("focusin", event => {
|
|
1581
|
+
if (config.management) return;
|
|
1582
|
+
const target = event.target.closest?.('[contenteditable="true"]');
|
|
1583
|
+
const focused = target ? config.codex.composer(target,{purpose:"insertion"}) : null;
|
|
1584
|
+
if (focused) focusedEditable = focused.editable;
|
|
1585
|
+
sync();
|
|
1586
|
+
}, {signal});
|
|
1587
|
+
|
|
1687
1588
|
const observer = new MutationObserver(() => {
|
|
1688
1589
|
if (syncQueued) return;
|
|
1689
1590
|
syncQueued = true;
|
|
@@ -1692,7 +1593,7 @@ const observer = new MutationObserver(() => {
|
|
|
1692
1593
|
sync();
|
|
1693
1594
|
});
|
|
1694
1595
|
});
|
|
1695
|
-
observer.observe(document.documentElement, {
|
|
1596
|
+
if (!config.management) observer.observe(document.documentElement, {
|
|
1696
1597
|
childList: true,
|
|
1697
1598
|
subtree: true,
|
|
1698
1599
|
attributes: true,
|
|
@@ -1745,24 +1646,25 @@ document.addEventListener(
|
|
|
1745
1646
|
);
|
|
1746
1647
|
window.addEventListener("resize", () => { clearQuickPreview(true); position(); }, { signal });
|
|
1747
1648
|
window.addEventListener("scroll", position, { signal, capture: true });
|
|
1748
|
-
|
|
1649
|
+
const insertionBridge = config.management ? null : installPromptInsertion(signal, {
|
|
1650
|
+
codex:config.codex, api, insertText:(body,editable) => window.__codexPersonalRuntime.insertText(body,editable),
|
|
1651
|
+
});
|
|
1652
|
+
if (config.management) openPage(); else sync();
|
|
1749
1653
|
|
|
1750
1654
|
config.registerSelfCheck?.(() => {
|
|
1751
|
-
const
|
|
1752
|
-
const checks=[{name:"prompts-
|
|
1753
|
-
if (composer) checks.push({name:"quick-prompts-anchor",available:Boolean(composer.add && button?.isConnected && composerRoot === composer.root)});
|
|
1655
|
+
const composer=config.codex.composer(null,{purpose:"insertion"});
|
|
1656
|
+
const checks=composer ? [{name:"quick-prompts-anchor",available:Boolean(composer.add && button?.isConnected && composerEditable === composer.editable)}] : [];
|
|
1754
1657
|
const available=checks.every(check=>check.available);
|
|
1755
|
-
return {state:available ? "available" : "unavailable",reason:available ? null : "prompts-anchor-unavailable",checks,
|
|
1756
|
-
scopes:[{name:"
|
|
1658
|
+
return {state:composer ? available ? "available" : "unavailable" : "not-applicable",reason:available ? null : "prompts-anchor-unavailable",checks,
|
|
1659
|
+
scopes:[{name:"composer",element:composer?.root},{name:"quick-prompts",element:panel}]};
|
|
1757
1660
|
});
|
|
1758
1661
|
|
|
1759
1662
|
return () => {
|
|
1760
1663
|
disposed = true;
|
|
1664
|
+
|
|
1761
1665
|
observer.disconnect();
|
|
1762
1666
|
close();
|
|
1763
1667
|
button?.remove();
|
|
1764
1668
|
closePage();
|
|
1765
|
-
navButton?.remove();
|
|
1766
|
-
ownNavGroup?.remove();
|
|
1767
1669
|
style.remove();
|
|
1768
1670
|
};
|
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
"back": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path d=\"M9.87117 11.6289C10.0762 11.8339 10.0762 12.166 9.87117 12.3711C9.66614 12.5761 9.334 12.5761 9.12898 12.3711L5.36433 8.60641C5.0296 8.27129 5.0296 7.72863 5.36433 7.39351L9.12898 3.62887C9.334 3.42384 9.66614 3.42384 9.87117 3.62887C10.0762 3.83389 10.0762 4.16603 9.87117 4.37105L6.24226 7.99996L9.87117 11.6289Z\" fill=\"currentColor\"/></svg>",
|
|
9
9
|
"settings": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M6.33374 8.97461C7.54786 8.97484 8.56163 9.83236 8.80347 10.9746H13.3337C13.6235 10.9749 13.8591 11.2102 13.8591 11.5C13.8591 11.7898 13.6235 12.0251 13.3337 12.0254H8.80347C8.56163 13.1676 7.54786 14.0252 6.33374 14.0254C5.11907 14.0254 4.10455 13.1673 3.86304 12.0244L2.66675 12.0254C2.37691 12.0253 2.14138 11.7899 2.14136 11.5C2.14136 11.2102 2.37693 10.9748 2.66675 10.9746H3.86304C4.10491 9.83221 5.11941 8.97461 6.33374 8.97461ZM6.33374 10.0254C5.51912 10.0254 4.85913 10.6854 4.85913 11.5C4.85913 12.3146 5.51912 12.9746 6.33374 12.9746C7.14814 12.9743 7.80835 12.3145 7.80835 11.5C7.80835 10.6855 7.14814 10.0257 6.33374 10.0254Z\" fill=\"currentColor\"/> <path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M9.66675 1.97461C10.881 1.97461 11.8935 2.83236 12.1355 3.97461H13.3328C13.6227 3.97459 13.8581 4.21005 13.8582 4.5C13.8581 4.78993 13.6227 5.02537 13.3328 5.02539H12.1365C11.8946 6.16774 10.881 7.02539 9.66675 7.02539C8.45245 7.02539 7.43893 6.16774 7.19702 5.02539H2.66675C2.3768 5.02539 2.14136 4.78995 2.14136 4.5C2.14136 4.21005 2.3768 3.97461 2.66675 3.97461H7.19702C7.43893 2.83226 8.45245 1.97461 9.66675 1.97461ZM9.66675 3.02539C8.85213 3.02539 8.19214 3.68538 8.19214 4.5C8.19214 5.31462 8.85213 5.97461 9.66675 5.97461C10.4814 5.97461 11.1414 5.31462 11.1414 4.5C11.1414 3.68538 10.4814 3.02539 9.66675 3.02539Z\" fill=\"currentColor\"/></svg>",
|
|
10
10
|
"drag": "<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n>\n <path d=\"M4 5h16\" />\n <path d=\"M4 12h16\" />\n <path d=\"M4 19h16\" />\n</svg>\n",
|
|
11
|
-
"prompt-nav": "<svg\n class=\"lucide lucide-book-open\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"-1.5 -1.5 27 27\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.8\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n>\n <path d=\"M12 7v14 M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z\" />\n</svg>",
|
|
12
|
-
"prompt-nav-selected": "<svg width=\"20\" height=\"20\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M240,56V200a8,8,0,0,1-8,8H160a24,24,0,0,0-24,23.94,7.9,7.9,0,0,1-5.12,7.55A8,8,0,0,1,120,232a24,24,0,0,0-24-24H24a8,8,0,0,1-8-8V56a8,8,0,0,1,8-8H88a32,32,0,0,1,32,32v87.73a8.17,8.17,0,0,0,7.47,8.25,8,8,0,0,0,8.53-8V80a32,32,0,0,1,32-32h64A8,8,0,0,1,240,56Z\"/></svg>",
|
|
13
11
|
"layout-grid": "<!-- Lucide, ISC license; see assets/brand/references/lucide-LICENSE.txt -->\n<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n>\n <rect width=\"7\" height=\"7\" x=\"3\" y=\"3\" rx=\"1\" />\n <rect width=\"7\" height=\"7\" x=\"14\" y=\"3\" rx=\"1\" />\n <rect width=\"7\" height=\"7\" x=\"14\" y=\"14\" rx=\"1\" />\n <rect width=\"7\" height=\"7\" x=\"3\" y=\"14\" rx=\"1\" />\n</svg>\n",
|
|
14
12
|
"list": "<!-- Lucide, ISC license; see assets/brand/references/lucide-LICENSE.txt -->\n<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n>\n <path d=\"M3 5h.01\" />\n <path d=\"M3 12h.01\" />\n <path d=\"M3 19h.01\" />\n <path d=\"M8 5h13\" />\n <path d=\"M8 12h13\" />\n <path d=\"M8 19h13\" />\n</svg>\n",
|
|
15
13
|
"square-pen": "<!-- Lucide, ISC license; see assets/brand/references/lucide-LICENSE.txt -->\n<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n>\n <path d=\"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\" />\n <path d=\"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z\" />\n</svg>\n"
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import {readFile} from 'node:fs/promises';
|
|
2
|
+
import {installPromptInsertion} from './adapters/codex/insertion.mjs';
|
|
3
|
+
export async function createPageScriptSource() {
|
|
4
|
+
return `const installPromptInsertion = ${installPromptInsertion.toString()};\n${await readFile(new URL('./frontend/entry.mjs',import.meta.url),'utf8')}`;
|
|
5
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "minecodex",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.16",
|
|
4
4
|
"description": "Lightweight, local-first plugins for the Codex desktop app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"assets/brand/minecodex-icon.svg",
|
|
58
58
|
"assets/brand/minecodex-icon-light.svg",
|
|
59
59
|
"assets/brand/minecodex-icon-dark.svg",
|
|
60
|
+
"assets/brand/minecodex-icon-selected.svg",
|
|
60
61
|
"assets/brand/references/lucide-LICENSE.txt",
|
|
61
62
|
"assets/readme/hero.svg",
|
|
62
63
|
"packages/ui/assets",
|
|
@@ -67,14 +68,18 @@
|
|
|
67
68
|
"features/file-navigation/src",
|
|
68
69
|
"features/file-navigation/codex-feature.json",
|
|
69
70
|
"features/file-navigation/assets",
|
|
70
|
-
"features/file-navigation/vendor/lucide-LICENSE"
|
|
71
|
+
"features/file-navigation/vendor/lucide-LICENSE",
|
|
72
|
+
"apps/dashboard/transport.js",
|
|
73
|
+
"packages/plugin/src",
|
|
74
|
+
"packages/plugin/assets",
|
|
75
|
+
"packages/plugin/plugin.json"
|
|
71
76
|
],
|
|
72
77
|
"engines": {
|
|
73
78
|
"node": ">=22.5.0"
|
|
74
79
|
},
|
|
75
80
|
"scripts": {
|
|
76
81
|
"start": "node packages/cli/bin/mcx.mjs serve",
|
|
77
|
-
"test": "node --test packages/cli/test/*.test.mjs packages/runtime-host/test/*.test.mjs features/model-slider/test/*.test.mjs features/notes/test/*.test.mjs features/images/test/*.test.mjs features/hide-upsell-banner/test/*.test.mjs features/session-status/test/*.test.mjs features/usage-meter/test/*.test.mjs apps/dashboard/test/*.test.mjs features/unread-inbox/test/*.test.mjs features/sidebar-focus/test/*.test.mjs features/sites/test/*.test.mjs packages/ui/test/*.test.mjs packages/codex-adapter/test/*.test.mjs features/markdown-reading/test/*.test.mjs features/file-navigation/test/*.test.mjs",
|
|
82
|
+
"test": "node --test packages/cli/test/*.test.mjs packages/runtime-host/test/*.test.mjs features/model-slider/test/*.test.mjs features/notes/test/*.test.mjs features/images/test/*.test.mjs features/hide-upsell-banner/test/*.test.mjs features/session-status/test/*.test.mjs features/usage-meter/test/*.test.mjs apps/dashboard/test/*.test.mjs features/unread-inbox/test/*.test.mjs features/sidebar-focus/test/*.test.mjs features/sites/test/*.test.mjs packages/ui/test/*.test.mjs packages/codex-adapter/test/*.test.mjs features/markdown-reading/test/*.test.mjs features/file-navigation/test/*.test.mjs packages/plugin/test/*.test.mjs",
|
|
78
83
|
"test:cli": "node --test packages/cli/test/*.test.mjs",
|
|
79
84
|
"test:runtime": "node --test packages/runtime-host/test/*.test.mjs",
|
|
80
85
|
"check": "node scripts/check-syntax.mjs",
|
|
@@ -83,7 +88,9 @@
|
|
|
83
88
|
"pack:audit": "node scripts/audit-package.mjs",
|
|
84
89
|
"prepublishOnly": "npm test && npm run check && npm run pack:audit",
|
|
85
90
|
"test:ui": "node --test packages/ui/test/*.test.mjs",
|
|
86
|
-
"build:icons": "node scripts/build-brand-icons.mjs"
|
|
91
|
+
"build:icons": "node scripts/build-brand-icons.mjs",
|
|
92
|
+
"build:plugin": "node scripts/build-plugin.mjs",
|
|
93
|
+
"test:plugin": "node --test packages/plugin/test/*.test.mjs"
|
|
87
94
|
},
|
|
88
95
|
"repository": {
|
|
89
96
|
"type": "git",
|
|
@@ -105,6 +112,12 @@
|
|
|
105
112
|
"win32"
|
|
106
113
|
],
|
|
107
114
|
"dependencies": {
|
|
108
|
-
"
|
|
115
|
+
"@modelcontextprotocol/ext-apps": "^2.0.3",
|
|
116
|
+
"@modelcontextprotocol/sdk": "^1.32.0",
|
|
117
|
+
"acorn": "^8.18.0",
|
|
118
|
+
"zod": "^4.6.5"
|
|
119
|
+
},
|
|
120
|
+
"devDependencies": {
|
|
121
|
+
"esbuild": "^0.28.2"
|
|
109
122
|
}
|
|
110
123
|
}
|
|
@@ -85,6 +85,7 @@ Commands:
|
|
|
85
85
|
mcx uninstall [--purge] Remove integration; preserve data unless --purge is used
|
|
86
86
|
mcx update Update to the latest stable npm release
|
|
87
87
|
mcx open Open the MineCodex plugin control panel
|
|
88
|
+
mcx plugin install Register the official Codex plugin entry
|
|
88
89
|
mcx status [--json] Show installation and plugin status
|
|
89
90
|
mcx restart Restart Codex and apply saved plugin switches
|
|
90
91
|
mcx --version Print the installed version
|
|
@@ -447,6 +448,15 @@ export async function runCli(argv, {
|
|
|
447
448
|
const [command = "--help", ...args] = argv;
|
|
448
449
|
if (["--help", "-h", "help"].includes(command)) return printHelp(output);
|
|
449
450
|
if (["--version", "-v"].includes(command)) return output(packageMetadata.version);
|
|
451
|
+
if (command === "plugin" && args[0] === "serve") {
|
|
452
|
+
const { servePlugin } = await import("../../plugin/src/server.mjs");
|
|
453
|
+
return servePlugin({ paths });
|
|
454
|
+
}
|
|
455
|
+
if (command === "plugin" && args[0] === "install") {
|
|
456
|
+
const { installPlugin } = await import("../../plugin/src/install.mjs");
|
|
457
|
+
await installPlugin({ paths });
|
|
458
|
+
return output("MineCodex plugin installed. Open Explore in Codex to find MineCodex.");
|
|
459
|
+
}
|
|
450
460
|
if (command === "install") return installCommand({ paths, platform, cliPath, output, serviceProbe, fetchImpl, confirmRestart, nodeVersion });
|
|
451
461
|
if (command === "open" || command === "gui") return openCommand({ paths, platform, output, serviceProbe, fetchImpl });
|
|
452
462
|
if (command === "status") return statusCommand({ paths, platform, jsonOutput: args.includes("--json"), output, fetchImpl });
|
|
@@ -65,12 +65,41 @@ export function createCodexEnvironment() {
|
|
|
65
65
|
const group = [...(scroll?.children ?? [])].find(child => child !== ownGroup && child.classList.contains('flex-col') && child.classList.contains('shrink-0'));
|
|
66
66
|
return node && template && scroll ? { rail:node, template, scroll, group } : null;
|
|
67
67
|
}
|
|
68
|
+
// 导航与 MCP iframe 身份在 Adapter 内解释,功能不接触 React props。
|
|
69
|
+
function navigation() {
|
|
70
|
+
const scope = props(rail(), value => Boolean((value.router ?? value.value?.router)?.navigate));
|
|
71
|
+
const router = scope?.router ?? scope?.value?.router;
|
|
72
|
+
if (!router?.subscribe) return null;
|
|
73
|
+
const read = () => {
|
|
74
|
+
const location = router.state.location;
|
|
75
|
+
return {path:location.pathname + (location.search ?? "") + (location.hash ?? ""), key:location.key};
|
|
76
|
+
};
|
|
77
|
+
return {location:read, navigate:path => router.navigate(path, {replace:true}),
|
|
78
|
+
subscribe:callback => router.subscribe(state => {
|
|
79
|
+
if (!state.navigation?.state || state.navigation.state === "idle") callback(read());
|
|
80
|
+
})};
|
|
81
|
+
}
|
|
82
|
+
function mcpAppSource(sourceWindow, {server, pluginId, resourceUri}) {
|
|
83
|
+
for (const frame of document.querySelectorAll('[data-mcp-extension-surface] iframe')) {
|
|
84
|
+
let source = sourceWindow, matches = false;
|
|
85
|
+
// 官方 sandbox proxy 包含资源 iframe;WindowProxy.parent 可跨源读取。
|
|
86
|
+
for (let depth = 0; source && depth < 4; depth++) {
|
|
87
|
+
if (source === frame.contentWindow) {matches = true;break;}
|
|
88
|
+
try {if (source.parent === source) break; source = source.parent;} catch {break;}
|
|
89
|
+
}
|
|
90
|
+
if (!matches) continue;
|
|
91
|
+
const view = props(frame, value => value.view?.resourceUri != null)?.view;
|
|
92
|
+
if (view?.resourceUri === resourceUri && (view.server === server || view.pluginId === pluginId))
|
|
93
|
+
return {connected:() => frame.isConnected};
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
68
97
|
function route() {
|
|
69
98
|
const node = rail();
|
|
70
99
|
const scope = node && props(node, value => Boolean((value.router ?? value.value?.router)?.state?.location));
|
|
71
100
|
return (scope?.router ?? scope?.value?.router)?.state?.location?.pathname ?? null;
|
|
72
101
|
}
|
|
73
|
-
return { composer, composerSurface, rail, railSlot, route, currentThreadId:element => composer(element)?.threadId ?? null,
|
|
102
|
+
return { composer, composerSurface, rail, railSlot, route, navigation, mcpAppSource, currentThreadId:element => composer(element)?.threadId ?? null,
|
|
74
103
|
// 仅供 UI/插件 native Adapter 使用,交互层不读取 React props。
|
|
75
104
|
currentFiber, props, report, diagnostics:() => [...capabilities.values()].map(item => ({...item})) };
|
|
76
105
|
}
|