minecodex 2.0.38 → 2.0.40

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 (42) hide show
  1. package/README.md +1 -0
  2. package/apps/dashboard/assets/preview-content.js +3 -0
  3. package/features/markdown-reading/src/adapters/codex/lists.mjs +0 -26
  4. package/features/markdown-reading/src/adapters/codex/reading-runtime.mjs +5 -3
  5. package/features/markdown-reading/src/adapters/codex/rich-text.mjs +1 -1
  6. package/features/markdown-reading/src/adapters/codex/table-actions.mjs +1 -108
  7. package/features/markdown-reading/src/adapters/codex/toc.mjs +1 -1
  8. package/features/markdown-reading/src/injection-source.mjs +8 -10
  9. package/features/markdown-reading/src/markdown-reading.mjs +5 -2
  10. package/features/mermaid-diagrams/assets/qv-dependencies.js +4412 -0
  11. package/features/mermaid-diagrams/assets/qv-export.js +2 -0
  12. package/features/mermaid-diagrams/assets/qv-loading.css +4 -0
  13. package/features/mermaid-diagrams/assets/qv-template.json +1 -0
  14. package/features/mermaid-diagrams/assets/workflow.svg +15 -0
  15. package/features/mermaid-diagrams/codex-feature.json +31 -0
  16. package/features/mermaid-diagrams/src/adapters/codex/mermaid-viewer.mjs +95 -0
  17. package/features/mermaid-diagrams/src/adapters/codex/mermaid.mjs +91 -0
  18. package/features/mermaid-diagrams/src/adapters/codex/native.mjs +34 -0
  19. package/features/mermaid-diagrams/src/adapters/codex/runtime.mjs +42 -0
  20. package/features/mermaid-diagrams/src/injection-source.mjs +26 -0
  21. package/features/mermaid-diagrams/src/mermaid/export.mjs +83 -0
  22. package/features/mermaid-diagrams/src/mermaid/frame.mjs +109 -0
  23. package/features/mermaid-diagrams/src/mermaid/loading.mjs +27 -0
  24. package/features/mermaid-diagrams/src/mermaid/theme.css +8 -0
  25. package/features/mermaid-diagrams/src/mermaid/viewport.mjs +75 -0
  26. package/features/mermaid-diagrams/src/mermaid-diagrams.mjs +85 -0
  27. package/features/mermaid-diagrams/vendor/lucide-LICENSE +43 -0
  28. package/features/mermaid-diagrams/vendor/qv/LICENSE +21 -0
  29. package/features/mermaid-diagrams/vendor/qv/THIRD_PARTY_NOTICES.txt +3009 -0
  30. package/features/mermaid-diagrams/vendor/qv/provenance.json +55 -0
  31. package/features/usage-meter/src/adapters/codex/renderer.mjs +11 -3
  32. package/features/usage-meter/src/usage-meter.mjs +19 -4
  33. package/package.json +19 -5
  34. package/packages/cli/src/config.mjs +2 -1
  35. package/packages/cli/src/control-server.mjs +1 -1
  36. package/packages/codex-adapter/src/markdown/block-actions.mjs +107 -0
  37. package/packages/codex-adapter/src/markdown/editor-api.mjs +26 -0
  38. package/{features/markdown-reading/src/adapters/codex → packages/codex-adapter/src/markdown}/preview.mjs +2 -8
  39. package/packages/plugin/assets/app.js +2 -2
  40. /package/{features/markdown-reading/src/adapters/codex → packages/codex-adapter/src/markdown}/markdown-native.mjs +0 -0
  41. /package/{features/markdown-reading/src/adapters/codex → packages/codex-adapter/src/markdown}/providers.mjs +0 -0
  42. /package/{features/markdown-reading/src/adapters/codex → packages/codex-adapter/src/markdown}/toc-native.mjs +0 -0
