minecodex 2.0.15 → 2.0.17
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 +9 -6
- 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/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 +23 -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
package/README.md
CHANGED
|
@@ -50,11 +50,18 @@ mcx open
|
|
|
50
50
|
| `mcx uninstall [--purge]` | 移除扩展;默认保留个人数据,带 `--purge` 参数才会清理 |
|
|
51
51
|
| `mcx update` | 更新到最新的稳定版本 |
|
|
52
52
|
| `mcx open` | 打开 MineCodex 控制台 |
|
|
53
|
+
| `mcx plugin install` | 通过官方插件机制注册 Codex 内的 MineCodex 控制台入口 |
|
|
53
54
|
| `mcx status [--json]` | 查看安装与插件运行状态 |
|
|
54
55
|
| `mcx restart` | 重启 Codex 并应用插件开启/关闭状态 |
|
|
55
56
|
|
|
56
57
|
关闭某项功能仅停用对应的扩展服务,不会删除任何个人数据;修改设置后运行 `mcx restart` 即可应用更改。
|
|
57
58
|
|
|
59
|
+
运行 `mcx plugin install` 后,在 Codex 的 Explore/探索中打开 MineCodex,也可按原生
|
|
60
|
+
导航设置固定到侧栏。该本地插件使用官方 MCP Apps 入口与面板,复用原有控制台。
|
|
61
|
+
关闭面板不会停止增强;各项功能继续由 MineCodex 本地服务和注入运行时提供。
|
|
62
|
+
当前正在运行的窗口若未刷新插件目录,下次正常启动后查看。安装命令不会重启 Codex。
|
|
63
|
+
插件卸载使用 `codex plugin remove minecodex@minecodex-local`,不删除增强配置或数据。
|
|
64
|
+
|
|
58
65
|
运行 `mcx uninstall` 会移除名为 MineCodex 的后台项目,并默认保留配置设置、Notes 笔记和 Images 图片索引;只有显式加上 `--purge` 参数,才会清理 MineCodex 产生的本地配置。MineCodex 绝不会删除 `~/.codex/generated_images` 中的原始图片或 Notes 引用的本地文件。
|
|
59
66
|
|
|
60
67
|
## 数据与图片操作
|
|
@@ -106,3 +113,5 @@ MineCodex 是非官方独立扩展项目,与 OpenAI 无关联。项目自有
|
|
|
106
113
|
支持 Prompts 与分类、公网 Sites、模型收藏。模型旧收藏在新版模型滑条首次启动后收录,
|
|
107
114
|
导入收藏在下次启动 Codex 后载入。图片文件、本地站点、账号信息与设备设置不包含在文件中;
|
|
108
115
|
可迁移功能的特殊说明可通过 info 查看。这是手动迁移,不是自动云同步。文件包含提示词正文,请妥善保管。
|
|
116
|
+
|
|
117
|
+
官方插件提供 MineCodex 控制台与 **Prompt** 两个入口,分别使用 MCX 品牌图标与 Prompt 图标。Prompt 仍由 MCX 管理,沿用原有提示词数据;输入框里的快捷提示词继续只填入、不发送。
|
package/apps/dashboard/app.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {diagnosticReport} from "./assets/diagnostics.js";
|
|
2
|
+
import {dashboardFetch} from "./transport.js";
|
|
2
3
|
let savedLocale;
|
|
3
4
|
try { savedLocale = localStorage.getItem("minecodex-language"); } catch { /* 存储不可用时跟随浏览器语言。 */ }
|
|
4
5
|
const locale = ["en", "zh-CN"].includes(savedLocale) ? savedLocale
|
|
@@ -221,7 +222,7 @@ function updatePluginCard(card, feature) {
|
|
|
221
222
|
const dot = card.querySelector(".plugin-state .status-dot");
|
|
222
223
|
const stateLabel = card.querySelector(".plugin-state-label");
|
|
223
224
|
card.dataset.featureId = feature.id;
|
|
224
|
-
image.src = `${feature.iconUrl}?v=resource-1`;
|
|
225
|
+
image.src = feature.iconUrl.startsWith("data:") ? feature.iconUrl : `${feature.iconUrl}?v=resource-1`;
|
|
225
226
|
name.textContent = feature.label;
|
|
226
227
|
description.textContent = feature.description;
|
|
227
228
|
input.checked = Boolean(feature.enabled);
|
|
@@ -310,7 +311,7 @@ function replaceFeatureInState(featureId, patch) {
|
|
|
310
311
|
async function load() {
|
|
311
312
|
elements.loadError.hidden = true;
|
|
312
313
|
try {
|
|
313
|
-
const response = await
|
|
314
|
+
const response = await dashboardFetch("/api/status", { headers: { "Accept-Language": locale } });
|
|
314
315
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
315
316
|
const fresh = await response.json();
|
|
316
317
|
if (pendingFeatureIds.size && state) fresh.features = fresh.features.map(feature =>
|
|
@@ -339,7 +340,7 @@ elements.pluginList.addEventListener("change", async (event) => {
|
|
|
339
340
|
updatePluginCard(card, state.features.find((feature) => feature.id === featureId));
|
|
340
341
|
elements.saveState.textContent = copy.saving;
|
|
341
342
|
try {
|
|
342
|
-
const response = await
|
|
343
|
+
const response = await dashboardFetch(`/api/features/${encodeURIComponent(featureId)}`, {
|
|
343
344
|
method: "PATCH",
|
|
344
345
|
headers: { "Content-Type": "application/json", "Accept-Language": locale },
|
|
345
346
|
body: JSON.stringify({ enabled: input.checked }),
|
|
@@ -369,7 +370,7 @@ elements.restartButton.addEventListener("click", async () => {
|
|
|
369
370
|
elements.restartButton.disabled = true;
|
|
370
371
|
elements.restartButton.querySelector(".button-label").textContent = copy.restarting;
|
|
371
372
|
try {
|
|
372
|
-
const response = await
|
|
373
|
+
const response = await dashboardFetch("/api/restart", {
|
|
373
374
|
method: "POST",
|
|
374
375
|
headers: { "Content-Type": "application/json", "Accept-Language": locale },
|
|
375
376
|
body: "{}",
|
|
@@ -404,7 +405,7 @@ elements.updateButton.addEventListener("click", async () => {
|
|
|
404
405
|
elements.updateButton.querySelector(".button-label").textContent = copy.checkingUpdates;
|
|
405
406
|
elements.updateResult.hidden = true;
|
|
406
407
|
try {
|
|
407
|
-
const response = await
|
|
408
|
+
const response = await dashboardFetch("/api/update");
|
|
408
409
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
409
410
|
const update = await response.json();
|
|
410
411
|
elements.updateResult.textContent = update.available ? copy.updateAvailable(update.latest) : copy.current;
|
|
@@ -422,7 +423,8 @@ const systemTheme = matchMedia("(prefers-color-scheme: dark)");
|
|
|
422
423
|
let selectedTheme = null;
|
|
423
424
|
try { selectedTheme = localStorage.getItem("minecodex-appearance"); } catch { /* 存储不可用时仍允许本次切换。 */ }
|
|
424
425
|
function applyTheme() {
|
|
425
|
-
const
|
|
426
|
+
const hostDark = window.minecodexHostTheme ? window.minecodexHostTheme === "dark" : systemTheme.matches;
|
|
427
|
+
const dark = selectedTheme === "dark" || (selectedTheme !== "light" && hostDark);
|
|
426
428
|
document.documentElement.dataset.theme = dark ? "dark" : "light";
|
|
427
429
|
elements.themeToggle.setAttribute("aria-label", locale === "zh-CN" ? "外观" : "Appearance");
|
|
428
430
|
for (const button of elements.themeButtons) {
|
|
@@ -447,6 +449,7 @@ elements.languageToggle.addEventListener("click", (event) => {
|
|
|
447
449
|
location.reload();
|
|
448
450
|
});
|
|
449
451
|
systemTheme.addEventListener("change", applyTheme);
|
|
452
|
+
window.addEventListener("minecodex-host-theme", applyTheme);
|
|
450
453
|
document.addEventListener("keydown", () => { document.documentElement.dataset.input = "keyboard"; });
|
|
451
454
|
document.addEventListener("pointerdown", () => { document.documentElement.dataset.input = "pointer"; });
|
|
452
455
|
applyStaticCopy();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { installFeatureTooltips } from './ui-tooltip.js';
|
|
2
|
+
import {dashboardFetch} from '../transport.js';
|
|
2
3
|
const tooltipLifetime = new AbortController();
|
|
3
4
|
const { tooltipSurface } = installFeatureTooltips(tooltipLifetime.signal);
|
|
4
5
|
window.addEventListener('pagehide', event => { if (!event.persisted) tooltipLifetime.abort(); });
|
|
@@ -20,7 +21,7 @@ document.querySelector('#data-close').setAttribute('aria-label', t('关闭', 'Cl
|
|
|
20
21
|
exportButton.querySelector('.button-label').textContent = t('导出', 'Export');
|
|
21
22
|
importButton.querySelector('.button-label').textContent = t('导入', 'Import');
|
|
22
23
|
async function request(url, data) {
|
|
23
|
-
const response = await
|
|
24
|
+
const response = await dashboardFetch(url, data ? { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) } : {});
|
|
24
25
|
if (!response.ok) throw new Error(String(response.status));
|
|
25
26
|
return response.json();
|
|
26
27
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// 浏览器继续走同源 HTTP;官方插件面板通过 MCP 调用同一套本地服务。
|
|
2
|
+
export async function dashboardFetch(url, options = {}) {
|
|
3
|
+
const app = window.minecodexMcpApp;
|
|
4
|
+
if (!app) return fetch(url, options);
|
|
5
|
+
const method = options.method ?? "GET";
|
|
6
|
+
const result = await app.callServerTool({
|
|
7
|
+
name: method === "GET" ? "minecodex_read" : "minecodex_write",
|
|
8
|
+
arguments: {
|
|
9
|
+
route: url,
|
|
10
|
+
method,
|
|
11
|
+
locale: new Headers(options.headers).get("Accept-Language") ?? document.documentElement.lang,
|
|
12
|
+
...(options.body ? { body: JSON.parse(options.body) } : {}),
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
if (result.isError) throw new Error(result.content?.find(item => item.type === "text")?.text ?? "MineCodex request failed");
|
|
16
|
+
const { status, body } = result.structuredContent;
|
|
17
|
+
return new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json" } });
|
|
18
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<!-- Generated by scripts/build-brand-icons.mjs from assets/brand/minecodex-icon.svg. -->
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><defs><mask id="pickaxe-cutout" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24" style="mask-type:luminance"><rect width="24" height="24" fill="white"/><g id="pickaxe" transform="translate(6.24 6.24) scale(.48)" fill="black">
|
|
3
|
+
<path d="M15.686 4.314 16.3 3.7a1 1 0 0 1 1.4 0l2.6 2.6a1 1 0 0 1 0 1.4l-.614.614a12.501 12.501 0 0 1 1.356 10.225 1 1 0 0 1-1.751-.119 22 22 0 0 0-3.393-6.319l-.198.199a1 1 0 0 1-1.4 0l-.219.169L6.619 20.38a1 1 0 1 1-3-3l7.912-7.912.169.232a1 1 0 0 1 0-1.4l.198-.197A22 22 0 0 0 5.58 4.71a1 1 0 0 1-.119-1.752 12.5 12.5 0 0 1 10.225 1.356Z"/>
|
|
4
|
+
</g></mask></defs><path fill="currentColor" mask="url(#pickaxe-cutout)" d="M9.3316 2.03768C11.1644 1.54688 13.0268 2.04668 14.356 3.20174C16.0859 2.86512 17.9511 3.36418 19.2935 4.70663C20.6356 6.04882 21.1326 7.91288 20.7964 9.64217C21.9527 10.9716 22.4534 12.8349 21.9625 14.6685C21.4711 16.5025 20.1051 17.8657 18.439 18.439C17.8658 20.1047 16.503 21.47 14.6695 21.9615C12.8356 22.4528 10.9727 21.9519 9.64312 20.7955C7.91371 21.1319 6.04993 20.635 4.70757 19.2926C3.36558 17.9505 2.86659 16.0866 3.20269 14.357C2.04693 13.0273 1.54757 11.1638 2.03863 9.33065C2.52997 7.49759 3.89436 6.13282 5.56011 5.55917C6.13382 3.8935 7.49837 2.52891 9.3316 2.03768Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<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>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!-- @license lucide-static v0.577.0 - ISC -->
|
|
2
|
+
<svg
|
|
3
|
+
class="lucide lucide-book-open"
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
width="20"
|
|
6
|
+
height="20"
|
|
7
|
+
viewBox="-1.5 -1.5 27 27"
|
|
8
|
+
fill="none"
|
|
9
|
+
stroke="currentColor"
|
|
10
|
+
stroke-width="1.8"
|
|
11
|
+
stroke-linecap="round"
|
|
12
|
+
stroke-linejoin="round"
|
|
13
|
+
>
|
|
14
|
+
<path d="M12 7v14" />
|
|
15
|
+
<path d="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" />
|
|
16
|
+
</svg>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"id": "notes",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.12",
|
|
5
5
|
"label": {
|
|
6
6
|
"en": "Prompt Library",
|
|
7
7
|
"zh-CN": "提示词库"
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"icons": "src/frontend/icons.json",
|
|
20
20
|
"styles": "src/frontend/styles.css",
|
|
21
21
|
"previews": "assets/prompt-previews.json"
|
|
22
|
-
}
|
|
22
|
+
},
|
|
23
|
+
"sourceFactory": "src/injection-source.mjs"
|
|
23
24
|
},
|
|
24
25
|
"placement": {
|
|
25
26
|
"order": 20
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// 请求只到达发起 App 所在窗口,不经共享服务队列寻找任意聚焦窗口。
|
|
2
|
+
export function installPromptInsertion(signal, {codex, api, insertText}) {
|
|
3
|
+
const navigation = codex.navigation();
|
|
4
|
+
let remembered = null, busy = false;
|
|
5
|
+
function remember(composer) {
|
|
6
|
+
const location = navigation?.location();
|
|
7
|
+
if (!location || location.path.startsWith('/mcp-app/') || !composer) return;
|
|
8
|
+
remembered = {location, editable:composer.editable, threadId:composer.threadId,
|
|
9
|
+
placement:composer.root.getAttribute('data-composer-placement')};
|
|
10
|
+
}
|
|
11
|
+
function available() {
|
|
12
|
+
const target = remembered?.editable;
|
|
13
|
+
return codex.composer(target ?? null, {purpose:'insertion'});
|
|
14
|
+
}
|
|
15
|
+
function waitForComposer(valid, original, deadline) {
|
|
16
|
+
return new Promise((resolve,reject) => {
|
|
17
|
+
let observer, timer;
|
|
18
|
+
const finish = (error, value) => {observer?.disconnect();clearTimeout(timer);error ? reject(error) : resolve(value);};
|
|
19
|
+
const check = () => {
|
|
20
|
+
if (!valid()) return finish(new Error('Input changed before insertion'));
|
|
21
|
+
const candidates = [...document.querySelectorAll('[contenteditable="true"]')]
|
|
22
|
+
.map(editable => codex.composer(editable,{purpose:'insertion'}))
|
|
23
|
+
.filter(composer => composer && composer.threadId === original.threadId
|
|
24
|
+
&& composer.root.getAttribute('data-composer-placement') === original.placement);
|
|
25
|
+
if (candidates.length > 1) return finish(new Error('Input changed before insertion'));
|
|
26
|
+
if (candidates.length === 1) finish(null,candidates[0]);
|
|
27
|
+
};
|
|
28
|
+
observer = new MutationObserver(check);
|
|
29
|
+
observer.observe(document.documentElement,{childList:true,subtree:true,attributes:true,attributeFilter:['hidden','inert','aria-hidden']});
|
|
30
|
+
timer = setTimeout(() => finish(new Error('No available input in the focused window')),Math.max(0,Math.min(2000,deadline-Date.now())));
|
|
31
|
+
check();
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async function receive(event) {
|
|
35
|
+
if (event.data?.type !== 'minecodex:insert-prompt' || typeof event.data.promptId !== 'string') return;
|
|
36
|
+
const port = event.ports?.[0];
|
|
37
|
+
const source = codex.mcpAppSource(event.source,{server:'minecodex',pluginId:'minecodex@minecodex-local',resourceUri:'ui://minecodex/prompts.html'});
|
|
38
|
+
if (!source || !port) return;
|
|
39
|
+
if (busy || !document.hasFocus()) {port.postMessage({error:'Input changed before insertion'});port.close();return;}
|
|
40
|
+
busy = true;
|
|
41
|
+
const deadline = Math.min(Date.now() + 4000, event.data.expiresAt ?? Infinity);
|
|
42
|
+
let cancelled = false, navigating = false, restoring = false, expected = navigation?.location(), inserted = false;
|
|
43
|
+
const target = available();
|
|
44
|
+
const original = target ? {editable:target.editable,threadId:target.threadId,placement:target.root.getAttribute('data-composer-placement')} : remembered;
|
|
45
|
+
const onBlur = () => {queueMicrotask(() => {if (!document.hasFocus()) cancelled = true;});};
|
|
46
|
+
const onFocus = event => {
|
|
47
|
+
const editable = event.target.closest?.('[contenteditable="true"]');
|
|
48
|
+
if (editable && editable !== original?.editable) {
|
|
49
|
+
const focused = codex.composer(editable,{purpose:'insertion'});
|
|
50
|
+
if (!restoring || navigation.location().path !== remembered?.location.path
|
|
51
|
+
|| focused?.threadId !== original?.threadId
|
|
52
|
+
|| focused?.root.getAttribute('data-composer-placement') !== original?.placement) cancelled = true;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
window.addEventListener('blur',onBlur);
|
|
56
|
+
document.addEventListener('focusin',onFocus);
|
|
57
|
+
const unsubscribe = navigation?.subscribe(location => {
|
|
58
|
+
if (navigating && location.path === remembered?.location.path) {expected = location;navigating = false;}
|
|
59
|
+
else if (location.key !== expected?.key) cancelled = true;
|
|
60
|
+
});
|
|
61
|
+
const valid = () => !cancelled && !signal.aborted && Date.now() < deadline && document.hasFocus();
|
|
62
|
+
try {
|
|
63
|
+
const state = await api('/api/state');
|
|
64
|
+
const prompt = state.prompts.find(item => item.id === event.data.promptId);
|
|
65
|
+
if (!prompt) throw new Error('Prompt was not found');
|
|
66
|
+
if (!valid() || !source.connected()) throw new Error('Input changed before insertion');
|
|
67
|
+
let composer = target && codex.composer(target.editable,{purpose:'insertion'});
|
|
68
|
+
if (target && !composer) throw new Error('Input changed before insertion');
|
|
69
|
+
if (!composer) {
|
|
70
|
+
if (!original || !remembered || !navigation) throw new Error('No available input in the focused window');
|
|
71
|
+
navigating = restoring = true;
|
|
72
|
+
let timer;
|
|
73
|
+
try {await Promise.race([navigation.navigate(remembered.location.path),new Promise((_,reject) => {
|
|
74
|
+
timer = setTimeout(() => {cancelled = true;reject(new Error('No available input in the focused window'));},2000);
|
|
75
|
+
})]);} finally {clearTimeout(timer);}
|
|
76
|
+
composer = await waitForComposer(valid,original,deadline);
|
|
77
|
+
}
|
|
78
|
+
if (!valid() || composer.threadId !== original?.threadId
|
|
79
|
+
|| composer.root.getAttribute('data-composer-placement') !== original?.placement
|
|
80
|
+
|| (!navigating && navigation && navigation.location().key !== expected?.key)) throw new Error('Input changed before insertion');
|
|
81
|
+
// 校验与同步填入之间不再等待;成功后计数失败不报告为可重试的插入失败。
|
|
82
|
+
insertText(prompt.body,composer.editable);
|
|
83
|
+
inserted = true;
|
|
84
|
+
port.postMessage({ok:true});
|
|
85
|
+
try {await api(`/api/prompts/${encodeURIComponent(prompt.id)}/use`,{method:'POST',body:{action:'insert',success:true}});}
|
|
86
|
+
catch (error) {console.warn('[Prompts] Usage could not be saved',error);}
|
|
87
|
+
} catch (error) {if (!inserted) port.postMessage({error:error.message});}
|
|
88
|
+
finally {unsubscribe?.();window.removeEventListener('blur',onBlur);document.removeEventListener('focusin',onFocus);port.close();busy=false;}
|
|
89
|
+
}
|
|
90
|
+
window.addEventListener('message',receive,{signal});
|
|
91
|
+
return {remember};
|
|
92
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function assertPromptsRequest(route, method = 'GET') {
|
|
2
|
+
const allowed = (route === '/api/state' && method === 'GET')
|
|
3
|
+
|| (['/api/prompts', '/api/categories', '/api/reorder'].includes(route) && method === 'POST')
|
|
4
|
+
|| (/^\/api\/(prompts|categories)\/[a-zA-Z0-9-]+$/.test(route) && ['PATCH','DELETE'].includes(method))
|
|
5
|
+
|| (/^\/api\/prompts\/[a-zA-Z0-9-]+\/use$/.test(route) && method === 'POST');
|
|
6
|
+
if (!allowed) throw new Error('Unsupported Prompts operation');
|
|
7
|
+
}
|
|
@@ -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
|
+
}
|