minecodex 2.0.54 → 2.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.
Files changed (68) hide show
  1. package/README.md +90 -26
  2. package/apps/dashboard/app.js +20 -14
  3. package/apps/dashboard/assets/chevron-left.svg +13 -0
  4. package/apps/dashboard/assets/chevron-right.svg +13 -0
  5. package/apps/dashboard/assets/feature-showcase.js +185 -0
  6. package/apps/dashboard/assets/pause.svg +14 -0
  7. package/apps/dashboard/assets/play.svg +13 -0
  8. package/apps/dashboard/assets/previews/composer-expand.jpg +0 -0
  9. package/apps/dashboard/assets/previews/composer-expand.mp4 +0 -0
  10. package/apps/dashboard/assets/previews/file-navigation.jpg +0 -0
  11. package/apps/dashboard/assets/previews/file-navigation.mp4 +0 -0
  12. package/apps/dashboard/assets/previews/files-auto-refresh.jpg +0 -0
  13. package/apps/dashboard/assets/previews/files-auto-refresh.mp4 +0 -0
  14. package/apps/dashboard/assets/previews/images.jpg +0 -0
  15. package/apps/dashboard/assets/previews/images.mp4 +0 -0
  16. package/apps/dashboard/assets/previews/manifest.json +67 -1
  17. package/apps/dashboard/assets/previews/markdown-reading.jpg +0 -0
  18. package/apps/dashboard/assets/previews/markdown-reading.mp4 +0 -0
  19. package/apps/dashboard/assets/previews/mermaid-diagrams.jpg +0 -0
  20. package/apps/dashboard/assets/previews/mermaid-diagrams.mp4 +0 -0
  21. package/apps/dashboard/assets/previews/model-slider.jpg +0 -0
  22. package/apps/dashboard/assets/previews/model-slider.mp4 +0 -0
  23. package/apps/dashboard/assets/previews/notes.jpg +0 -0
  24. package/apps/dashboard/assets/previews/notes.mp4 +0 -0
  25. package/apps/dashboard/assets/previews/quick-chat.jpg +0 -0
  26. package/apps/dashboard/assets/previews/quick-chat.mp4 +0 -0
  27. package/apps/dashboard/assets/previews/session-status.jpg +0 -0
  28. package/apps/dashboard/assets/previews/session-status.mp4 +0 -0
  29. package/apps/dashboard/assets/previews/sidebar-focus.jpg +0 -0
  30. package/apps/dashboard/assets/previews/sidebar-focus.mp4 +0 -0
  31. package/apps/dashboard/assets/previews/sites.jpg +0 -0
  32. package/apps/dashboard/assets/previews/sites.mp4 +0 -0
  33. package/apps/dashboard/assets/previews/usage-meter.jpg +0 -0
  34. package/apps/dashboard/assets/previews/usage-meter.mp4 +0 -0
  35. package/apps/dashboard/index.html +26 -2
  36. package/apps/dashboard/styles.css +58 -39
  37. package/assets/readme/hero-dark.svg +50 -0
  38. package/assets/readme/hero.svg +36 -79
  39. package/features/images/src/adapters/codex/rspack-modules.mjs +9 -3
  40. package/features/images/src/library-prompt-source.mjs +2 -2
  41. package/features/mermaid-diagrams/assets/qv-template.json +1 -1
  42. package/features/mermaid-diagrams/src/mermaid/qv-polish.css +6 -2
  43. package/features/notes/codex-feature.json +1 -0
  44. package/features/quick-chat/src/adapters/codex/renderer.mjs +3 -1
  45. package/package.json +3 -3
  46. package/packages/cli/src/config.mjs +1 -2
  47. package/packages/cli/src/control-server.mjs +1 -1
  48. package/packages/plugin/assets/app.js +35 -35
  49. package/packages/plugin/assets/prompts.js +1 -1
  50. package/packages/runtime-host/src/codex-cdp.mjs +0 -7
  51. package/packages/runtime-host/src/codex-injection.mjs +50 -2114
  52. package/packages/runtime-host/src/codex-runtime.mjs +4 -203
  53. package/packages/runtime-host/src/feature-registry.mjs +10 -91
  54. package/packages/runtime-host/src/main.mjs +0 -1
  55. package/packages/ui/src/native/tokens.mjs +0 -28
  56. package/apps/dashboard/assets/feature-previews.js +0 -206
  57. package/features/images/src/adapters/codex/cloud-import.mjs +0 -52
  58. package/features/images/src/backend/cloud-library.mjs +0 -12
  59. package/features/images/src/chatgpt-image-source-page.mjs +0 -207
  60. package/features/images/src/chatgpt-image-source.mjs +0 -42
  61. package/features/images/src/http-server.mjs +0 -487
  62. package/features/images/src/main.mjs +0 -52
  63. package/features/images/src/save-as.mjs +0 -69
  64. package/features/images/web/app.js +0 -1780
  65. package/features/images/web/i18n.mjs +0 -132
  66. package/features/images/web/index.html +0 -187
  67. package/features/images/web/styles.css +0 -1114
  68. package/packages/runtime-host/src/host-actions.mjs +0 -221
