minecodex 2.0.51 → 2.0.53

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 (30) hide show
  1. package/features/composer-expand/src/injection-source.mjs +3 -3
  2. package/features/composer-expand/src/native.mjs +23 -1
  3. package/features/file-navigation/src/adapters/codex/modules.mjs +23 -2
  4. package/features/file-navigation/src/injection-source.mjs +3 -3
  5. package/features/images/src/adapters/codex/native-image-modules.mjs +11 -4
  6. package/features/images/src/adapters/codex/rspack-modules.mjs +148 -0
  7. package/features/images/src/library-prompt-source.mjs +3 -2
  8. package/features/markdown-reading/src/adapters/codex/links.mjs +10 -1
  9. package/features/markdown-reading/src/adapters/codex/rich-text.mjs +12 -0
  10. package/features/markdown-reading/src/injection-source.mjs +6 -4
  11. package/features/mermaid-diagrams/src/adapters/codex/native.mjs +15 -0
  12. package/features/mermaid-diagrams/src/injection-source.mjs +6 -5
  13. package/features/model-slider/src/adapters/codex/controller.mjs +57 -2
  14. package/features/model-slider/src/frontend/install.mjs +49 -43
  15. package/features/quick-chat/src/adapters/codex/rspack.mjs +50 -0
  16. package/features/quick-chat/src/adapters/codex/shortcut.mjs +8 -1
  17. package/features/quick-chat/src/adapters/codex/sidebar.mjs +5 -1
  18. package/features/quick-chat/src/injection-source.mjs +3 -2
  19. package/features/session-status/src/session-status.mjs +9 -3
  20. package/features/sites/src/adapters/codex/renderer.mjs +2 -2
  21. package/package.json +1 -1
  22. package/packages/cli/src/control-server.mjs +4 -5
  23. package/packages/cli/src/npm-adapter.mjs +4 -4
  24. package/packages/codex-adapter/src/markdown/block-actions.mjs +4 -1
  25. package/packages/codex-adapter/src/markdown/editor-api.mjs +26 -1
  26. package/packages/codex-adapter/src/markdown/rspack.mjs +74 -0
  27. package/packages/codex-adapter/src/markdown/toc-native.mjs +4 -1
  28. package/packages/codex-adapter/src/renderer/modules.mjs +79 -0
  29. package/packages/plugin/assets/app.js +2 -2
  30. package/packages/plugin/assets/prompts.js +1 -1
@@ -1,8 +1,8 @@
1
- import { findCodexModule } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
1
+ import { findCodexModule, codexModuleRegistry } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
2
2
  import {readFile} from 'node:fs/promises';
3
- import {loadComposerExpandControls,createComposerExpandAdapter} from './native.mjs';
3
+ import {loadComposerExpandControls,loadRspackComposerExpandControls,createComposerExpandControls,createComposerExpandAdapter} from './native.mjs';
4
4
  import {composerCanExpand,installComposerExpand} from './install.mjs';
5
5
  export async function createPageScriptSource(){
6
- return [findCodexModule, loadComposerExpandControls,createComposerExpandAdapter,composerCanExpand,installComposerExpand]
6
+ return [findCodexModule,codexModuleRegistry,loadRspackComposerExpandControls,createComposerExpandControls,loadComposerExpandControls,createComposerExpandAdapter,composerCanExpand,installComposerExpand]
7
7
  .map(fn=>`const ${fn.name}=${fn.toString()};`).join('\n')+'\n'+await readFile(new URL('./entry.mjs',import.meta.url),'utf8');
8
8
  }
@@ -1,6 +1,8 @@
1
- import { findCodexModule } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
1
+ import { findCodexModule, codexModuleRegistry } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
2
2
  export async function loadComposerExpandControls(signal) {
3
3
  signal.throwIfAborted();
4
+ const registry=await codexModuleRegistry({read:async url=>{const response=await fetch(url,{signal});if(!response.ok)throw new Error('composer-expand-source-unavailable');return response.text();}});
5
+ if (registry) return loadRspackComposerExpandControls(registry);
4
6
  const initialUrl=await findCodexModule('app-initial',{signal,waitForEntry:true});
5
7
  const mainUrl=await findCodexModule('app-main',{signal,waitForEntry:true});
6
8
  if (!initialUrl || !mainUrl) throw new Error('composer-expand-modules-unavailable');
@@ -62,6 +64,26 @@ export async function loadComposerExpandControls(signal) {
62
64
  binding(initialSource,initialUrl,transactionObserver),
63
65
  ]);
64
66
  const React=reactFactory(),ReactDOM=rootFactory();