@@ -0,0 +1,55 @@
1
+ {
2
+ "project": "QuickerVisulize",
3
+ "commit": "07005ddce591a0407fca82cc03aa4f5586eac2ba",
4
+ "files": {
5
+ "mermaid-xy-model.js": "764ea99121ec89053f21ce8a9a2fa933f900a1790124e3d6ae06c0f587024b76",
6
+ "mermaid-timeline-model.js": "851c21aa0e79ddd916e5a9d387f7878dae78f160ec7d2c47621901c5ccddcfc8",
7
+ "mermaid-class-model.js": "74a215193bcfbf7052e9ea188223f6468e92924b5c0763d9c8badec8e7f69b8f",
8
+ "mermaid-gantt-model.js": "aebcb753b4f3d128dee6807e0cb6077b92f4b35c5f9da0acdf61b9a43bc30e6b",
9
+ "mermaid-navigation.js": "791a63eeb4e6f0cabbb49ff7ccc13d954124c99a4d3d549ec6176cbdd7e19049",
10
+ "mermaid-presentation.js": "e66a45c6c88427ab1b17bedddd5a799573e458c8ae140f7a1a573de0fc7a8a43",
11
+ "mermaid-c4-layout.js": "d1d1469754fa039b46e414c35567ae452c6c312525b5092c51c170245773b6df",
12
+ "mermaid-kanban-model.js": "6093fd04f24f47ed30114d299623cd0ac50ad35e3a372f77d87eb2651c2d71da",
13
+ "mermaid-timeline.js": "55742310c9a7a532d0bcd0f0939601afea8b21b69f09d3450b74ca093ba1a382",
14
+ "mermaid-journey.js": "edd7aef2357ce4dd8b3aac6e6a7861341d977adc488d11c5b67a261dc49b3a83",
15
+ "mermaid-sequence.js": "bf2417ba3003d1dcdaf7da168aaad21b614366ab2da4158c2cefe281ed298258",
16
+ "mermaid-radar.js": "8d5051fb923b340c0394ed8942830f9e84090fec587540885d23710a199dc5db",
17
+ "mermaid-gantt.js": "e9187bbb16a43ca7846c42c7728e1d5e4e4d09b2183d3b832eaab8825c1a10e8",
18
+ "mermaid-c4.js": "cf871a5082aa6365d837e41d2a47b3e7c4346cb3240b49eb76d119c688a27d8d",
19
+ "mermaid-journey-model.js": "5268939151b31966d553f8941d481142d9c1ae1bdfaacb1ec8de1a521c8d2493",
20
+ "mermaid-layout.js": "01cf444fa3daa2e5eb901363ca43077d02a690d4841530981863be508b302d6c",
21
+ "mermaid-xy.js": "9288bbff0c9937b3654cc7b8790815ed3d663798138a0fe09e71844a1e9c80e8",
22
+ "mermaid-block-model.js": "b0ebd80c20d6353bac2b734fb8bc7f9e53cb335b7c58260b3d41ef1571ba53a0",
23
+ "mermaid-quadrant.js": "097e4702a6856a93c0d13753d22294ed5ac5685670e337f0bcf6b0ba9c2cab96",
24
+ "mermaid-radar-model.js": "259f0f4f04f82198af9f9d2fa8f30088dde9c0d7d7feaac774219a65e2adfd17",
25
+ "mermaid-c4-model.js": "1461030c686649ec44c72d57ec60df7dbef1c556fe2683c36612f2fb7ab58162",
26
+ "mermaid-viewport.js": "2e12ed469a4925f82af8d0bc334a6601d2df48fb68e88ceb7eda0586ed8d0743",
27
+ "mermaid-mindmap.js": "f7d954f9efc0f051ab14cf96e244b2cc3198c992ccdf8a36b8dedf6ae2621288",
28
+ "mermaid-state-model.js": "933ce449000ea83579ef3b96e99e8a345ec2cb5bcfa24344a1ff4aa9f1a8c623",
29
+ "mermaid-model.js": "94c953ded8b60baf4157d8e9a371736d3c8adc7884efd69a7c274b55d90e70f4",
30
+ "mermaid-pie.js": "935d7a6d1ffb312ad5678be6130a19f60976bd0fa66ffd82792673f02c71b6c1",
31
+ "mermaid-class.js": "289a671f265af9c1984ccf9998e638391db2b9bc0b904a64193104d5d7d20042",
32
+ "mermaid-mindmap-model.js": "dde10d6e5a92fe128007311f7a4b370f4fa5a2c0d58e42299dd04dd05fd2e65e",
33
+ "mermaid-architecture-reader.js": "2b9bab5f75dfc0e6c42b09301c6cb40e86a0b27bf5403517de4aaaa14943fc8a",
34
+ "mermaid-pie-model.js": "d0e21d7e00c8ca5fa9b411f0fe0fb774bbbe2c0ba790772b9b92e78ab3eb626b",
35
+ "mermaid-sankey-model.js": "73a34dc038e328e9b5b759818e79275baa52ceed0b74ab4476c96a0df6db5204",
36
+ "mermaid-er.js": "237773cbfec13807e0eb0f844f4bebba429b05cd6b15cc8920387b294ce240bb",
37
+ "mermaid-block-layout.js": "cf1a7143b3c535fa155e51f6e6aaf8f4893de2bb3528729840d9315f6d8fe891",
38
+ "mermaid-block.js": "600a741577cb566039dd2893173f708f5fa6e2c2695799edac6300ece1776cbf",
39
+ "mermaid-kanban.js": "b2c95fc35aaf3226ebc971914bfa62387017a670f54e529d5720ce14dec37574",
40
+ "mermaid-sequence-model.js": "e36cc27cd1580adfe84ff2e9438f3798a7c269c522dc3703c9bcd66ba1fe1e77",
41
+ "mermaid-sankey.js": "86f72f55da3faa0e63b79fc7bce84a91304bf55e9cfb4c86b85e643166447604",
42
+ "mermaid-state.js": "0b9f058c82ac0373b26009a020ba89014f33dd4f6a94678025d63d07a1da2509",
43
+ "mermaid-er-model.js": "a19467729fe8c868b3f03e3ff41140a7f4890f217bc234f436b8f8382086ab16",
44
+ "mermaid-quadrant-model.js": "775448876b802fae5f1148d92ab99c491168be87aea6aecf2a92ce27fbb5c939",
45
+ "mermaid.html": "36d7e6994c8c8eebb13fffe79b67a82cd6d336d66142ee62784cfe8650c0ad9e",
46
+ "donut-chart.html": "70578f946929abfd284e61c64ab06a2812cdc2374bf8cb6a6f0bbb7c0c167363",
47
+ "radar-chart.html": "4f18a77b7ec77c495758177a01c930574aa23c533c588c82ced86719f635790a",
48
+ "chart-loading.html": "700f7879be8e22b82a8d03be7ce9abf188f2db4a71f9a91229d2bb338add5b6b"
49
+ },
50
+ "generated": {
51
+ "chart-presets.js": "mermaid_presets.py compile_presets",
52
+ "loading.mjs": "chart-loading.html dot-field drawing; Preview owns readiness and cleanup"
53
+ },
54
+ "adaptation": "offline dependencies and static motion; model, layout, drawing and presentation retained"
55
+ }
@@ -1,6 +1,6 @@
1
1
  export function createUsageAdapter(signal, codex, invalidate) {
2
2
  let client=null,unsubscribe=null;
3
- function remainingWindows(data) {
3
+ function remainingWindows(data, updatedAt) {
4
4
  const windows = [data?.rate_limit?.primary_window, data?.rate_limit?.secondary_window];
5
5
  const remaining = seconds => {
6
6
  const window = windows.find(window => window?.limit_window_seconds === seconds);
@@ -8,7 +8,15 @@ function remainingWindows(data) {
8
8
  ? Math.round(Math.max(0, Math.min(100, 100 - window.used_percent))) : null;
9
9
  };
10
10
  const weekly = remaining(604800), fiveHour = remaining(18000);
11
- return weekly == null ? null : { weekly, fiveHour, primary: fiveHour ?? weekly };
11
+ const resetAt = seconds => {
12
+ const window = windows.find(window => window?.limit_window_seconds === seconds);
13
+ if (typeof window?.reset_at === "number" && Number.isFinite(window.reset_at) && window.reset_at > 0) return window.reset_at * 1000;
14
+ // 相对秒数绑定缓存更新时间,不能随着每次读取顺延。
15
+ if (typeof window?.reset_after_seconds === "number" && Number.isFinite(window.reset_after_seconds) && window.reset_after_seconds >= 0 && Number.isFinite(updatedAt) && updatedAt > 0) return updatedAt + window.reset_after_seconds * 1000;
16
+ return null;
17
+ };
18
+ return weekly == null ? null : { weekly, fiveHour, primary: fiveHour ?? weekly,
19
+ resetAt: { weekly: resetAt(604800), fiveHour: fiveHour == null ? null : resetAt(18000) } };
12
20
  }
13
21
  function nativeContext(rail) {
14
22
  const result = { auth:null,client:null,router:null };
@@ -27,7 +35,7 @@ function currentWindows(context) {
27
35
  const data = client?.getQueryData(["rate-limit-status"]);
28
36
  // 账号与缓存必须同时一致;旧账号的最后一次响应不能留在导航栏。
29
37
  if (!data || data.user_id !== auth.userId || data.account_id !== auth.accountId) return null;
30
- return remainingWindows(data);
38
+ return remainingWindows(data, client.getQueryState?.(["rate-limit-status"])?.dataUpdatedAt);
31
39
  }
32
40
  function findAvatar(rail) {
33
41
  const buttons = [...(rail?.querySelectorAll("button") ?? [])];
@@ -25,6 +25,8 @@ style.textContent = `
25
25
  #${TIP_ID} dl { display:grid; grid-template-columns:auto auto; gap:2px 16px; margin:0; }
26
26
  #${TIP_ID} dt { text-align:left; }
27
27
  #${TIP_ID} dd { margin:0; text-align:right; font-variant-numeric:tabular-nums; }
28
+ #${TIP_ID} .mcx-usage-group { display:grid; grid-template-columns:subgrid; grid-column:1 / -1; gap:2px 16px; }
29
+ #${TIP_ID} .mcx-usage-group + .mcx-usage-group { border-top:1px solid color-mix(in srgb,var(--mcx-color-border-subtle) 50%,transparent); margin-top:calc(var(--mcx-spacing) * 1); padding-top:calc(var(--mcx-spacing) * 1); }
28
30
  `;
29
31
  document.head.append(style);
30
32
 
@@ -45,6 +47,7 @@ function quotaArc(start, end, clockwise) {
45
47
  }
46
48
 
47
49
  function draw(windows) {
50
+ record.windows = windows;
48
51
  const svg = svgNode("svg", { viewBox: "0 0 32 32", class: "mcx-usage-ring", "aria-hidden": "true" });
49
52
  if (windows.fiveHour != null) {
50
53
  // 左下、右下各留 24° 圆头中心间距;两段都从左下切口扣减。
@@ -74,13 +77,24 @@ function draw(windows) {
74
77
  number.setAttribute("data-digits", String(number.textContent.length));
75
78
  record.button.replaceChildren(svg, number);
76
79
  const list = document.createElement("dl");
77
- const rows = windows.fiveHour == null ? [] : [["5h Limit", windows.fiveHour]];
78
- rows.push(["Weekly Limit", windows.weekly]);
79
- for (const [label, percent] of rows) {
80
+ const rows = windows.fiveHour == null ? [] : [["5h Limit", windows.fiveHour, windows.resetAt?.fiveHour]];
81
+ rows.push(["Weekly Limit", windows.weekly, windows.resetAt?.weekly]);
82
+ for (const [label, percent, resetAt] of rows) {
83
+ const group = document.createElement("div");
84
+ group.className = "mcx-usage-group";
80
85
  const term = document.createElement("dt"), value = document.createElement("dd");
81
86
  term.textContent = label;
82
87
  value.textContent = `${percent}%`;
83
- list.append(term, value);
88
+ group.append(term, value);
89
+ const date = new Date(resetAt);
90
+ if (resetAt != null && Number.isFinite(date.getTime())) {
91
+ const resetTerm = document.createElement("dt"), resetValue = document.createElement("dd");
92
+ resetTerm.textContent = "Reset in";
93
+ const pad = value => String(value).padStart(2, "0");
94
+ resetValue.textContent = `${pad(date.getMonth() + 1)}/${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
95
+ group.append(resetTerm, resetValue);
96
+ }
97
+ list.append(group);
84
98
  }
85
99
  record.tooltip.replaceChildren(list);
86
100
  record.button.setAttribute("aria-label", rows.map(([label, percent]) => `${label} ${percent}%`).join(", "));
@@ -100,6 +114,7 @@ function positionTooltip() {
100
114
  function showTooltip() {
101
115
  clearTimeout(hoverTimer);
102
116
  if (!record) return;
117
+ draw(record.windows);
103
118
  record.tooltip.hidden = false;
104
119
  record.button.setAttribute("aria-describedby", TIP_ID);
105
120
  positionTooltip();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecodex",
3
- "version": "2.0.38",
3
+ "version": "2.0.40",
4
4
  "description": "Lightweight, local-first plugins for the Codex desktop app.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -76,14 +76,21 @@
76
76
  "apps/dashboard/transport.js",
77
77
  "packages/plugin/src",
78
78
  "packages/plugin/assets",
79
- "packages/plugin/plugin.json"
79
+ "packages/plugin/plugin.json",
80
+ "features/mermaid-diagrams/vendor/qv/LICENSE",
81
+ "features/mermaid-diagrams/vendor/qv/provenance.json",
82
+ "features/mermaid-diagrams/vendor/qv/THIRD_PARTY_NOTICES.txt",
83
+ "features/mermaid-diagrams/src",
84
+ "features/mermaid-diagrams/assets",
85
+ "features/mermaid-diagrams/codex-feature.json",
86
+ "features/mermaid-diagrams/vendor/lucide-LICENSE"
80
87
  ],
81
88
  "engines": {
82
89
  "node": ">=22.5.0"
83
90
  },
84
91
  "scripts": {
85
92
  "start": "node packages/cli/bin/mcx.mjs serve",
86
- "test": "node --test packages/cli/test/*.test.mjs packages/runtime-host/test/*.test.mjs features/model-slider/test/*.test.mjs features/notes/test/*.test.mjs features/images/test/*.test.mjs features/hide-upsell-banner/test/*.test.mjs features/session-status/test/*.test.mjs features/usage-meter/test/*.test.mjs apps/dashboard/test/*.test.mjs features/unread-inbox/test/*.test.mjs features/quick-chat/test/*.test.mjs features/sidebar-focus/test/*.test.mjs features/sites/test/*.test.mjs packages/ui/test/*.test.mjs packages/codex-adapter/test/*.test.mjs features/markdown-reading/test/*.test.mjs features/file-navigation/test/*.test.mjs packages/plugin/test/*.test.mjs",
93
+ "test": "node --test packages/cli/test/*.test.mjs packages/runtime-host/test/*.test.mjs features/model-slider/test/*.test.mjs features/notes/test/*.test.mjs features/images/test/*.test.mjs features/hide-upsell-banner/test/*.test.mjs features/session-status/test/*.test.mjs features/usage-meter/test/*.test.mjs apps/dashboard/test/*.test.mjs features/unread-inbox/test/*.test.mjs features/quick-chat/test/*.test.mjs features/sidebar-focus/test/*.test.mjs features/sites/test/*.test.mjs packages/ui/test/*.test.mjs packages/codex-adapter/test/*.test.mjs features/markdown-reading/test/*.test.mjs features/file-navigation/test/*.test.mjs packages/plugin/test/*.test.mjs features/mermaid-diagrams/test/*.test.mjs",
87
94
  "test:cli": "node --test packages/cli/test/*.test.mjs",
88
95
  "test:runtime": "node --test packages/runtime-host/test/*.test.mjs",
89
96
  "check": "node scripts/check-syntax.mjs",
@@ -94,7 +101,8 @@
94
101
  "test:ui": "node --test packages/ui/test/*.test.mjs",
95
102
  "build:icons": "node scripts/build-brand-icons.mjs",
96
103
  "build:plugin": "node scripts/build-plugin.mjs",
97
- "test:plugin": "node --test packages/plugin/test/*.test.mjs"
104
+ "test:plugin": "node --test packages/plugin/test/*.test.mjs",
105
+ "build:mermaid": "node scripts/build-qv-mermaid.mjs"
98
106
  },
99
107
  "repository": {
100
108
  "type": "git",
@@ -122,6 +130,12 @@
122
130
  "zod": "^4.6.5"
123
131
  },
124
132
  "devDependencies": {
125
- "esbuild": "^0.28.2"
133
+ "d3": "7.9.0",
134
+ "d3-sankey": "0.12.3",
135
+ "elkjs": "0.10.0",
136
+ "esbuild": "^0.28.2",
137
+ "html-to-image": "1.11.13",
138
+ "lucide": "0.468.0",
139
+ "mermaid": "11.12.0"
126
140
  }
127
141
  }
@@ -2,7 +2,7 @@ import { randomBytes } from "node:crypto";
2
2
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
 
5
- export const FEATURE_IDS = Object.freeze(["model-slider", "notes", "images", "hide-upsell-banner", "session-status", "usage-meter", "sidebar-focus", "sites", "markdown-reading", "file-navigation", "quick-chat"]);
5
+ export const FEATURE_IDS = Object.freeze(["model-slider", "notes", "images", "hide-upsell-banner", "session-status", "usage-meter", "sidebar-focus", "sites", "markdown-reading", "mermaid-diagrams", "file-navigation", "quick-chat"]);
6
6
  export const DEFAULT_CONFIG = Object.freeze({
7
7
  schemaVersion: 1,
8
8
  features: Object.freeze({
@@ -15,6 +15,7 @@ export const DEFAULT_CONFIG = Object.freeze({
15
15
  "sidebar-focus": true,
16
16
  sites: true,
17
17
  "markdown-reading": true,
18
+ "mermaid-diagrams": false,
18
19
  "file-navigation": true,
19
20
  "quick-chat": true,
20
21
  }),
@@ -14,7 +14,7 @@ import { createDataTransfer, MAX_TRANSFER_BYTES } from "./data-transfer/service.
14
14
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
15
15
  const packageMetadata = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8"));
16
16
  const MAX_BODY_BYTES = 64 * 1024;
17
- const DASHBOARD_FEATURE_ORDER = ["model-slider", "notes", "usage-meter", "session-status", "markdown-reading", "file-navigation", "images", "sites", "quick-chat", "sidebar-focus", "hide-upsell-banner"];
17
+ const DASHBOARD_FEATURE_ORDER = ["model-slider", "notes", "usage-meter", "session-status", "markdown-reading", "mermaid-diagrams", "file-navigation", "images", "sites", "quick-chat", "sidebar-focus", "hide-upsell-banner"];
18
18
  const CONTENT_TYPES = new Map([
19
19
  [".css", "text/css; charset=utf-8"],
20
20
  [".html", "text/html; charset=utf-8"],
@@ -0,0 +1,107 @@
1
+ import { loadNativeMarkdownRenderer } from './markdown-native.mjs';
2
+
3
+ export function markdownExportAlias(source, name) {
4
+ const start = source.lastIndexOf('export{');
5
+ if (start < 0) return undefined;
6
+ const entries = source.slice(start + 7).split('}')[0].split(',');
7
+ for (const entry of entries) {
8
+ const [local, alias = local] = entry.trim().split(/\s+as\s+/);
9
+ if (local === name) return alias;
10
+ }
11
+ return undefined;
12
+ }
13
+
14
+ // 从 Chat Markdown 的公开组件发现悬浮操作栏和控件,不绑定构建 hash。
15
+ export async function loadNativeMarkdownTableActions(native) {
16
+ const url = [...document.querySelectorAll('link[rel="modulepreload"],script[src]')]
17
+ .map((n) => n.href || n.src)
18
+ .find((url) => /\/app-initial-[^/]+\.js/.test(url));
19
+ const response = await fetch(url);
20
+ if (!response.ok) throw new Error('native-table-actions-source-unavailable');
21
+ const source = await response.text();
22
+ const section = (marker) => {
23
+ const at = source.indexOf(marker),
24
+ start = source.lastIndexOf('function ', at),
25
+ end = source.indexOf('function ', at);
26
+ if (at < 0 || start < 0 || end < 0) throw new Error('native-table-actions-contract-changed');
27
+ return source.slice(start, end);
28
+ };
29
+ const actions = section('"data-block-actions":!0'),
30
+ table = section('id:`markdown.expandTable`');
31
+ const exported = source.slice(source.lastIndexOf('export{'));
32
+ const module = await import(url);
33
+ const publicBinding = (name) => {
34
+ const alias = markdownExportAlias(exported, name);
35
+ if (!alias || !module[alias]) throw new Error('native-table-actions-export-unavailable');
36
+ return module[alias];
37
+ };
38
+ // 初始化原生 lazy module,再使用其公开 React 组件。
39
+ const initializer = source
40
+ .slice(source.indexOf(table) + table.length)
41
+ .match(/^function ([\w$]+)\(\)\{return\(\1=/)?.[1];
42
+ if (!initializer) throw new Error('native-table-actions-initializer-unavailable');
43
+ publicBinding(initializer)();
44
+ const imports = new Map();
45
+ for (const match of source.matchAll(/import\{([^}]+)\}from["']([^"']+)["']/g))
46
+ for (const entry of match[1].split(',')) {
47
+ const parts = entry.trim().split(/\s+as\s+/);
48
+ imports.set(parts.at(-1), { name: parts[0], url: new URL(match[2], url).href });
49
+ }
50
+ const binding = async (name) => {
51
+ const item = imports.get(name);
52
+ if (!item) throw new Error('native-table-actions-dependency-unavailable');
53
+ return (await import(item.url))[item.name];
54
+ };
55
+ const sharedUrl = [...imports.values()].find((item) => /\/app-shared-[^/]+\.js/.test(item.url))?.url;
56
+ if (!sharedUrl) throw new Error('native-code-wrap-module-unavailable');
57
+ const sharedSource = await (await fetch(sharedUrl)).text(),
58
+ shared = await import(sharedUrl);
59
+ const wrapAt = sharedSource.indexOf('id:`codeSnippet.wrap.disable`');
60
+ const wrapSection = sharedSource.slice(
61
+ sharedSource.lastIndexOf('function ', wrapAt),
62
+ sharedSource.indexOf('function ', wrapAt),
63
+ );
64
+ const wrapPair = wrapSection.match(
65
+ /children:[\w$]+\?\(0,[\w$]+\.jsx\)\(([\w$]+),\{\}\):\(0,[\w$]+\.jsx\)\(([\w$]+),\{\}\)/,
66
+ );
67
+ if (wrapAt < 0 || !wrapPair) throw new Error('native-code-wrap-icons-unavailable');
68
+ const sharedExports = sharedSource.slice(sharedSource.lastIndexOf('export{'));
69
+ const sharedBinding = (name) => shared[markdownExportAlias(sharedExports, name)];
70
+ const assignment = sharedSource.indexOf(`${wrapPair[1]}=`);
71
+ const initialize = sharedSource
72
+ .slice(sharedSource.lastIndexOf('function ', assignment))
73
+ .match(/^function ([\w$]+)\(\)/)?.[1];
74
+ if (initialize && typeof sharedBinding(initialize) === 'function') sharedBinding(initialize)();
75
+ const wrapIcons = { disable: sharedBinding(wrapPair[1]), enable: sharedBinding(wrapPair[2]) };
76
+ if (!wrapIcons.disable || !wrapIcons.enable) throw new Error('native-code-wrap-icon-exports-unavailable');
77
+ const button = table.match(/jsx\)\(([\w$]+),\{"aria-label":[\w$]+,"aria-expanded"/)?.[1];
78
+ const copy = table.match(/jsx\)\(([\w$]+),\{ariaLabel:[\w$]+,buttonText:/)?.[1];
79
+ const tooltip = table.match(/jsx\)\(([\w$]+),\{tooltipContent:[\w$]+,delayOpen:!0/)?.[1];
80
+ if (!button || !copy || !tooltip) throw new Error('native-table-actions-controls-unavailable');
81
+ const [Button, CopyButton, Tooltip] = await Promise.all([binding(button), binding(copy), binding(tooltip)]);
82
+ const previewName = table.match(/jsx\)\(([\w$]+),\{src:``,alt:/)?.[1];
83
+ const tableName = table.match(/jsx\)\(([\w$]+),\{className:[\w$]+,children:[\w$]+\}/)?.[1];
84
+ const classesName = table.match(/([\w$]+)\.MarkdownTablePreview/)?.[1];
85
+ const icon = table.match(/children:\(0,[\w$]+\.jsx\)\(([\w$]+),\{icon:\{16:([\w$]+),20:([\w$]+)\}\}\)/);
86
+ if (!previewName || !tableName || !classesName || !icon)
87
+ throw new Error('native-table-preview-contract-unavailable');
88
+ const [Icon, small, medium, renderer] = await Promise.all([
89
+ binding(icon[1]),
90
+ binding(icon[2]),
91
+ binding(icon[3]),
92
+ loadNativeMarkdownRenderer(source, url, module),
93
+ ]);
94
+ return {
95
+ ...native,
96
+ ...renderer,
97
+ TablePreview: publicBinding(previewName),
98
+ Table: publicBinding(tableName),
99
+ tableClasses: publicBinding(classesName),
100
+ ExpandIcon: () => native.React.createElement(Icon, { icon: { 16: small, 20: medium } }),
101
+ Actions: publicBinding(actions.match(/^function ([\w$]+)/)[1]),
102
+ Button,
103
+ wrapIcons,
104
+ CopyButton,
105
+ Tooltip,
106
+ };
107
+ }
@@ -0,0 +1,26 @@
1
+ // 使用当前编辑器所属的原生 CodeMirror 实例,避免引入第二套编辑器依赖。
2
+ export async function loadMarkdownListAPI(view) {
3
+ const links = [...document.querySelectorAll('link[rel="modulepreload"]')];
4
+ for (const link of links) {
5
+ if (!/\/dist-[\w-]+\.js$/.test(link.href)) continue;
6
+ const exports = Object.values(await import(link.href));
7
+ if (!exports.includes(view.constructor)) continue;
8
+ const Decoration = exports.find((value) => value?.line && value?.replace && value?.mark);
9
+ const ViewPlugin = exports.find((value) => value?.fromClass && value?.define);
10
+ const StateEffect = exports.find((value) => value?.appendConfig && value?.reconfigure);
11
+ const Compartment = exports.find(
12
+ (value) => value?.prototype?.reconfigure && value?.prototype?.of && value?.prototype?.get,
13
+ );
14
+ const WidgetType = exports.find(
15
+ (value) => value?.prototype?.eq && value?.prototype?.ignoreEvent && value?.prototype?.updateDOM,
16
+ );
17
+ const StateField = exports.find(
18
+ (value) =>
19
+ value?.define && value?.prototype?.slot && value?.prototype?.init && value?.prototype?.create,
20
+ );
21
+ const Prec = exports.find((value) => value?.highest && value?.lowest && value?.high);
22
+ if (Decoration && ViewPlugin && StateEffect && Compartment && WidgetType && Prec && StateField)
23
+ return { Decoration, ViewPlugin, StateEffect, Compartment, WidgetType, Prec, StateField };
24
+ }
25
+ throw new Error('native-markdown-list-api-unavailable');
26
+ }
@@ -1,8 +1,5 @@
1
- import { createMarkdownReadingRuntime } from './reading-runtime.mjs';
2
-
3
1
  // 当前原生 Markdown Preview 是 CodeMirror 视图;Source 与 Pages 不属于此契约。
4
- export function createMarkdownPreviewAdapter(codex) {
5
- let runtime;
2
+ export function createMarkdownPreviewAdapter(codex, attach) {
6
3
  function preview(editor) {
7
4
  if (!editor.isConnected || !editor.closest('[data-editor-search-surface]')) return null;
8
5
  const content = editor.querySelector('.cm-content[data-language="markdown"]');
@@ -30,9 +27,6 @@ export function createMarkdownPreviewAdapter(codex) {
30
27
  return {
31
28
  preview,
32
29
  previews,
33
- attach(item) {
34
- runtime ??= createMarkdownReadingRuntime(codex);
35
- return runtime.attach(item);
36
- },
30
+ attach(item) { return attach(item); },
37
31
  };
38
32
  }