@@ -1,52 +0,0 @@
1
- import { createLibraryApi } from "./library-api.mjs";
2
- import { promptFromConversation } from "./prompt-resolver-source.mjs";
3
-
4
- // 在原生 Renderer 内使用现有登录态;凭据和临时下载地址不跨出 Renderer。
5
- export async function cloudImagesInRenderer(operation, payload, resolvePrompt = promptFromConversation, createApi = createLibraryApi) {
6
- const key = Symbol.for("minecodex.cloud-images.v1");
7
- const state = window[key] ??= { files: new Map(), conversations: new Map() };
8
- const library = state.library ??= createApi();
9
- if (operation === "list") {
10
- if (!payload.cursor) { state.files.clear(); state.conversations.clear(); }
11
- const result = await library.list(payload.cursor, AbortSignal.timeout(25_000));
12
- const files = result.items.filter(file => ["image/png", "image/jpeg", "image/webp"].includes(file.mime_type)
13
- && file.origination_thread_id && file.origination_message_id && !file.trashed_at);
14
- for (const file of files) state.files.set(file.id, file);
15
- return { items: files.map(file => ({ libraryId: file.id, version: `${file.current_version_number}:${file.updated_at}`, name: file.file_name })), cursor: result.cursor ?? null };
16
- }
17
- const file = state.files.get(payload.libraryId);
18
- if (!file) throw new Error("Cloud image must come from the current Library listing");
19
- const threadId = file.origination_thread_id;
20
- if (!state.conversations.has(threadId)) {
21
- const request = library.conversation(threadId,AbortSignal.timeout(25_000)).catch(error=>{state.conversations.delete(threadId);throw error;});
22
- state.conversations.set(threadId, request);
23
- }
24
- const conversation = await state.conversations.get(threadId);
25
- const node = conversation.mapping?.[file.origination_message_id];
26
- const resolved = resolvePrompt(file, conversation);
27
- if (!resolved) return { skipped: true };
28
- const prompt = resolved.text, promptKind = resolved.kind;
29
- const max = 32 * 1024 * 1024;
30
- if (file.file_size_bytes > max) throw new Error("Cloud image exceeds 32 MB");
31
- const imageBlob = await library.download(file, AbortSignal.timeout(60_000));
32
- if (!(imageBlob instanceof Blob) || imageBlob.size > max) throw new Error("Cloud image download returned an invalid image");
33
- const reader = imageBlob.stream().getReader(), chunks = [];
34
- let length = 0;
35
- try {
36
- while (true) {
37
- const {done, value} = await reader.read(); if (done) break;
38
- length += value.length;
39
- if (length > max) throw new Error("Cloud image exceeds 32 MB");
40
- chunks.push(value);
41
- }
42
- } finally { await reader.cancel().catch(() => {}); }
43
- let binary = "";
44
- for (const chunk of chunks) for (let i = 0; i < chunk.length; i += 0x8000) binary += String.fromCharCode(...chunk.subarray(i, i + 0x8000));
45
- return {
46
- bytesBase64: btoa(binary), mimeType: imageBlob.type.split(";")[0] || file.mime_type,
47
- itemId: file.file_id, conversationId: threadId, conversationTitle: conversation.title,
48
- sourceKind: "chat", prompt, promptKind,
49
- cloudLibraryId: file.id, cloudVersion: `${file.current_version_number}:${file.updated_at}`,
50
- createdAt: new Date((node.message.create_time || conversation.create_time) * 1000).toISOString(),
51
- };
52
- }
@@ -1,12 +0,0 @@
1
- import { findCodexModule } from '../../../../packages/codex-adapter/src/renderer/modules.mjs';
2
- import { cloudImagesInRenderer } from "../adapters/codex/cloud-import.mjs";
3
- import { createLibraryApi } from "../adapters/codex/library-api.mjs";
4
- import { promptResolverSource } from "../adapters/codex/prompt-resolver-source.mjs";
5
-
6
- export async function cloudImagesAction(evaluate, operation, payload = {}) {
7
- if (!["list", "import"].includes(operation)) throw new Error("Unknown cloud image operation");
8
- if (payload.cursor != null && (typeof payload.cursor !== "string" || payload.cursor.length > 4096)) throw new Error("Invalid Library cursor");
9
- if (operation === "import" && (typeof payload.libraryId !== "string" || !/^libfile_[\w-]+$/.test(payload.libraryId))) throw new Error("Invalid Library file ID");
10
- return evaluate(`(async()=>{const findCodexModule=${findCodexModule.toString()};
11
- return (${cloudImagesInRenderer.toString()})(${JSON.stringify(operation)},${JSON.stringify(payload)},${promptResolverSource()},${createLibraryApi.toString()});})()`, { timeoutMs: 110_000 });
12
- }
@@ -1,207 +0,0 @@
1
- const MAX_BYTES = 32 * 1024 * 1024;
2
- const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "[::1]"]);
3
- const MARKER_SELECTORS = [
4
- "[data-image-generation-result]",
5
- "[data-generated-image]",
6
- '[data-testid="image-generation-result"]',
7
- '[data-testid="generated-image"]',
8
- '[data-testid="image-generation"]',
9
- ];
10
-
11
- function importUrl() {
12
- try {
13
- const origin = new URL(config?.serviceOrigin);
14
- if (
15
- origin.protocol !== "http:"
16
- || !LOOPBACK_HOSTS.has(origin.hostname)
17
- || origin.username
18
- || origin.password
19
- ) return null;
20
- return new URL("/api/import-generated-image", origin.origin).href;
21
- } catch {
22
- return null;
23
- }
24
- }
25
-
26
- const IMPORT_URL = importUrl();
27
- if (!IMPORT_URL) return () => {};
28
-
29
- function sendImport(payload) {
30
- if (
31
- typeof config?.bindingName === "string"
32
- && typeof config?.bindingToken === "string"
33
- && typeof config?.featureId === "string"
34
- && typeof window[config.bindingName] === "function"
35
- ) {
36
- window[config.bindingName](JSON.stringify({
37
- token: config.bindingToken,
38
- featureId: config.featureId,
39
- requestId: `${payload.itemId}:${Date.now()}`,
40
- action: "import-generated-image",
41
- payload,
42
- }));
43
- return Promise.resolve();
44
- }
45
- return window.fetch(IMPORT_URL, {
46
- method: "POST",
47
- credentials: "omit",
48
- referrerPolicy: "no-referrer",
49
- headers: { "Content-Type": "application/json" },
50
- body: JSON.stringify(payload),
51
- signal,
52
- }).then((result) => {
53
- if (!result.ok) throw new Error(`Images import returned ${result.status}`);
54
- });
55
- }
56
-
57
- function sourceKind() {
58
- const selectedConversation = document.querySelector?.('[role="button"][aria-current="page"]');
59
- const selectedLabel = selectedConversation?.textContent?.trim() ?? "";
60
- if (/work$/i.test(selectedLabel)) return "work";
61
- if (/chat$/i.test(selectedLabel)) return "chat";
62
- const active = Array.from(document.querySelectorAll('[aria-selected="true"], [data-state="active"]'))
63
- .map((element) => `${element.getAttribute("aria-label") ?? ""} ${element.textContent ?? ""}`)
64
- .join(" ");
65
- const context = `${window.location.pathname} ${active}`.toLowerCase();
66
- if (/\bwork\b/.test(context)) return "work";
67
- if (/\bchat\b/.test(context)) return "chat";
68
- return null;
69
- }
70
-
71
- function selectedConversationTitle() {
72
- const label = document.querySelector?.('[role="button"][aria-current="page"]')?.textContent?.trim() ?? "";
73
- return label.replace(/(?:chat|work)$/i, "").trim() || null;
74
- }
75
-
76
- function generatedGalleryMetadata(element) {
77
- const preview = element.closest?.('[data-testid="generated-image-preview"]');
78
- const gallery = preview?.closest?.('[data-testid="generated-image-gallery"]');
79
- if (!gallery) return null;
80
-
81
- const previews = Array.from(gallery.querySelectorAll?.('[data-testid="generated-image-preview"] img') ?? []);
82
- const imageIndex = previews.indexOf(element);
83
- if (imageIndex < 0) return null;
84
-
85
- const fiberKey = Object.getOwnPropertyNames(gallery).find((key) => key.startsWith("__reactFiber$"));
86
- let fiber = fiberKey ? gallery[fiberKey] : null;
87
- for (let depth = 0; fiber && depth < 8; depth += 1, fiber = fiber.return) {
88
- for (const props of [fiber.pendingProps, fiber.memoizedProps]) {
89
- if (!props || !Array.isArray(props.images) || typeof props.conversationId !== "string") continue;
90
- const image = props.images[imageIndex];
91
- const itemId = typeof image?.id === "string" ? image.id : null;
92
- if (!itemId || typeof image.src !== "string" || !image.src.startsWith("sediment://file_")) return null;
93
- return {
94
- itemId,
95
- conversationId: props.conversationId,
96
- conversationTitle: selectedConversationTitle(),
97
- };
98
- }
99
- }
100
- return null;
101
- }
102
-
103
- function closestValue(element, attribute) {
104
- return element.closest?.(`[${attribute}]`)?.getAttribute(attribute)?.trim() || null;
105
- }
106
-
107
- function descriptor(element) {
108
- const source = element.currentSrc || element.src || "";
109
- let protocol;
110
- try { protocol = new URL(source, window.location.href).protocol.slice(0, -1); } catch { protocol = null; }
111
- const gallery = generatedGalleryMetadata(element);
112
- if (gallery && new Set(["https", "blob", "data"]).has(protocol)) {
113
- return {
114
- source,
115
- ...gallery,
116
- sourceKind: sourceKind(),
117
- prompt: null,
118
- createdAt: null,
119
- };
120
- }
121
- const marker = MARKER_SELECTORS.some((selector) => element.matches?.(selector) || element.closest?.(selector));
122
- const assistant = Boolean(element.closest?.('[data-message-author-role="assistant"], [data-message-author-role="tool"]'));
123
- const itemId = element.dataset.imageId
124
- || element.dataset.itemId
125
- || closestValue(element, "data-image-id")
126
- || closestValue(element, "data-item-id")
127
- || closestValue(element, "data-message-id");
128
- if (!marker || !assistant || !itemId || itemId.length > 256 || /[\\/\u0000]/.test(itemId)) return null;
129
- if (!new Set(["https", "blob", "data"]).has(protocol)) return null;
130
- return {
131
- source,
132
- itemId,
133
- sourceKind: sourceKind(),
134
- conversationId: closestValue(element, "data-conversation-id"),
135
- conversationTitle: closestValue(element, "data-conversation-title"),
136
- prompt: element.getAttribute("data-image-prompt") || null,
137
- createdAt: element.getAttribute("data-created-at") || null,
138
- };
139
- }
140
-
141
- function base64(bytes) {
142
- let output = "";
143
- const chunkSize = 0x8000;
144
- for (let offset = 0; offset < bytes.length; offset += chunkSize) {
145
- output += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
146
- }
147
- return window.btoa(output);
148
- }
149
-
150
- async function imagePayload(image, element, signal) {
151
- try {
152
- const response = await window.fetch(image.source, {
153
- credentials: "same-origin",
154
- redirect: "error",
155
- referrerPolicy: "no-referrer",
156
- signal,
157
- });
158
- if (!response.ok) throw new Error(`generated image fetch returned ${response.status}`);
159
- const mimeType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() || null;
160
- if (!mimeType) throw new Error("generated image response has no Content-Type");
161
- const length = Number(response.headers.get("content-length") || 0);
162
- if (length > MAX_BYTES) throw new Error("generated image exceeds the import limit");
163
- return { mimeType, bytes: new Uint8Array(await response.arrayBuffer()) };
164
- } catch (error) {
165
- if (!image.source.startsWith("blob:") || !element?.complete || !element.naturalWidth) throw error;
166
- const canvas = document.createElement("canvas");
167
- canvas.width = element.naturalWidth;
168
- canvas.height = element.naturalHeight;
169
- canvas.getContext("2d").drawImage(element, 0, 0);
170
- const blob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
171
- if (!blob) throw error;
172
- return { mimeType: "image/png", bytes: new Uint8Array(await blob.arrayBuffer()) };
173
- }
174
- }
175
-
176
- async function importImage(image, element, signal) {
177
- const { mimeType, bytes } = await imagePayload(image, element, signal);
178
- if (!bytes.length || bytes.length > MAX_BYTES) throw new Error("generated image exceeds the import limit");
179
- const payload = {
180
- ...image,
181
- mimeType,
182
- bytesBase64: base64(bytes),
183
- };
184
- delete payload.source;
185
- await sendImport(payload);
186
- }
187
-
188
- const seen = new Set();
189
- const inFlight = new Set();
190
- const scan = () => {
191
- if (!sourceKind()) return;
192
- for (const element of document.querySelectorAll("img")) {
193
- const image = descriptor(element);
194
- if (!image || !image.sourceKind || seen.has(image.itemId) || inFlight.has(image.itemId)) continue;
195
- inFlight.add(image.itemId);
196
- void importImage(image, element, signal)
197
- .then(() => seen.add(image.itemId))
198
- .catch(() => {})
199
- .finally(() => inFlight.delete(image.itemId));
200
- }
201
- };
202
-
203
- const observer = new MutationObserver(scan);
204
- observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ["src", "srcset", "data-image-id", "data-item-id"] });
205
- scan();
206
- signal.addEventListener("abort", () => observer.disconnect(), { once: true });
207
- return () => observer.disconnect();
@@ -1,42 +0,0 @@
1
- const SUPPORTED_SURFACES = new Set(["chat", "work"]);
2
-
3
- export function sourceKindForSurface({ pathname = "", activeLabel = "" } = {}) {
4
- const value = `${pathname} ${activeLabel}`.toLowerCase();
5
- if (/\bwork\b/.test(value)) return "work";
6
- if (/\bchat\b/.test(value)) return "chat";
7
- return null;
8
- }
9
-
10
- export function classifyGeneratedImage({
11
- hasGenerationMarker = false,
12
- isAssistantResult = false,
13
- itemId = null,
14
- sourceProtocol = null,
15
- } = {}) {
16
- if (!hasGenerationMarker || !isAssistantResult) return { accepted: false, reason: "NOT_GENERATED_RESULT" };
17
- if (!itemId || typeof itemId !== "string" || itemId.length > 256 || /[\\/\u0000]/.test(itemId)) {
18
- return { accepted: false, reason: "MISSING_STABLE_ITEM_ID" };
19
- }
20
- if (!new Set(["https", "blob", "data"]).has(sourceProtocol)) {
21
- return { accepted: false, reason: "UNSUPPORTED_SOURCE" };
22
- }
23
- return { accepted: true };
24
- }
25
-
26
- export function buildImportMetadata({
27
- sourceKind,
28
- itemId,
29
- conversationId = null,
30
- conversationTitle = null,
31
- prompt = null,
32
- createdAt = null,
33
- } = {}) {
34
- return {
35
- sourceKind: SUPPORTED_SURFACES.has(sourceKind) ? sourceKind : null,
36
- itemId: typeof itemId === "string" ? itemId.slice(0, 256) : null,
37
- conversationId: typeof conversationId === "string" ? conversationId.slice(0, 256) : null,
38
- conversationTitle: typeof conversationTitle === "string" ? conversationTitle.slice(0, 512) : null,
39
- prompt: typeof prompt === "string" ? prompt.slice(0, 8_192) : null,
40
- createdAt: typeof createdAt === "string" ? createdAt.slice(0, 64) : null,
41
- };
42
- }