67
+ return createComposerExpandControls({React,ReactDOM,Button,Collapse,Expand,subscribe});
68
+ }
69
+
70
+ export function loadRspackComposerExpandControls(registry) {
71
+ const owner=registry.module(source=>/["']data-composer-expand-toggle["']:/.test(source),'Composer expand control');
72
+ const marker=owner.source.search(/["']data-composer-expand-toggle["']:/);
73
+ const start=owner.source.lastIndexOf('function ',marker);
74
+ const control=owner.source.slice(start,owner.source.indexOf('}function ',marker)+1);
75
+ const button=control.match(/\.jsx\)\(([\w$]+\.[\w$]+),\{"aria-label"/)?.[1];
76
+ const icons=control.match(/children:[\w$]+\?\(0,[\w$]+\.jsx\)\(([\w$]+\.[\w$]+),\{\}\):\(0,[\w$]+\.jsx\)\(([\w$]+\.[\w$]+),\{\}\)/);
77
+ const react=control.match(/\(0,([\w$]+)\.useContext\)/)?.[1];
78
+ if (!button || !icons || !react) throw new Error('composer-expand-control-contract-changed');
79
+ const roots=[...new Set(registry.entries.filter(entry=>/\.createRoot=/.test(entry.source)).map(entry=>registry.exports(entry)).filter(value=>typeof value.createRoot==='function'))];
80
+ if (roots.length!==1) throw new Error('composer-expand-react-root-unavailable');
81
+ const observer=registry.module(source=>source.includes('prosemirrorDispatchTransaction')&&source.includes('eventTarget:'),'Composer transaction observer');
82
+ const subscribe=registry.value(observer,(value,source)=>typeof value==='function'&&source.includes('eventTarget:')&&source.includes('.addEventListener('),'Composer transaction subscription');
83
+ return createComposerExpandControls({React:registry.binding(owner,react),ReactDOM:roots[0],Button:registry.binding(owner,button),Collapse:registry.binding(owner,icons[1]),Expand:registry.binding(owner,icons[2]),subscribe});
84
+ }
85
+
86
+ export function createComposerExpandControls({React,ReactDOM,Button,Collapse,Expand,subscribe}) {
65
87
  return {subscribe:(view,listener)=>subscribe(view,changed=>{if(changed)listener();}),mount(container,onToggle) {
66
88
  const root=ReactDOM.createRoot(container);
67
89
  let previous;
@@ -1,12 +1,15 @@
1
1
  import { loadInlineFileNavigationModules } from './inline-modules.mjs';
2
- import { findCodexModule } from '../../../../../packages/codex-adapter/src/renderer/modules.mjs';
2
+ import { findCodexModule, codexModuleRegistry } from '../../../../../packages/codex-adapter/src/renderer/modules.mjs';
3
3
  // 依照原生 breadcrumb 的依赖角色查找公开模块,不绑定产物 hash 或短变量名。
4
4
  export async function loadFileNavigationModules({read = async url => { const response = await fetch(url); if (!response.ok) throw new Error('file-navigation-source-unavailable'); return response.text(); }, load = url => import(url)} = {}) {
5
+ const registry=await codexModuleRegistry({read,load});
6
+ let resolved;
7
+ if (registry) resolved=loadRspackFileNavigationModules(registry);
8
+ else {
5
9
  const [breadcrumbUrl,mainUrl]=await Promise.all([
6
10
  findCodexModule('review-file-source-breadcrumb',{read}),findCodexModule('app-main',{read}),
7
11
  ]);
8
12
  if (!mainUrl) throw new Error('file-navigation-modules-unavailable');
9
- let resolved;
10
13
  if (!breadcrumbUrl) {
11
14
  const initialUrl=await findCodexModule('app-initial',{read});
12
15
  if (!initialUrl) throw new Error('file-navigation-modules-unavailable');
@@ -62,6 +65,7 @@ export async function loadFileNavigationModules({read = async url => { const res
62
65
  // 分列导航没有纵向展开状态;通过原生样式入口使用固定的原生文件夹图标。
63
66
  resolved={Tree,React,ReactDOM,request,folder};
64
67
  }
68
+ }
65
69
  const {Tree,React,ReactDOM,request,folder}=resolved;
66
70
  const folderImage = `url("data:image/svg+xml,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${folder.canvas.viewBox}">${folder.body}</svg>`)}")`;
67
71
  const columnStyles = `
@@ -76,3 +80,20 @@ export async function loadFileNavigationModules({read = async url => { const res
76
80
  `;
77
81
  return { Tree, React, ReactDOM, request, columnStyles };
78
82
  }
83
+
84
+
85
+ export function loadRspackFileNavigationModules(registry) {
86
+ const owner=registry.module(source=>/contextMenuEnabled:[\w$]+,decorationAlignment:/.test(source),'file tree');
87
+ const Tree=registry.value(owner,(value,source)=>typeof value==='function'&&source.includes('initialExpandedPaths:')&&source.includes('onSelectionChange:')&&source.includes('contextMenuEnabled:'),'file tree component');
88
+ const react=registry.sourceOf(Tree).match(/\(0,([\w$]+)\.useState\)/)?.[1];
89
+ if (!react) throw new Error('file-navigation-react-unavailable');
90
+ const React=registry.binding(owner,react);
91
+ const roots=[...new Set(registry.entries.filter(entry=>/\.createRoot=/.test(entry.source)).map(entry=>registry.exports(entry)).filter(value=>typeof value.createRoot==='function'))];
92
+ if (roots.length!==1 || typeof React.createElement!=='function') throw new Error('file-navigation-react-root-unavailable');
93
+ const directory=registry.module(source=>source.includes('workspace-directory-entries')&&source.includes('.workspaceFiles')&&source.includes('.listDirectory('),'workspace directory');
94
+ const list=registry.value(directory,(value,source)=>typeof value==='function'&&source.includes('.listDirectory(')&&source.includes('.throwIfAborted('),'workspace directory request');
95
+ const icon=registry.module(source=>source.includes('name:"folder-light-16"'),'folder icon');
96
+ const folder=registry.value(icon,value=>value?.name==='folder-light-16','folder icon');
97
+ if (!folder?.canvas?.viewBox || typeof folder.body!=='string') throw new Error('file-navigation-folder-icon-unavailable');
98
+ return {Tree,React,ReactDOM:roots[0],folder,request:(_method,{params,signal})=>list(params,signal)};
99
+ }
@@ -1,11 +1,11 @@
1
1
  import { loadInlineFileNavigationModules } from './adapters/codex/inline-modules.mjs';
2
- import { findCodexModule } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
2
+ import { findCodexModule, codexModuleRegistry } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
3
3
  import { readFile } from 'node:fs/promises';
4
4
  import { fileNavigationPath, selectFileNavigationDirectory } from './domain/navigation.mjs';
5
- import { loadFileNavigationModules } from './adapters/codex/modules.mjs';
5
+ import { loadFileNavigationModules,loadRspackFileNavigationModules } from './adapters/codex/modules.mjs';
6
6
  import { createFileNavigationAdapter } from './adapters/codex/navigation.mjs';
7
7
  import { installFileNavigation } from './frontend/navigation.mjs';
8
8
  export async function createPageScriptSource() {
9
- const bindings = [loadInlineFileNavigationModules,findCodexModule, fileNavigationPath, selectFileNavigationDirectory, loadFileNavigationModules, createFileNavigationAdapter, installFileNavigation];
9
+ const bindings = [loadInlineFileNavigationModules,findCodexModule,codexModuleRegistry,loadRspackFileNavigationModules, fileNavigationPath, selectFileNavigationDirectory, loadFileNavigationModules, createFileNavigationAdapter, installFileNavigation];
10
10
  return bindings.map(fn => `const ${fn.name} = ${fn.toString()};`).join('\n') + '\n' + await readFile(new URL('./entry.mjs', import.meta.url), 'utf8');
11
11
  }
@@ -1,4 +1,5 @@
1
- import { findCodexModule } from '../../../../../packages/codex-adapter/src/renderer/modules.mjs';
1
+ import {createRspackImageModules} from './rspack-modules.mjs';
2
+ import { findCodexModule, codexModuleRegistry } from '../../../../../packages/codex-adapter/src/renderer/modules.mjs';
2
3
  // 以调用位置和参数结构识别角色,不绑定压缩后的局部变量名。
3
4
  export function discoverImageActionBinding(source, role) {
4
5
  const patterns = {
@@ -81,6 +82,8 @@ export function createNativeImageModules({read = async url => (await fetch(url))
81
82
 
82
83
  async function bootstrap() {
83
84
  if (!starting) starting = (async () => {
85
+ const registry = await codexModuleRegistry({read, load});
86
+ if (registry) return {rspack:await createRspackImageModules(registry)};
84
87
  const [initialUrl,mainUrl]=await Promise.all([
85
88
  findCodexModule('app-initial',{read}),findCodexModule('app-main',{read}),
86
89
  ]);
@@ -139,7 +142,9 @@ export function createNativeImageModules({read = async url => (await fetch(url))
139
142
 
140
143
  async function gallery() {
141
144
  if (!connecting) connecting = (async () => {
142
- const {React,ReactDOM,module,component,binding,initialUrl,initialSource,initial}=await bootstrap();
145
+ const boot = await bootstrap();
146
+ if (boot.rspack) return boot.rspack.gallery();
147
+ const {React,ReactDOM,module,component,binding,initialUrl,initialSource,initial}=boot;
143
148
  const [masonry,layout,sourceImage,menu,toolbar]=await Promise.all([
144
149
  module("library-masonry"),module("library-item-layout"),module("generated-image-source"),
145
150
  module("generated-image-context-menu"),module("library-selection-toolbar"),
@@ -234,7 +239,9 @@ export function createNativeImageModules({read = async url => (await fetch(url))
234
239
 
235
240
  async function canvas() {
236
241
  if (!canvasConnecting) canvasConnecting = (async () => {
237
- const {module,binding,component,initial}=await bootstrap();
242
+ const boot = await bootstrap();
243
+ if (boot.rspack) return boot.rspack.canvas();
244
+ const {module,binding,component,initial}=boot;
238
245
  const [canvas,canvasLayout,filePreview]=await Promise.all([
239
246
  module("image-side-panel"),module("preview-layout"),module("library-cloud-file-preview"),
240
247
  ]);
@@ -260,5 +267,5 @@ export function createNativeImageModules({read = async url => (await fetch(url))
260
267
  })().catch(error=>{canvasConnecting=null;throw error;});
261
268
  return canvasConnecting;
262
269
  }
263
- return {navigation:async()=>{const {React,ReactDOM}=await bootstrap();return {React,ReactDOM};},gallery,canvas};
270
+ return {navigation:async()=>{const boot=await bootstrap();return boot.rspack ? boot.rspack.navigation() : {React:boot.React,ReactDOM:boot.ReactDOM};},gallery,canvas};
264
271
  }
@@ -0,0 +1,148 @@
1
+ // 只按当前原生模块的角色与真实引用取值;不固定 chunk ID、导出别名或构建 hash。
2
+ export async function createRspackImageModules(registry) {
3
+ const {module, value, binding} = registry;
4
+ const required = (source, pattern, role) => {
5
+ const match = source.match(pattern);
6
+ if (!match) throw new Error(`Native Images contract changed: ${role}`);
7
+ return match[1];
8
+ };
9
+ const fn = (entry, predicate, role) => value(entry, (item, source) => typeof item === 'function' && predicate(source), role);
10
+ const bind = (entry, pattern, role) => binding(entry, required(entry.source, pattern, role));
11
+ const ref = '[\\w$]+(?:\\.[\\w$]+)+';
12
+ const renderRef = (props) => new RegExp(`\\.jsx\\)\\((${ref}),\\{${props}`);
13
+ const masonry = module(source => source.includes('Loading library') && source.includes('animate:'), 'image masonry');
14
+ const React = binding(masonry, required(masonry.source, /\(0,([\w$]+)\.useRef\)/, 'React'));
15
+ const roots = registry.entries.filter(entry => /\.createRoot=/.test(entry.source));
16
+ const rootExports = [...new Set(roots.map(entry => registry.exports(entry)).filter(item => typeof item.createRoot === 'function'))];
17
+ if (rootExports.length !== 1) throw new Error('Native Images React root is ambiguous');
18
+ const [ReactDOM] = rootExports;
19
+ if (!React.createElement || !ReactDOM?.createRoot) throw new Error('Native Images React runtime is unavailable');
20
+ let connecting;
21
+ async function gallery() {
22
+ if (!connecting) connecting = (async () => {
23
+ const toolbar = module(source => source.includes('libraryNext.selection.more'), 'selection toolbar');
24
+ const selection = module(source => source.includes('default:e.MoveLibraryItemsDialog'), 'Library actions');
25
+ const checkbox = module(source => source.includes('onCheckedChange:') && source.includes('viewMode:') && source.includes('cursor-interaction') && source.includes('data-library-selection'), 'image checkbox');
26
+ const menu = module(source => source.includes('getGeneratedImageContextMenuItems:()=>'), 'image actions');
27
+ const favorite = module(source => source.includes('target_ref:') && source.includes('showToast:'), 'Library favorite');
28
+ const favoriteItem = module(source => source.includes('library.favorites.add') && source.includes('assets:') && source.includes('surface:"grid"'), 'favorite icon');
29
+ const dialogs = await registry.lazy(toolbar, 'ConfirmLibraryItemsDialog');
30
+ const move = await registry.lazy(selection, 'MoveLibraryItemsDialog');
31
+ const iconBefore = marker => {
32
+ const position = toolbar.source.indexOf(marker);
33
+ if (position < 0) throw new Error('Native Library toolbar label changed');
34
+ const prefix = toolbar.source.slice(Math.max(0, position - 700), position);
35
+ const matches = [...prefix.matchAll(/\.jsx\)\(([\w$]+\.[\w$]+),\{\}\)/g)];
36
+ if (!matches.length) throw new Error('Native Library toolbar icon changed');
37
+ return React.createElement(binding(toolbar, matches.at(-1)[1]));
38
+ };
39
+ const favoriteAssets = favoriteItem.source.match(/assets:[\s\S]{0,80}?\?\{16:([\w$]+\.[\w$]+),20:([\w$]+\.[\w$]+)\}:\{16:([\w$]+\.[\w$]+),20:([\w$]+\.[\w$]+)\}/);
40
+ if (!favoriteAssets) throw new Error('Native Library favorite icon changed');
41
+ const FavoriteIcon = bind(favoriteItem, /\.jsx\)\(([\w$]+\.[\w$]+),\{icon:\{assets:/, 'favorite icon');
42
+ const assets = favoriteAssets.slice(1).map(reference => binding(favoriteItem, reference));
43
+ const favoriteIcon = saved => React.createElement(FavoriteIcon, {icon:{assets:{16:assets[saved?0:2],20:assets[saved?1:3]}}});
44
+ const folderAssets = toolbar.source.match(/pageMovePending\?[\s\S]+?:\(0,[\w$]+\.jsx\)\(([\w$]+\.[\w$]+),\{icon:\{assets:\{16:([\w$]+\.[\w$]+),20:([\w$]+\.[\w$]+)/);
45
+ if (!folderAssets) throw new Error('Native Library folder icon changed');
46
+ const folderIcon = React.createElement(binding(toolbar, folderAssets[1]), {icon:{assets:{16:binding(toolbar, folderAssets[2]),20:binding(toolbar, folderAssets[3])}}});
47
+ const sourceImage = module(source => source.includes('shouldLoadFileDataUrl:') && source.includes('loadTiming:') && source.includes('downloadSrc:'), 'image resolver');
48
+ const layout = module(source => source.includes('actionIndicator:') && /documentHeader:[\w$]+,header:[\w$]+,overlayHeader:/.test(source), 'image card');
49
+ const range = module(source => source.includes('orderedIds:') && source.includes('extendRange:') && source.length < 1000, 'range selection');
50
+ const uploadModule = module(source => source.includes('uploadChatGptConversationFile:()=>'), 'Library upload');
51
+ const openModule = module(source => source.includes('openImagePreview:()=>'), 'image preview');
52
+ const toolbarClass = required(toolbar.source, /className:"(Toolbar-[\w-]+)"/, 'toolbar class');
53
+ const toolbarPositionClass = `${required(toolbar.source, /"(Container-[\w-]+)"/, 'toolbar container')} fixed`;
54
+ return {
55
+ React, ReactDOM,
56
+ Masonry:fn(masonry, source => /\{items:/.test(source) && /animate:/.test(source), 'masonry'),
57
+ Skeleton:fn(masonry, source => source.includes('square:') && source.includes('Loading library'), 'skeleton'),
58
+ Card:fn(layout, source => source.includes('actionIndicator:') && source.includes('documentHeader:'), 'card'),
59
+ useImage:fn(sourceImage, source => source.includes('shouldLoadFileDataUrl:'), 'image resolver'),
60
+ imageMenu:registry.exports(menu).getGeneratedImageContextMenuItems,
61
+ openImage:registry.exports(openModule).openImagePreview,
62
+ upload:registry.exports(uploadModule).uploadChatGptConversationFile,
63
+ setFavorite:fn(favorite, source => source.includes('showToast:') && source.includes('target_ref:'), 'favorite action'),
64
+ accountAtom:bind(favorite, /accountId:[\w$]+\.get\(([\w$]+\.[\w$]+)\)/, 'account'),
65
+ userAtom:bind(favorite, /userId:[\w$]+\.get\(([\w$]+\.[\w$]+)\)/, 'user'),
66
+ toastAtom:bind(menu, /get\(([\w$]+\.[\w$]+)\)\.danger\(/, 'toast'),
67
+ routerAtom:bind(selection, /get\(([\w$]+\.[\w$]+)\)\.prepareNavigation\(/, 'router'),
68
+ switchMode:bind(selection, /\(0,([\w$]+\.[\w$]+)\)\([\w$]+,[\w$]+\);let [\w$]+=1===/, 'mode switch'),
69
+ createChatDraft:bind(selection, /let [\w$]+=\(0,([\w$]+\.[\w$]+)\)\([\w$]+,\[\]\)/, 'Chat draft'),
70
+ imageFile:bind(menu, /let\{file:[\w$]+\}=\(0,([\w$]+\.[\w$]+)\)\(/, 'image file'),
71
+ attachChatImages:bind(menu, /await\s*\(0,([\w$]+\.[\w$]+)\)\([\w$]+,[\w$]+,\[[\w$]+\],\{isTemporaryChat:/, 'Chat attachment'),
72
+ downloadImage:bind(menu, /await\s*\(0,([\w$]+\.[\w$]+)\)\(\{downloadSrc:/, 'image download'),
73
+ Dropdown:bind(toolbar, renderRef('surface:"opaque"'), 'dropdown'),
74
+ Menu:bind(toolbar, /\.jsx\)\(([\w$]+\.[\w$]+)\.Item,\{/, 'menu'),
75
+ Button:bind(toolbar, renderRef('color:"mediaSolid"'), 'button'),
76
+ MoreIcon:bind(toolbar, /id:"libraryNext.selection.more"[\s\S]+?children:\(0,[\w$]+\.jsx\)\(([\w$]+\.[\w$]+),\{\}\)/, 'more icon'),
77
+ ClearIcon:bind(toolbar, /id:"libraryNext.selection.clear"[\s\S]+?\.jsx\)\(([\w$]+\.[\w$]+),\{\}\)/, 'clear icon'),
78
+ Checkbox:bind(checkbox, renderRef('className:"cursor-interaction"'), 'checkbox'),
79
+ rangeSelection:fn(range, source => source.includes('orderedIds:'), 'range selection'),
80
+ nativeSelectedIds:bind(toolbar, /\.set\(([\w$]+\.[\w$]+),new Set/, 'selected IDs'),
81
+ Dialog:bind(dialogs, renderRef('open:[^,]+,showDialogClose:!1'), 'dialog'),
82
+ DialogContent:bind(dialogs, renderRef('as:"form",onSubmit:'), 'dialog content'),
83
+ ConfirmationContent:registry.exports(dialogs).LibraryItemConfirmationContent,
84
+ MoveDialog:registry.exports(move).MoveLibraryItemsDialog,
85
+ favoriteIcon, toolbarClass, toolbarPositionClass,
86
+ chatIcon:iconBefore('id:"libraryNext.selection.chat"'),
87
+ downloadIcon:iconBefore('id:"libraryNext.actions.download"'),
88
+ deleteIcon:iconBefore('id:"libraryNext.actions.delete"'),
89
+ folderIcon,
90
+ };
91
+ })().catch(error => {connecting=null;throw error;});
92
+ return connecting;
93
+ }
94
+ async function canvas() {
95
+ const preview = module(source => source.includes('homeComposerController:') && source.includes('prepareLocalConversationAttachments:'), 'file preview Composer');
96
+ const references = discoverRspackPreviewBindings(preview.source);
97
+ const composer = Object.fromEntries(Object.entries(references).map(([role, reference]) => [role, binding(preview, reference)]));
98
+ const canvasModule = module(source => /attachmentSrc:[\w$]+,chatGptConversationId:[\w$]+,contextMenuEnabled:/.test(source) && source.includes('onImageEditSubmit:'), 'image canvas');
99
+ const layout = module(source => source.includes('reserveImageComposerSpace:') && source.includes('contentOwnsHeader:') && /composerPresentation:[\w$]+,contentOwnsHeader:/.test(source), 'preview layout');
100
+ const edit = module(source => source.includes('requireDispatchAcceptance:') && source.includes('sourceAttachment:') && source.includes('imageVariant'), 'image edit');
101
+ const portalModule = module(source => source.includes('__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=') && /\.createPortal=function/.test(source), 'React portal');
102
+ const createPortal = registry.exports(portalModule).createPortal;
103
+ if (typeof createPortal !== 'function' || !composer.composerRouteScope?.parent) throw new Error('Native Images canvas contract changed');
104
+ return {...composer, createPortal,
105
+ Canvas:fn(canvasModule, source => /attachmentSrc:[\w$]+,chatGptConversationId:[\w$]+,contextMenuEnabled:/.test(source), 'image canvas'),
106
+ CanvasLayout:fn(layout, source => source.includes('reserveImageComposerSpace:') && source.includes('contentOwnsHeader:'), 'preview layout'),
107
+ editImage:fn(edit, source => source.includes('requireDispatchAcceptance:') && source.includes('sourceAttachment:'), 'image edit'),
108
+ };
109
+ }
110
+ return {navigation:async()=>({React,ReactDOM}),gallery,canvas};
111
+ }
112
+
113
+
114
+ export function discoverRspackPreviewBindings(source) {
115
+ const ref = '[\\w$]+\\.[\\w$]+';
116
+ const requireMatch = (text, pattern) => {
117
+ const match = text.match(pattern);
118
+ if (!match) throw new Error('Native file preview Composer contract changed');
119
+ return match[1];
120
+ };
121
+ const body = marker => {
122
+ const index = source.indexOf(marker);
123
+ if (index < 0) throw new Error('Native file preview Composer contract changed');
124
+ const start = source.lastIndexOf('function ', index), end = source.indexOf('}function ', index);
125
+ return source.slice(start, end < 0 ? source.length : end + 1);
126
+ };
127
+ const scope = body('draftId:'), home = body('homeComposerController:');
128
+ const composer = body('lockedLayout:"single-line"'), local = body('prepareLocalConversationAttachments:');
129
+ const alias = (text, name) => requireMatch(text, new RegExp(`\\b${name}=(${ref})(?=[,;])`));
130
+ const scopes = [...scope.matchAll(/\.jsx\)\(([\w$]+),\{scope:([\w$]+),value:([\w$]+)/g)];
131
+ if (scopes.length !== 2 || alias(scope, scopes[0][1]) !== alias(scope, scopes[1][1])) throw new Error('Native file preview scope providers changed');
132
+ const controller = requireMatch(scope, /composerController:([\w$]+)/);
133
+ const navigate = local.match(/,([\w$]+)\(\(0,([\w$]+\.[\w$]+)\)\([\w$]+\),\{state:/);
134
+ if (!navigate) throw new Error('Native file preview navigation changed');
135
+ return {
136
+ useComposerController:requireMatch(scope, new RegExp(`\\b${controller}=\\(0,(${ref})\\)\\(\\)`)),
137
+ useHomeComposer:requireMatch(home, new RegExp(`\\b[\\w$]+=\\(0,(${ref})\\)\\([\\w$]+\\),[\\w$]+=[\\w$]+\\?\\.composerProps`)),
138
+ Composer:requireMatch(composer, new RegExp(`\\.jsx\\)\\((${ref}),\\{\\.\\.\\.[\\w$]+,ref:[\\w$]+,composerController:`)),
139
+ ComposerSurface:requireMatch(composer, new RegExp(`\\.jsx\\)\\((${ref}),\\{placement:`)),
140
+ ScopeProvider:alias(scope, scopes[0][1]), composerScope:alias(scope, scopes[0][2]), composerRouteScope:alias(scope, scopes[1][2]),
141
+ createComposerValue:requireMatch(scope, new RegExp(`\\b${scopes[0][3]}=\\(0,(${ref})\\)`)),
142
+ FileComposer:requireMatch(local, new RegExp(`\\.jsx\\)\\((${ref}),\\{\\.\\.\\.[\\w$]+,aboveComposerContent:`)),
143
+ fileComposerAvailability:requireMatch(local, new RegExp(`composerModeAvailability:(${ref})`)),
144
+ localAttachmentService:requireMatch(local, new RegExp(`\\(0,(${ref})\\)\\([\\w$]+,[\\w$]+\\)\\.createFileAttachment`)),
145
+ deleteLocalAttachment:requireMatch(local, new RegExp(`\\(0,(${ref})\\)\\([\\w$]+,[\\w$]+,[\\w$]+\\.path\\)`)),
146
+ useFileNavigate:requireMatch(local, new RegExp(`\\b${navigate[1]}=\\(0,(${ref})\\)\\(\\)`)), localThreadRoute:navigate[2],
147
+ };
148
+ }
@@ -1,4 +1,5 @@
1
- import { findCodexModule } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
1
+ import {createRspackImageModules,discoverRspackPreviewBindings} from './adapters/codex/rspack-modules.mjs';
2
+ import { findCodexModule, codexModuleRegistry } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
2
3
  import { createCloudImageActions } from "./frontend/cloud-image-actions.mjs";
3
4
  import { createImageSourcesAdapter } from "./adapters/codex/image-sources.mjs";
4
5
  import { createNativeImageSource } from "./adapters/codex/native-image-source.mjs";
@@ -12,7 +13,7 @@ import { promptResolverSource } from "./adapters/codex/prompt-resolver-source.mj
12
13
  export async function createPageScriptSource() {
13
14
  const source=await readFile(new URL("./library-prompt-page.mjs",import.meta.url),"utf8");
14
15
  const promptGlyphSource=await readFile(new URL("./assets/prompts-menu.svg",import.meta.url),"utf8");
15
- const functions={findCodexModule,discoverImageCanvasComponents,createCloudImageActions,createImageSourcesAdapter,discoverImageActionBinding,discoverFilePreviewComposerBindings,createNativeImageModules,createNativeImageSource,installLocalImageSource,createLibraryApi,createLibraryViewerAdapter};
16
+ const functions={findCodexModule,codexModuleRegistry,createRspackImageModules,discoverRspackPreviewBindings,discoverImageCanvasComponents,createCloudImageActions,createImageSourcesAdapter,discoverImageActionBinding,discoverFilePreviewComposerBindings,createNativeImageModules,createNativeImageSource,installLocalImageSource,createLibraryApi,createLibraryViewerAdapter};
16
17
  const declarations=Object.entries(functions).map(([name,fn])=>`const ${name}=${fn.toString()};`).join("\n");
17
18
  return `${declarations}\nconst promptGlyphSource=${JSON.stringify(promptGlyphSource)};\nconst promptFromConversation=${promptResolverSource()};\n${source}`;
18
19
  }
@@ -1,6 +1,15 @@
1
- import { findCodexModule } from '../../../../../packages/codex-adapter/src/renderer/modules.mjs';
1
+ import { findCodexModule, codexModuleRegistry } from '../../../../../packages/codex-adapter/src/renderer/modules.mjs';
2
2
  // 修补原生 Preview 的链接装饰;源码、语法树与编辑器实例继续归 Codex。
3
3
  export async function loadNativeMarkdownLinks() {
4
+ const registry=await codexModuleRegistry();
5
+ if(registry){
6
+ const owner=registry.module(source=>source.includes('currentFilePath:')&&source.includes('markdown_link_click')&&source.includes('originHostId:'),'Markdown editor links');
7
+ const ref=owner.source.match(/\(0,([\w$]+\.[\w$]+)\)\(\{event:[\w$]+,href:[\w$]+,initiator:"markdown_link_click"/)?.[1];
8
+ if(!ref)throw new Error('native-markdown-link-opener-unavailable');
9
+ const open=registry.binding(owner,ref);
10
+ if(typeof open!=='function')throw new Error('native-markdown-link-opener-unavailable');
11
+ return open;
12
+ }
4
13
  const url = await findCodexModule('text-file-editor-extensions',{via:['text-file-editor-tab-content.electron']});
5
14
  if (!url) throw new Error('native-markdown-link-module-unavailable');
6
15
  const source = await (await fetch(url)).text();
@@ -4,6 +4,7 @@ import { captureMarkdownProviders } from '../../../../../packages/codex-adapter/
4
4
 
5
5
  // 复用消息流的代码组件与 Markdown lexer;文件内容及编辑器仍由 Codex 持有。
6
6
  export async function loadNativeMarkdownRichText(native) {
7
+ if(native.rspack)return loadRspackMarkdownRichText(native);
7
8
  const { sharedUrl } = native;
8
9
  const sharedSource = await (await fetch(sharedUrl)).text(),
9
10
  shared = await import(sharedUrl);
@@ -52,6 +53,17 @@ export async function loadNativeMarkdownRichText(native) {
52
53
  return { ...native, CodeSnippet: sharedBinding(codeName), katex };
53
54
  }
54
55
 
56
+ export async function loadRspackMarkdownRichText(native) {
57
+ const registry=native.rspack;
58
+ const code=registry.module(source=>source.includes('CodeSnippet:()=>')&&source.includes('renderSvgAsImage:'),'code content renderer');
59
+ const CodeSnippet=registry.exports(code).CodeSnippet;
60
+ const editor=registry.module(source=>source.includes('currentFilePath:')&&source.includes('renderToString')&&source.includes('katex:'),'Markdown formula renderer');
61
+ const math=await registry.lazy(editor,'katex');
62
+ const katex=registry.exports(math).katex;
63
+ if(typeof CodeSnippet!=='function'||typeof katex?.renderToString!=='function')throw new Error('native-rich-text-modules-unavailable');
64
+ return {...native,CodeSnippet,katex};
65
+ }
66
+
55
67
  export function installMarkdownRichText(view, api, native, codex, wrapState) {
56
68
  const { Decoration, StateField, StateEffect, Compartment, WidgetType, Prec } = api;
57
69
  const {
@@ -1,5 +1,6 @@
1
- import { findCodexModule, codexModuleStyles } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
2
- import { loadMarkdownListAPI } from '../../../packages/codex-adapter/src/markdown/editor-api.mjs';
1
+ import {loadRspackMarkdownToc,loadRspackMarkdownTableActions,loadRspackMarkdownRenderer} from '../../../packages/codex-adapter/src/markdown/rspack.mjs';
2
+ import { findCodexModule, codexModuleStyles, codexModuleRegistry } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
3
+ import { loadMarkdownListAPI,loadRspackMarkdownListAPI } from '../../../packages/codex-adapter/src/markdown/editor-api.mjs';
3
4
  import { markdownExportAlias, loadNativeMarkdownTableActions } from '../../../packages/codex-adapter/src/markdown/block-actions.mjs';
4
5
  import { loadNativeMarkdownRenderer } from '../../../packages/codex-adapter/src/markdown/markdown-native.mjs';
5
6
  import { collectMarkdownFootnotes } from './domain/footnotes.mjs';
@@ -9,7 +10,7 @@ import {
9
10
  createMarkdownWrapState,
10
11
  installMarkdownMetadataScroll,
11
12
  } from './adapters/codex/reading-runtime.mjs';
12
- import { loadNativeMarkdownRichText, installMarkdownRichText } from './adapters/codex/rich-text.mjs';
13
+ import { loadNativeMarkdownRichText,loadRspackMarkdownRichText, installMarkdownRichText } from './adapters/codex/rich-text.mjs';
13
14
  import { installMarkdownTableActions } from './adapters/codex/table-actions.mjs';
14
15
  import { loadNativeMarkdownLinks, installMarkdownLinks } from './adapters/codex/links.mjs';
15
16
  import { loadNativeMarkdownToc } from '../../../packages/codex-adapter/src/markdown/toc-native.mjs';
@@ -19,7 +20,7 @@ import { installMarkdownTables, markdownTableActionGeometry } from './adapters/c
19
20
  import { installMarkdownLists } from './adapters/codex/lists.mjs';
20
21
  import { createMarkdownPreviewAdapter } from '../../../packages/codex-adapter/src/markdown/preview.mjs';
21
22
  export async function createPageScriptSource() {
22
- const bindings = [findCodexModule, codexModuleStyles,
23
+ const bindings = [findCodexModule, codexModuleStyles,codexModuleRegistry,loadRspackMarkdownListAPI,loadRspackMarkdownToc,loadRspackMarkdownTableActions,loadRspackMarkdownRenderer,
23
24
  collectMarkdownFootnotes,
24
25
  captureMarkdownProviders,
25
26
  createMarkdownReadingRuntime,
@@ -38,6 +39,7 @@ export async function createPageScriptSource() {
38
39
  markdownExportAlias,
39
40
  loadNativeMarkdownTableActions,
40
41
  installMarkdownTableActions,
42
+ loadRspackMarkdownRichText,
41
43
  loadNativeMarkdownRichText,
42
44
  installMarkdownRichText,
43
45
  ];
@@ -3,6 +3,7 @@ import { saveQvDiagram } from '../../mermaid/export.mjs';
3
3
 
4
4
  // 图表下载协议只由实验性功能加载,阅读增强不依赖此能力。
5
5
  export async function loadNativeMermaidActions(native, {read = async url => { const response = await fetch(url); if (!response.ok) throw new Error('native-diagram-source-unavailable'); return response.text(); }, load = url => import(url)} = {}) {
6
+ if (native.rspack) return loadRspackMermaidActions(native);
6
7
  const url = await findCodexModule('app-initial',{read});
7
8
  if (!url) throw new Error('native-diagram-module-unavailable');
8
9
  const source = await read(url);
@@ -41,3 +42,17 @@ export async function loadNativeMermaidActions(native, {read = async url => { co
41
42
  return saveQvDiagram(downloadSrc,saveFile,markdownDownloadsDirectory,suggestedFilename);
42
43
  } };
43
44
  }
45
+
46
+
47
+ export function loadRspackMermaidActions(native) {
48
+ const registry=native.rspack;
49
+ const preview=registry.module(source=>source.includes('imagePreviewDialog.label')&&source.includes('suggestedFilename:')&&source.includes('downloadSrc:'),'diagram preview download');
50
+ const call=preview.source.match(/\(0,([\w$]+\.[\w$]+)\)\(\{downloadSrc:[^}]*suggestedFilename:/);
51
+ const icon=call&&preview.source.slice(call.index).match(/children:\(0,[\w$]+\.jsx\)\(([\w$]+\.[\w$]+),\{className:"icon-xs"\}/)?.[1];
52
+ if (!call || !icon) throw new Error('native-diagram-download-unavailable');
53
+ const downloadImage=registry.binding(preview,call[1]),DownloadIcon=registry.binding(preview,icon);
54
+ if (typeof DownloadIcon!=='function' || typeof downloadImage!=='function'
55
+ || !String(downloadImage).includes('workspaceFiles.saveStream')
56
+ || !String(downloadImage).includes('Failed to download image')) throw new Error('native-diagram-download-unavailable');
57
+ return {...native,DownloadIcon,downloadImage};
58
+ }
@@ -1,9 +1,10 @@
1
- import { findCodexModule, codexModuleStyles } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
1
+ import {loadRspackMarkdownToc,loadRspackMarkdownTableActions,loadRspackMarkdownRenderer} from '../../../packages/codex-adapter/src/markdown/rspack.mjs';
2
+ import { findCodexModule, codexModuleStyles, codexModuleRegistry } from '../../../packages/codex-adapter/src/renderer/modules.mjs';
2
3
  import { readFile } from 'node:fs/promises';
3
4
  import { homedir } from 'node:os';
4
5
  import { join } from 'node:path';
5
6
  import { createMarkdownPreviewAdapter } from '../../../packages/codex-adapter/src/markdown/preview.mjs';
6
- import { loadMarkdownListAPI } from '../../../packages/codex-adapter/src/markdown/editor-api.mjs';
7
+ import { loadMarkdownListAPI,loadRspackMarkdownListAPI } from '../../../packages/codex-adapter/src/markdown/editor-api.mjs';
7
8
  import { captureMarkdownProviders } from '../../../packages/codex-adapter/src/markdown/providers.mjs';
8
9
  import { loadNativeMarkdownToc } from '../../../packages/codex-adapter/src/markdown/toc-native.mjs';
9
10
  import { loadNativeMarkdownRenderer } from '../../../packages/codex-adapter/src/markdown/markdown-native.mjs';
@@ -13,14 +14,14 @@ import { exportQvDiagram, saveQvDiagram } from './mermaid/export.mjs';
13
14
  import { createQvLoading } from './mermaid/loading.mjs';
14
15
  import { createMarkdownMermaidControls } from './adapters/codex/mermaid-viewer.mjs';
15
16
  import { installMarkdownMermaid } from './adapters/codex/mermaid.mjs';
16
- import { loadNativeMermaidActions } from './adapters/codex/native.mjs';
17
+ import { loadNativeMermaidActions, loadRspackMermaidActions } from './adapters/codex/native.mjs';
17
18
  import { createMermaidRuntime } from './adapters/codex/runtime.mjs';
18
19
 
19
20
  export async function createPageScriptSource() {
20
- const bindings = [findCodexModule, codexModuleStyles, createMarkdownPreviewAdapter, loadMarkdownListAPI, captureMarkdownProviders,
21
+ const bindings = [findCodexModule, codexModuleStyles,codexModuleRegistry,loadRspackMarkdownListAPI,loadRspackMarkdownToc,loadRspackMarkdownTableActions,loadRspackMarkdownRenderer, createMarkdownPreviewAdapter, loadMarkdownListAPI, captureMarkdownProviders,
21
22
  loadNativeMarkdownToc, loadNativeMarkdownRenderer, markdownExportAlias, loadNativeMarkdownTableActions,
22
23
  createQvResources, createQvFrame, readQvTheme, exportQvDiagram, saveQvDiagram, createQvLoading,
23
- createMarkdownMermaidControls, installMarkdownMermaid, loadNativeMermaidActions, createMermaidRuntime];
24
+ createMarkdownMermaidControls, installMarkdownMermaid, loadNativeMermaidActions, loadRspackMermaidActions, createMermaidRuntime];
24
25
  const declarations = bindings.map(binding => `const ${binding.name} = ${binding.toString()};`).join('\n');
25
26
  const entry = await readFile(new URL('./mermaid-diagrams.mjs',import.meta.url),'utf8');
26
27
  return `const markdownDownloadsDirectory=${JSON.stringify(join(homedir(),'Downloads'))};\n${declarations}\n${entry}`;
@@ -2,7 +2,12 @@ import { findCodexModule } from '../../../../../packages/codex-adapter/src/rende
2
2
  export function createModelControllerAdapter(codex,comparableModelLabel) {
3
3
  function controllerView(props) {const view=Object.fromEntries(["models","model","reasoningEffort","powerSelections","selectedPowerSelection","fallbackPowerSelection","serviceTierOptions","selectedServiceTier","onSelectModel","onSelectReasoningEffort","onSelectPower","onCommitPower","onSelectServiceTier"].map(key=>[key,props[key]]));codex.report('model-controller',true);return view;}
4
4
  function nativeComposerTriggers() {
5
- return Array.from(document.querySelectorAll("[data-codex-intelligence-trigger]"))
5
+ const native = Array.from(document.querySelectorAll("[data-codex-intelligence-trigger]"));
6
+ // 26.1029 起模型按钮改用 composer 导航锚点,旧属性不再渲染。
7
+ const triggers = native.length
8
+ ? native
9
+ : Array.from(document.querySelectorAll('[data-composer-navigation-target="reasoning"]'));
10
+ return triggers
6
11
  .filter((trigger) => trigger.checkVisibility({ checkVisibilityCSS: true, checkOpacity: true }));
7
12
  }
8
13
  function nativeComposerTrigger() {
@@ -10,12 +15,62 @@ function nativeComposerTrigger() {
10
15
  return triggers.find((trigger) => trigger.getAttribute("data-state") === "open")
11
16
  ?? triggers[0] ?? null;
12
17
  }
18
+ // 26.1029 起模型列表由 modelListConfig.options 提供,档位在 powerSelections。
19
+ function controllerViewFromModelList(props) {
20
+ const config = props.modelListConfig;
21
+ const options = (Array.isArray(config?.options) ? config.options : [])
22
+ .filter((option) => option && typeof option.id === "string" && typeof option.onSelect === "function");
23
+ if (!options.length) return null;
24
+ const power = Array.isArray(props.powerSelections) ? props.powerSelections : [];
25
+ const selectedPower = props.selectedPowerSelection ?? null;
26
+ const model = String(selectedPower?.model ?? options.find((option) => option.selected)?.id ?? "");
27
+ if (!model) return null;
28
+ const models = options.map((option) => ({
29
+ model: String(option.id),
30
+ displayName: String(option.id),
31
+ supportedReasoningEfforts: power
32
+ .filter((entry) => entry?.model === option.id)
33
+ .map((entry) => ({ reasoningEffort: String(entry.reasoningEffort ?? "") }))
34
+ .filter((entry) => entry.reasoningEffort),
35
+ defaultReasoningEffort: "",
36
+ disabled: Boolean(option.disabled),
37
+ }));
38
+ function onSelectModel(id, reasoningEffort) {
39
+ const slug = String(id ?? "");
40
+ const target = power.find((entry) => entry?.model === slug
41
+ && String(entry.reasoningEffort ?? "") === String(reasoningEffort ?? ""));
42
+ if (target && typeof props.onSelectPower === "function") {
43
+ props.onSelectPower(target);
44
+ return;
45
+ }
46
+ options.find((option) => String(option.id) === slug)?.onSelect();
47
+ }
48
+ return controllerView({
49
+ models,
50
+ model,
51
+ reasoningEffort: selectedPower?.reasoningEffort ?? null,
52
+ powerSelections: power,
53
+ selectedPowerSelection: selectedPower,
54
+ fallbackPowerSelection: props.fallbackPowerSelection ?? null,
55
+ serviceTierOptions: props.serviceTierOptions,
56
+ selectedServiceTier: props.selectedServiceTier,
57
+ onSelectModel,
58
+ onSelectPower: props.onSelectPower,
59
+ onCommitPower: props.onCommitPower,
60
+ onSelectServiceTier: props.onSelectServiceTier,
61
+ });
62
+ }
13
63
  function nativeComposerModelController(trigger = nativeComposerTrigger()) {
14
64
  codex.report("model-controller",false,"task-controller-unavailable");
15
65
  let fiber = codex.currentFiber(trigger);
16
66
  let powerController = null;
17
- for (let depth = 0; fiber && depth < 40; depth += 1, fiber = fiber.return) {
67
+ for (let depth = 0; fiber && depth < 60; depth += 1, fiber = fiber.return) {
18
68
  const props = fiber.memoizedProps;
69
+ if (!props || typeof props !== "object") continue;
70
+ if (props.modelListConfig && Array.isArray(props.modelListConfig.options)) {
71
+ const view = controllerViewFromModelList(props);
72
+ if (view) return view;
73
+ }
19
74
  if (
20
75
  Array.isArray(props?.powerSelections)
21
76
  && typeof props.onCommitPower === "function"