minecodex 2.0.1 → 2.0.3

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 (102) hide show
  1. package/DESIGN.md +149 -0
  2. package/apps/dashboard/DESIGN.md +45 -0
  3. package/features/images/DESIGN.md +71 -0
  4. package/features/images/README.md +2 -0
  5. package/features/images/codex-feature.json +2 -1
  6. package/features/images/src/adapters/codex/cloud-import.mjs +52 -0
  7. package/features/images/src/adapters/codex/image-prompt.mjs +22 -0
  8. package/features/images/src/adapters/codex/library-api.mjs +55 -0
  9. package/features/images/src/adapters/codex/library-viewer.mjs +18 -0
  10. package/features/images/src/adapters/codex/prompt-resolver-source.mjs +8 -0
  11. package/features/images/src/backend/cloud-library.mjs +10 -0
  12. package/features/images/src/domain/library-prompt.mjs +7 -0
  13. package/features/images/src/library-prompt-page.mjs +8 -76
  14. package/features/images/src/library-prompt-source.mjs +10 -0
  15. package/features/images/web/app.js +9 -9
  16. package/features/images/web/styles.css +219 -219
  17. package/features/model-slider/DESIGN.md +34 -0
  18. package/features/model-slider/src/adapters/codex/controller.mjs +93 -0
  19. package/features/model-slider/src/domain/model-rules.mjs +143 -0
  20. package/features/model-slider/src/frontend/install.mjs +2110 -0
  21. package/features/model-slider/src/injection-source.mjs +10 -0
  22. package/features/notes/DESIGN.md +19 -15
  23. package/features/notes/README.md +10 -2
  24. package/features/notes/assets/default-prompts.json +170 -0
  25. package/features/notes/assets/dsh-prompt-LICENSE.txt +24 -0
  26. package/features/notes/assets/icons/book-open-nav-fill.svg +1 -0
  27. package/features/notes/codex-feature.json +5 -1
  28. package/features/notes/src/backend/default-prompts.mjs +29 -0
  29. package/features/notes/src/{product.mjs → backend/product.mjs} +3 -1
  30. package/features/notes/src/{quick-prompts-server.mjs → backend/quick-prompts-server.mjs} +2 -2
  31. package/features/notes/src/{quick-prompts-page.mjs → frontend/entry.mjs} +78 -266
  32. package/features/notes/src/frontend/icons.json +13 -0
  33. package/features/notes/src/frontend/styles.css +110 -0
  34. package/features/notes/src/main.mjs +2 -2
  35. package/features/notes/vendor/phosphor-LICENSE +21 -0
  36. package/features/session-status/DESIGN.md +7 -7
  37. package/features/session-status/README.md +4 -0
  38. package/features/session-status/codex-feature.json +1 -1
  39. package/features/session-status/src/adapters/codex/context-usage.mjs +39 -0
  40. package/features/session-status/src/adapters/codex/session-log.mjs +81 -0
  41. package/features/session-status/src/adapters/codex/session-reader.mjs +106 -0
  42. package/features/session-status/src/domain/session-stats.mjs +141 -0
  43. package/features/session-status/src/injection-source.mjs +6 -0
  44. package/features/session-status/src/session-status.mjs +43 -103
  45. package/features/sidebar-focus/DESIGN.md +6 -5
  46. package/features/sidebar-focus/codex-feature.json +2 -1
  47. package/features/sidebar-focus/src/adapters/codex/renderer.mjs +28 -0
  48. package/features/sidebar-focus/src/injection-source.mjs +6 -0
  49. package/features/sidebar-focus/src/sidebar-focus.mjs +19 -66
  50. package/features/sites/DESIGN.md +60 -0
  51. package/features/sites/README.md +27 -0
  52. package/features/sites/codex-feature.json +28 -0
  53. package/features/sites/src/adapters/codex/catalog.mjs +26 -0
  54. package/features/sites/src/adapters/codex/renderer.mjs +83 -0
  55. package/features/sites/src/backend/discovery.mjs +73 -0
  56. package/features/sites/src/backend/project-sites.mjs +34 -0
  57. package/features/sites/src/backend/site-launcher.mjs +54 -0
  58. package/features/sites/src/backend/sites-preview.mjs +53 -0
  59. package/features/sites/src/backend/sites-service.mjs +162 -0
  60. package/features/sites/src/domain/site.mjs +40 -0
  61. package/features/sites/src/frontend/entry.mjs +383 -0
  62. package/features/sites/src/frontend/icons.json +12 -0
  63. package/features/sites/src/frontend/styles.css +72 -0
  64. package/features/sites/src/injection-source.mjs +7 -0
  65. package/features/sites/vendor/lucide-LICENSE +35 -0
  66. package/features/unread-inbox/DESIGN.md +6 -2
  67. package/features/unread-inbox/codex-feature.json +2 -1
  68. package/features/unread-inbox/src/adapters/codex/renderer.mjs +66 -0
  69. package/features/unread-inbox/src/injection-source.mjs +6 -0
  70. package/features/unread-inbox/src/unread-inbox.mjs +36 -91
  71. package/features/usage-meter/DESIGN.md +9 -9
  72. package/features/usage-meter/README.md +2 -1
  73. package/features/usage-meter/codex-feature.json +3 -3
  74. package/features/usage-meter/src/adapters/codex/renderer.mjs +61 -0
  75. package/features/usage-meter/src/injection-source.mjs +6 -0
  76. package/features/usage-meter/src/usage-meter.mjs +27 -66
  77. package/package.json +17 -4
  78. package/packages/cli/src/config.mjs +2 -1
  79. package/packages/codex-adapter/README.md +15 -0
  80. package/packages/codex-adapter/package.json +1 -0
  81. package/packages/codex-adapter/src/renderer/environment.mjs +53 -0
  82. package/packages/runtime-host/README.md +27 -0
  83. package/packages/runtime-host/src/codex-injection.mjs +36 -2393
  84. package/packages/runtime-host/src/codex-runtime.mjs +21 -3
  85. package/packages/runtime-host/src/feature-registry.mjs +28 -3
  86. package/packages/ui/DESIGN.md +31 -0
  87. package/packages/ui/package.json +8 -0
  88. package/packages/ui/src/components/button.mjs +21 -0
  89. package/packages/ui/src/components/composer-menu.mjs +18 -0
  90. package/packages/ui/src/components/dialog.mjs +55 -0
  91. package/packages/ui/src/components/navigation-rail.mjs +77 -0
  92. package/packages/ui/src/components/page.mjs +8 -0
  93. package/packages/ui/src/components/tooltip.mjs +96 -0
  94. package/packages/ui/src/foundation/layers.mjs +14 -0
  95. package/packages/ui/src/foundation/surfaces.mjs +34 -0
  96. package/packages/ui/src/install.mjs +22 -0
  97. package/packages/ui/src/native/navigation-rail.mjs +33 -0
  98. package/packages/ui/src/native/theme.mjs +35 -0
  99. package/packages/{runtime-host/src/codex-design-contract.mjs → ui/src/native/tokens.mjs} +67 -24
  100. package/packages/runtime-host/src/cloud-images.mjs +0 -123
  101. package/packages/runtime-host/src/session-status-reader.mjs +0 -340
  102. /package/features/notes/src/{prompt-schema.mjs → backend/prompt-schema.mjs} +0 -0
@@ -1,154 +1,13 @@
1
- const ICONS = {
2
- prompt:
3
- '<svg\n class="lucide lucide-book-open"\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.1"\n stroke-linecap="round"\n stroke-linejoin="round"\n>\n <path d="M12 7v14" />\n <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" />\n</svg>',
4
- chevron:
5
- '<!-- @license lucide-static v0.577.0 - ISC -->\n<svg\n class="lucide lucide-chevron-down"\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="m6 9 6 6 6-6" />\n</svg>\n',
6
- search:
7
- '<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="M7.32849 1.91016C10.2877 1.91016 12.6867 4.30938 12.6869 7.26855C12.6869 8.57363 12.2192 9.7688 11.4437 10.6982L14.0433 13.2979C14.2481 13.5028 14.2481 13.8351 14.0433 14.04C13.8384 14.245 13.5062 14.2449 13.3011 14.04L10.6957 11.4346C9.77536 12.1793 8.60462 12.627 7.32849 12.627C4.36931 12.6268 1.97009 10.2278 1.97009 7.26855C1.97027 4.30949 4.36942 1.91033 7.32849 1.91016ZM7.32849 2.96094C4.94932 2.96111 3.02105 4.88939 3.02087 7.26855C3.02087 9.64787 4.94921 11.577 7.32849 11.5771C9.70792 11.5771 11.6371 9.64798 11.6371 7.26855C11.6369 4.88928 9.70781 2.96094 7.32849 2.96094Z" fill="currentColor"/></svg>',
8
- "arrow-down-wide-narrow":
9
- '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4.66722 3.14218C4.95711 3.14225 5.19261 3.37767 5.19261 3.66757V11.6295L6.95824 9.82578C7.16093 9.61896 7.49327 9.61573 7.70043 9.81796C7.90753 10.0208 7.91092 10.353 7.70824 10.5602L5.27953 13.0406L5.2141 13.1012C4.89729 13.3642 4.43507 13.3645 4.1184 13.1012L4.05297 13.0406L1.62523 10.5602C1.4228 10.353 1.42608 10.0207 1.63304 9.81796C1.8401 9.61558 2.17245 9.61912 2.37523 9.82578L4.14183 11.6295V3.66757C4.14183 3.37762 4.37727 3.14218 4.66722 3.14218Z" fill="currentColor"/> <path d="M10.7854 2.98203C11.1021 2.71876 11.5643 2.719 11.8811 2.98203L11.9465 3.04257L14.3752 5.52304C14.5779 5.73021 14.5745 6.06242 14.3674 6.26523C14.1603 6.46786 13.828 6.46437 13.6252 6.25742L11.8586 4.45175V12.3336C11.8584 12.6233 11.623 12.859 11.3332 12.859C11.0436 12.8588 10.8081 12.6232 10.8078 12.3336V4.45371L9.04222 6.25742C8.83948 6.46447 8.50723 6.46775 8.30004 6.26523C8.093 6.06236 8.08941 5.73017 8.29222 5.52304L10.72 3.04257L10.7854 2.98203Z" fill="currentColor"/></svg>',
10
- check:
11
- '<!-- @license lucide-static v0.577.0 - ISC -->\n<svg\n class="lucide lucide-check"\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="M20 6 9 17l-5-5" />\n</svg>\n',
12
- x: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M10.962 4.29539C11.1669 4.09057 11.4992 4.09076 11.7042 4.29539C11.9092 4.50039 11.9091 4.83255 11.7042 5.03758L8.74229 7.99949L11.7052 10.9624C11.9097 11.1673 11.9097 11.4996 11.7052 11.7046C11.5003 11.9095 11.168 11.9093 10.963 11.7046L8.0001 8.74168L5.03721 11.7046C4.83216 11.9093 4.49994 11.9095 4.29502 11.7046C4.09047 11.4996 4.09045 11.1673 4.29502 10.9624L7.25791 7.99949L4.296 5.03758C4.09101 4.83255 4.09099 4.5004 4.296 4.29539C4.50105 4.09079 4.8333 4.09054 5.03819 4.29539L8.0001 7.2573L10.962 4.29539Z" fill="currentColor"/></svg>',
13
- 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>',
14
- settings:
15
- '<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>',
16
- 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',
17
- "prompt-nav":
18
- '<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>',
19
- };
1
+ const ICONS = config.resources.icons;
20
2
  const MARKER = "data-minecodex-quick-prompts";
21
3
  const zh = () => (document.documentElement.lang || navigator.language || "en").toLowerCase().startsWith("zh");
22
4
  const text = (en, cn) => (zh() ? cn : en);
23
5
  const style = document.createElement("style");
24
- style.textContent = `
25
- [${MARKER}=page] .pm-title-control { display:flex; align-items:stretch; min-height:40px; border:1px solid var(--color-border-primary-outline); border-radius:var(--radius-lg); background:var(--color-background-primary-soft); overflow:hidden; }
26
- [${MARKER}=page] .pm-title-control:focus-within { border-color:var(--color-ring); }
27
- [${MARKER}=page] .pm-title-control>.pm-emoji-trigger { display:flex; flex:none; align-items:center; justify-content:center; width:40px; padding:0; border-inline-end:1px solid var(--color-border); }
28
- [${MARKER}=page] .pm-title-control>.pm-emoji-trigger:hover { background:var(--color-background-primary-ghost-hover); }
29
- [${MARKER}=page] .pm-title-control>.pm-emoji-trigger:focus-visible { outline-offset:-3px; }
30
- [${MARKER}=page] .pm-editor .pm-title-control>input { flex:1; min-width:0; border:0; border-radius:0; background:transparent; padding:8px 10px; outline:none; box-shadow:none; }
31
-
32
- [${MARKER}=panel] { position:fixed; z-index:50; zoom:var(--codex-window-zoom,1); transform:translateY(-100%); box-sizing:border-box; display:flex; flex-direction:column; overflow:hidden; padding:var(--spacing); border:1px solid var(--color-border); border-radius:var(--radius-suggestion-menu,var(--radius-2xl)); background:var(--color-surface-elevated-secondary); color:var(--color-text-primary); font-family:var(--font-sans-default); font-size:var(--text-sm); line-height:1.43; }
33
- [${MARKER}=panel] *, [${MARKER}=button] { box-sizing:border-box; }
34
- [${MARKER}=panel] button { cursor:var(--cursor-interaction,pointer); }
35
- [${MARKER}=panel] button:disabled { opacity:.4; cursor:default; }
36
- [${MARKER}=panel] button:focus-visible, [${MARKER}=panel] input:focus-visible, [${MARKER}=panel] textarea:focus-visible { outline:2px solid var(--color-ring,var(--color-text-secondary)); outline-offset:-2px; }
37
- [${MARKER}=panel] svg { width:16px; height:16px; flex:none; }
38
- [${MARKER}=panel] .qp-header { display:flex; align-items:center; justify-content:space-between; padding:var(--spacing) var(--padding-row-x,8px); color:var(--color-text-tertiary); flex:none; }
39
- [${MARKER}=panel] .qp-scroll { overflow-y:auto; min-height:0; padding-bottom:var(--spacing); }
40
- [${MARKER}=panel] .qp-row { display:flex; align-items:center; min-width:0; border-radius:var(--suggestion-menu-item-radius,var(--radius-xl)); }
41
- [${MARKER}=panel] .qp-row:hover, [${MARKER}=panel] .qp-row:focus-within { background:var(--color-background-primary-ghost-hover); }
42
- [${MARKER}=panel] .qp-select { display:flex; flex:1; align-items:center; min-width:0; gap:calc(var(--spacing)*1.5); text-align:left; padding:var(--suggestion-menu-item-padding,var(--app-menu-item-padding,var(--padding-row-y,5px) var(--padding-row-x,8px))); border-radius:inherit; }
43
- [${MARKER}=panel] .qp-title { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
44
- [${MARKER}=panel] .qp-preview { flex:1; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:var(--color-text-tertiary); }
45
- [${MARKER}=panel] .qp-empty { padding:calc(var(--spacing)*3) var(--padding-row-x,8px); color:var(--color-text-tertiary); }
46
- [${MARKER}=panel] .qp-command { padding:var(--spacing) calc(var(--spacing)*2); border-radius:var(--radius-xl); }
47
- [${MARKER}=panel] .qp-command:hover { background:var(--color-background-primary-ghost-hover); }
48
- [${MARKER}=panel] .qp-error { color:var(--color-text-danger,var(--color-text-primary)); padding:var(--spacing) var(--padding-row-x,8px); }
49
- .qp-symbol { display:inline-flex; width:20px; height:20px; flex:none; align-items:center; justify-content:center; font-size:18px; line-height:1; color:var(--prompt-icon-light,var(--color-text-secondary)); }
50
- .qp-symbol svg { width:20px; height:20px; }
51
- .dark .qp-symbol, [data-theme=dark] .qp-symbol { color:var(--prompt-icon-dark,var(--color-text-secondary)); }
52
- [${MARKER}=page] { position:fixed; z-index:20; overflow:auto; border-radius:var(--radius-xl-base); background:var(--color-surface); color:var(--color-text-primary); font-family:var(--font-sans-default); font-size:var(--text-sm); line-height:var(--text-sm--line-height); }
53
- [${MARKER}=page] * { box-sizing:border-box; }
54
- [${MARKER}=page] button { cursor:var(--cursor-interaction,pointer); }
55
- [${MARKER}=page] button:disabled { opacity:.35; cursor:default; }
56
- [${MARKER}=page] button:focus-visible, [${MARKER}=page] input:focus-visible, [${MARKER}=page] textarea:focus-visible { outline:2px solid var(--color-text-secondary); outline-offset:2px; }
57
- [${MARKER}=page] .pm-wrap { max-width:1440px; margin:auto; padding:28px 48px 48px; }
58
- [${MARKER}=page] .pm-heading { display:flex; align-items:center; justify-content:space-between; gap:16px; min-height:36px; margin-bottom:24px; }
59
- [${MARKER}=page] h1 { font-size:var(--text-heading-lg); font-weight:var(--font-weight-medium); line-height:1.2; margin:0; }
60
- [${MARKER}=page] h2 { font-size:var(--text-base,14px); font-weight:600; margin:24px 0 12px; }
61
- [${MARKER}=page] .pm-header-actions { display:flex; align-items:center; gap:8px; }
62
- [${MARKER}=page] .pm-tab-row { display:flex; align-items:center; gap:8px; margin-bottom:24px; }
63
- [${MARKER}=page] .pm-tab-row .pm-tabs { margin-bottom:0; min-width:0; }
64
- [${MARKER}=page] .pm-icon { display:flex; align-items:center; justify-content:center; width:32px; height:32px; flex:none; padding:0; border-radius:var(--radius-full); color:var(--color-text-secondary); }
65
- [${MARKER}=page] .pm-icon[hidden] { display:none; }
66
- [${MARKER}=page] .pm-icon svg { width:16px; height:16px; }
67
- [${MARKER}=page] .pm-icon:hover, [${MARKER}=page] .pm-icon[aria-expanded=true], [${MARKER}=page] .pm-icon[aria-pressed=true] { background:var(--color-background-primary-ghost-hover); color:var(--color-text-primary); }
68
- [${MARKER}=page] .pm-popup { position:fixed; z-index:30; width:200px; padding:var(--app-menu-gutter,var(--spacing)); border-radius:var(--radius-2xl); background:color-mix(in srgb,var(--color-surface-elevated-secondary) 90%,transparent); box-shadow:0 0 0 .5px var(--color-border),var(--shadow-xl-spread,0 8px 16px -4px rgba(0,0,0,.12)); backdrop-filter:blur(4px); }
69
- [${MARKER}=page] .pm-popup button { width:100%; min-height:var(--app-menu-item-height,30px); display:flex; align-items:center; justify-content:space-between; gap:16px; padding:var(--app-menu-item-padding,5px 8px); border-radius:var(--radius-xl); text-align:left; }
70
- [${MARKER}=page] .pm-popup button:hover, [${MARKER}=page] .pm-popup button:focus-visible { background:var(--color-background-primary-ghost-hover); }
71
- [${MARKER}=page] .pm-popup svg { width:16px; height:16px; }
72
- [${MARKER}=page] .pm-command { display:inline-flex; align-items:center; justify-content:center; gap:6px; min-height:32px; padding:6px 12px; border-radius:var(--radius-full,999px); color:inherit; white-space:nowrap; }
73
- [${MARKER}=page] .pm-command:hover { background:var(--color-background-primary-ghost-hover); }
74
- [${MARKER}=page] .pm-primary { background:var(--color-background-primary-solid); color:var(--color-text-primary-solid); }
75
- [${MARKER}=page] .pm-primary:hover { background:var(--color-background-primary-solid-hover); }
76
- [${MARKER}=page] .pm-tabs { display:flex; gap:4px; overflow:auto; padding-bottom:4px; margin-bottom:24px; }
77
- [${MARKER}=page] .pm-tab { display:flex; align-items:center; gap:6px; flex:none; height:36px; border-radius:999px; padding:0 16px; font-weight:500; color:var(--color-text-secondary); }
78
- [${MARKER}=page] .pm-tab:hover, [${MARKER}=page] .pm-tab[aria-selected=true] { background:var(--color-background-primary-ghost-hover); color:var(--color-text-primary); }
79
- [${MARKER}=page] input, [${MARKER}=page] textarea { font:inherit; color:inherit; background:transparent; border:1px solid var(--color-border); border-radius:var(--radius-lg,12px); padding:8px 12px; }
80
- [${MARKER}=page] .pm-list { display:flex; flex-direction:column; gap:4px; }
81
- [${MARKER}=page] .pm-row { display:flex; align-items:center; gap:12px; padding:16px 12px; border-radius:var(--radius-xl,16px); }
82
- [${MARKER}=page] .pm-row:hover { background:var(--color-background-primary-ghost-hover); }
83
- [${MARKER}=page] .pm-info { flex:1; min-width:0; text-align:left; }
84
- [${MARKER}=page] .pm-title { font-weight:500; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
85
- [${MARKER}=page] .pm-preview { color:var(--color-text-secondary); overflow:hidden; white-space:nowrap; text-overflow:ellipsis; margin-top:3px; }
86
- [${MARKER}=page] .pm-stats { color:var(--color-text-tertiary); min-width:130px; text-align:right; font-variant-numeric:tabular-nums; font-size:var(--text-xs,12px); }
87
- [${MARKER}=page] .pm-count { color:var(--color-text-secondary); }
88
- [${MARKER}=page] .pm-empty { color:var(--color-text-tertiary); padding:48px 0; text-align:center; }
89
- [${MARKER}=page] .pm-form { display:flex; flex-direction:column; gap:16px; }
90
- [${MARKER}=page] .pm-field { display:flex; flex-direction:column; gap:6px; color:var(--color-text-secondary); }
91
- [${MARKER}=page] .pm-field textarea { min-height:220px; resize:vertical; color:var(--color-text-primary); }
92
- [${MARKER}=page] .pm-footer { display:flex; justify-content:flex-end; gap:8px; margin-top:8px; }
93
- [${MARKER}=page] .pm-grid { display:flex; flex-wrap:wrap; gap:4px; }
94
- [${MARKER}=page] .pm-choice { width:36px; height:36px; border-radius:10px; display:flex; justify-content:center; align-items:center; }
95
- [${MARKER}=page] .pm-choice[aria-pressed=true] { background:var(--color-background-primary-ghost-hover); outline:1px solid var(--color-text-secondary); }
96
- [${MARKER}=page] .pm-swatch { width:18px; height:18px; border-radius:50%; background:currentColor; }
97
- [${MARKER}=page] .pm-error { color:var(--color-text-danger,var(--color-text-primary)); margin:12px 0; }
98
- [${MARKER}=page] .pm-editor h2 { margin-top:0; }
99
- @media(max-width:800px) { [${MARKER}=page] .pm-wrap { padding:24px 30px; } [${MARKER}=page] .pm-stats { min-width:90px; } }
100
- [${MARKER}=page] .pm-modal-layer { position:fixed; inset:0; z-index:60; display:grid; place-items:center; padding:24px; background:var(--color-background-overlay,rgba(0,0,0,.16)); }
101
- [${MARKER}=page] .pm-editor { position:relative; top:auto; width:440px; max-width:calc(100vw - 48px); max-height:calc(100vh - 64px); margin:0; padding:20px; overflow:auto; border:0; border-radius:var(--radius-3xl,25px); background:color-mix(in srgb,var(--color-surface-elevated-secondary) 90%,transparent); box-shadow:0 0 0 .5px var(--color-border),var(--shadow-lg); backdrop-filter:blur(24px); }
102
- [${MARKER}=page] .pm-prompt-editor { width:520px; }
103
- [${MARKER}=page] .pm-modal-heading { display:flex; align-items:center; gap:8px; margin:0 0 16px; }
104
- [${MARKER}=page] .pm-modal-heading h2 { flex:1; font-size:var(--text-lg,18px); font-weight:600; line-height:28px; margin:0; }
105
- [${MARKER}=page] .pm-modal-heading .pm-icon { width:28px; height:28px; }
106
- [${MARKER}=page] .pm-editor .pm-row { min-height:44px; padding:8px; gap:10px; border-radius:var(--radius-lg); }
107
- [${MARKER}=page] .pm-editor .pm-form { gap:12px; }
108
- [${MARKER}=page] .pm-editor .pm-field { gap:8px; color:var(--color-text-primary); }
109
- [${MARKER}=page] .pm-editor input, [${MARKER}=page] .pm-editor textarea { border-radius:var(--radius-md); padding:8px 10px; background:var(--color-background-primary-soft,rgba(0,0,0,.025)); }
110
- [${MARKER}=page] .pm-editor .pm-footer { margin-top:20px; gap:12px; }
111
- [${MARKER}=page] .pm-editor .pm-footer .pm-command { border-radius:var(--radius-button-action,999px); min-height:32px; padding:6px 16px; background:var(--color-background-primary-ghost-hover); }
112
- [${MARKER}=page] .pm-editor .pm-footer .pm-primary { background:var(--color-background-primary-solid); color:var(--color-text-primary-solid); }
113
- [${MARKER}=page] .pm-editor .pm-footer .pm-primary:hover { background:var(--color-background-primary-solid-hover); }
114
- [${MARKER}=page] .pm-grid { gap:4px; }
115
- [${MARKER}=page] .pm-choice { width:36px; height:36px; border-radius:var(--radius-lg); }
116
- [${MARKER}=page] .pm-choice svg { width:20px; height:20px; }
117
- [${MARKER}=page] .pm-choice[aria-pressed=true] { outline:none; box-shadow:inset 0 0 0 1px var(--color-border); }
118
- [${MARKER}=page] .pm-search-inline { display:flex; align-items:center; width:clamp(150px,22vw,300px); height:32px; padding:0 12px; gap:8px; border:1px solid var(--color-border-primary-outline,var(--color-border)); border-radius:var(--radius-full); background:transparent; }
119
- [${MARKER}=page] .pm-search-inline input { min-width:0; width:100%; padding:0; border:0; background:transparent; outline:none; }
120
- [${MARKER}=page] .pm-search-inline>svg { width:16px; height:16px; flex:none; color:var(--color-text-tertiary); }
121
- [${MARKER}=page] .pm-list>h2 { color:var(--color-text-tertiary); font-size:var(--text-sm,13px); font-weight:500; }
122
- [${MARKER}=page] .pm-stats { min-width:64px; }
123
- [${MARKER}=page] .pm-modal-heading>.pm-icon:first-child { margin-left:-11px; }
124
- [${MARKER}=page] .pm-modal-heading>.pm-icon:last-child { margin-right:-10px; }
125
- [${MARKER}=page] .pm-editor input:focus, [${MARKER}=page] .pm-editor textarea:focus, [${MARKER}=page] .pm-popup input:focus { outline:none; box-shadow:none; border-color:var(--color-ring,var(--color-border-primary-outline)); }
126
- [${MARKER}=page] .pm-editor .pm-footer .pm-danger { color:var(--color-text-danger); background:var(--color-background-danger-soft-alpha); }
127
- [${MARKER}=page] .pm-editor .pm-footer .pm-danger:hover { background:var(--color-background-danger-soft-alpha-hover); }
128
- [${MARKER}=page] .pm-confirm-description { color:var(--color-text-secondary); line-height:1.6; }
129
- [${MARKER}=page] .pm-select { display:flex; align-items:center; gap:8px; min-height:36px; padding:8px 10px; background:var(--color-background-primary-soft); border:1px solid var(--color-border); border-radius:var(--radius-md); text-align:left; }
130
- [${MARKER}=page] .pm-select>svg { width:16px; height:16px; }
131
- [${MARKER}=page] .pm-option-label { flex:1; }
132
- [${MARKER}=page] .pm-empty-state { display:flex; flex-direction:column; align-items:center; justify-content:center; min-height:340px; gap:8px; text-align:center; }
133
- [${MARKER}=page] .pm-empty-art { font-size:var(--text-5xl,48px); line-height:1; margin-bottom:8px; }
134
- [${MARKER}=page] .pm-empty-title { font-size:var(--text-lg,18px); line-height:24px; font-weight:500; }
135
- [${MARKER}=page] .pm-empty-description { color:var(--color-text-secondary); }
136
- [${MARKER}=page] .pm-empty-state>.pm-command { margin-top:24px; }
137
- [${MARKER}=page] .pm-drag { cursor:grab; color:var(--color-text-tertiary); }
138
- [${MARKER}=page] .pm-drag:active { cursor:grabbing; }
139
- [${MARKER}=page] .pm-drop-placeholder { flex:none; border:1px dashed var(--color-border-primary-outline,var(--color-border)); border-radius:var(--radius-xl); background:var(--color-background-primary-ghost-hover); box-sizing:border-box; }
140
- [${MARKER}=page] .pm-dragging { position:absolute; opacity:0; pointer-events:none; }
141
- [${MARKER}=page] .pm-popup { z-index:70; max-height:calc(100vh - 32px); overflow:auto; }
142
- [${MARKER}=page] .pm-symbol-picker { width:320px; padding:12px; display:flex; flex-direction:column; gap:12px; }
143
- [${MARKER}=page] .pm-symbol-picker>input { width:100%; flex:none; border-radius:var(--radius-full); }
144
- [${MARKER}=page] .pm-emoji-grid { display:grid; grid-template-columns:repeat(8,minmax(0,1fr)); gap:4px; max-height:264px; overflow:auto; }
145
- [${MARKER}=page] .pm-emoji-grid .pm-emoji-choice { width:32px; height:32px; min-height:32px; padding:0; display:flex; justify-content:center; font-size:22px; border-radius:var(--radius-lg); }
146
- [${MARKER}=page] .pm-emoji-choice[aria-pressed=true] { background:var(--color-background-primary-ghost-hover); }
147
- `;
6
+ style.textContent = config.resources.styles;
148
7
  document.head.append(style);
149
8
  let button = null;
150
9
  let addButton = null;
151
- let composerCard = null;
10
+ let composerRoot = null;
152
11
  let composerThread = null;
153
12
  let panel = null;
154
13
  let items = [];
@@ -194,7 +53,7 @@ function control(label, handler, className = "qp-command", iconName = null) {
194
53
  node.setAttribute("aria-label", label);
195
54
  if (iconName) {
196
55
  node.append(icon(iconName));
197
- node.title = label;
56
+ node.setAttribute("data-minecodex-tooltip", label);
198
57
  }
199
58
  node.addEventListener("click", handler);
200
59
  return node;
@@ -210,13 +69,8 @@ function showError(error) {
210
69
  panel.append(message);
211
70
  }
212
71
  function position() {
213
- if (!panel || !composerCard?.isConnected) return;
214
- const rect = composerCard.getBoundingClientRect();
215
- const zoom = Number.parseFloat(getComputedStyle(panel).zoom) || 1;
216
- panel.style.left = `${rect.left / zoom}px`;
217
- panel.style.top = `${(rect.top - 4) / zoom}px`;
218
- panel.style.width = `${rect.width / zoom}px`;
219
- panel.style.maxHeight = `${Math.max(0, Math.min(400, (rect.top - 16) / zoom))}px`;
72
+ if (!panel || !composerRoot?.isConnected) return;
73
+ config.ui.composerMenu(panel, addButton);
220
74
  }
221
75
  function close(restoreFocus = false) {
222
76
  generation++;
@@ -251,7 +105,7 @@ async function loadList() {
251
105
  }
252
106
  }
253
107
  const COLORS = {
254
- black: ["var(--color-text-secondary)", "var(--color-text-secondary)"],
108
+ black: ["var(--mcx-color-text-secondary)", "var(--mcx-color-text-secondary)"],
255
109
  blue: ["#0285ff", "#339cff"],
256
110
  green: ["#04b84c", "#40c977"],
257
111
  orange: ["#fb6a22", "#ff8549"],
@@ -352,6 +206,7 @@ function open() {
352
206
  if (addButton.getAttribute("aria-expanded") === "true") addButton.click();
353
207
  panel = element("div");
354
208
  panel.setAttribute(MARKER, "panel");
209
+ config.ui.surface(panel, "composer-menu");
355
210
  panel.setAttribute("role", "dialog");
356
211
  panel.setAttribute("aria-label", text("Quick Prompts", "快捷提示词"));
357
212
  document.body.append(panel);
@@ -360,46 +215,45 @@ function open() {
360
215
  position();
361
216
  void loadList();
362
217
  }
218
+ function isOpenComposerMenu(node) {
219
+ return node.getAttribute("aria-expanded") === "true"
220
+ && node.checkVisibility({ visibilityProperty: true })
221
+ && (node.getAttribute("data-composer-navigation-target") === "add-context"
222
+ || node.getAttribute("aria-haspopup") === "menu");
223
+ }
363
224
  function sync() {
364
225
  if (disposed || signal.aborted) return;
365
226
  syncNavigation();
366
- const add = [
367
- ...document.querySelectorAll("[data-codex-composer-root] button"),
368
- ].find(
369
- (node) =>
370
- node.getAttribute("data-composer-navigation-target") === "add-context",
371
- );
372
- const card = add?.closest("[data-codex-composer-root]");
373
- const thread =
374
- card
375
- ?.querySelector("[data-above-composer-conversation-id]")
376
- ?.getAttribute("data-above-composer-conversation-id") ?? null;
227
+ const nativeComposer = config.codex.composer(null,{threadOnly:false});
228
+ const add = nativeComposer?.add;
229
+ const card = nativeComposer?.root;
230
+ const thread = nativeComposer?.threadId ?? null;
377
231
  if (thread !== composerThread) {
378
232
  close();
379
233
  composerThread = thread;
380
234
  }
381
- if (add !== addButton || card !== composerCard) {
235
+ if (add !== addButton || card !== composerRoot) {
382
236
  close();
383
237
  button?.remove();
384
238
  button = null;
385
239
  addButton = add;
386
- composerCard = card;
240
+ composerRoot = card;
387
241
  }
388
242
  if (!add) return;
389
243
  if (
390
244
  panel &&
391
- [...card.querySelectorAll("button[aria-expanded=true]")].some(
392
- (node) => node !== button,
393
- )
245
+ [...card.querySelectorAll("button")].some(isOpenComposerMenu)
394
246
  )
395
247
  close();
396
248
  if (!button) {
397
249
  button = add.cloneNode(false);
250
+ button.removeAttribute("title");
251
+ button.removeAttribute("aria-describedby");
398
252
  button.removeAttribute("id");
399
253
  button.removeAttribute("data-composer-navigation-target");
400
254
  button.setAttribute(MARKER, "button");
401
255
  button.setAttribute("aria-label", text("Quick Prompts", "快捷提示词"));
402
- button.title = text("Quick Prompts", "快捷提示词");
256
+ button.setAttribute("data-minecodex-tooltip", text("Quick Prompts", "快捷提示词"));
403
257
  button.setAttribute("aria-haspopup", "dialog");
404
258
  button.setAttribute("aria-expanded", "false");
405
259
  button.setAttribute("data-state", "closed");
@@ -440,10 +294,10 @@ let searchText = "";
440
294
  let editor = null;
441
295
  let pageFocus = null;
442
296
  const hiddenSurfaces = new Map();
443
- const nativeNavigationSelection = new Map();
297
+ let navigationRail = null;
444
298
 
445
- function pageControl(label, handler, primary = false) {
446
- return control(label, handler, `pm-command${primary ? " pm-primary" : ""}`);
299
+ function pageControl(label, handler, primary = false, tone = "secondary") {
300
+ return config.ui.action(control(label, handler, `pm-command${primary ? " pm-primary" : ""}`), primary ? "primary" : tone);
447
301
  }
448
302
  function pageError(error) {
449
303
  if (!page) return;
@@ -454,7 +308,7 @@ function pageError(error) {
454
308
  }
455
309
  function positionPage() {
456
310
  if (!page) return;
457
- const rail = document.querySelector("[data-app-navigation-rail]");
311
+ const rail = config.codex.rail();
458
312
  if (!rail) {
459
313
  closePage();
460
314
  return;
@@ -466,30 +320,10 @@ function positionPage() {
466
320
  page.style.bottom = "4px";
467
321
  }
468
322
  function syncNavigationSelection() {
469
- if (!page) return;
470
- for (const node of document.querySelectorAll('[data-app-navigation-rail] [data-sidebar-destination][data-selected], [data-app-navigation-rail] [data-sidebar-destination][aria-current="page"]')) {
471
- if (!nativeNavigationSelection.has(node)) {
472
- nativeNavigationSelection.set(node, new Map(
473
- ["data-selected", "aria-current", "data-suppress-active-style"].map(name => [name, node.getAttribute(name)]),
474
- ));
475
- }
476
- node.removeAttribute("data-selected");
477
- node.removeAttribute("aria-current");
478
- node.setAttribute("data-suppress-active-style", "");
479
- }
480
- if (navButton && !navButton.hasAttribute("data-selected")) navButton.setAttribute("data-selected", "");
481
- if (navButton?.getAttribute("aria-current") !== "page") navButton?.setAttribute("aria-current", "page");
323
+ navigationRail?.select(Boolean(page));
482
324
  }
483
325
  function restoreNavigationSelection() {
484
- // 退出覆盖页时还原原生状态,随后由原生路由接管新的选择。
485
- for (const [node, attributes] of nativeNavigationSelection) {
486
- if (!node.isConnected) continue;
487
- for (const [name, value] of attributes) {
488
- if (value === null) node.removeAttribute(name);
489
- else node.setAttribute(name, value);
490
- }
491
- }
492
- nativeNavigationSelection.clear();
326
+ navigationRail?.select(false);
493
327
  }
494
328
  function closePage(restoreFocus = false) {
495
329
  dragState?.cancel();
@@ -528,6 +362,7 @@ function openPage() {
528
362
  pageFocus = document.activeElement;
529
363
  page = element("section");
530
364
  page.setAttribute(MARKER, "page");
365
+ config.ui.page(page);
531
366
  page.setAttribute("aria-label", text("Prompts", "提示词"));
532
367
  document.body.append(page);
533
368
  // 只隔离主内容的焦点,保留导航栏与原生窗口控件。
@@ -549,7 +384,7 @@ let pagePopup = null;
549
384
  let popupTrigger = null;
550
385
  function pageIcon(label, name, handler) {
551
386
  const button = control(label, handler, "pm-icon", name);
552
- button.title = label;
387
+ button.setAttribute("data-minecodex-tooltip", label);
553
388
  return button;
554
389
  }
555
390
  function closePagePopup(restoreFocus = false) {
@@ -569,6 +404,7 @@ function makePagePopup(trigger, className, role, matchTriggerWidth = false) {
569
404
  popupTrigger = trigger;
570
405
  trigger.setAttribute("aria-expanded", "true");
571
406
  pagePopup = element("div", `pm-popup ${className}`);
407
+ config.ui.surface(pagePopup, "dropdown");
572
408
  pagePopup.setAttribute("role", role);
573
409
  pagePopup.setAttribute("aria-label", trigger.getAttribute("aria-label"));
574
410
  (editorLayer ?? page).append(pagePopup);
@@ -859,10 +695,10 @@ function orderControls(item, ordered, isCategory, row) {
859
695
  () => {},
860
696
  );
861
697
  handle.classList.add("pm-drag");
862
- handle.title = text(
698
+ handle.setAttribute("data-minecodex-tooltip", text(
863
699
  "Drag to reorder · Alt + ↑ / ↓",
864
700
  "拖拽排序 · Alt + ↑ / ↓",
865
- );
701
+ ));
866
702
  row.dataset.orderId = item.id;
867
703
  const group = isCategory ? "categories" : `prompts:${item.categoryId ?? ""}`;
868
704
  row.dataset.orderGroup = group;
@@ -981,9 +817,12 @@ function field(label, input) {
981
817
  }
982
818
  let editorLayer = null;
983
819
  let editorReturnFocus = null;
820
+ let editorFocusCleanup = null;
984
821
  function closeEditor(restoreFocus = true) {
985
822
  dragState?.cancel();
986
823
  closePagePopup();
824
+ editorFocusCleanup?.();
825
+ editorFocusCleanup = null;
987
826
  editorLayer?.remove();
988
827
  editorLayer = null;
989
828
  editor = null;
@@ -997,7 +836,11 @@ function editorShell(title, back = null) {
997
836
  if (!editorLayer) {
998
837
  editorReturnFocus = document.activeElement;
999
838
  editorLayer = element("div", "pm-modal-layer");
1000
- page.append(editorLayer);
839
+ editorLayer.setAttribute(MARKER, "modal");
840
+ config.ui.overlay(editorLayer);
841
+ // 弹窗遮罩覆盖整个窗口,避免受页面与侧栏的层级约束。
842
+ config.ui.mount(editorLayer, "modal");
843
+ editorFocusCleanup = config.ui.trapFocus(() => editor, { onClose:()=>closeEditor(), isBlocked:()=>Boolean(pagePopup) });
1001
844
  page.querySelector(".pm-wrap").inert = true;
1002
845
  }
1003
846
  editorLayer.replaceChildren();
@@ -1006,48 +849,15 @@ function editorShell(title, back = null) {
1006
849
  editor.setAttribute("aria-modal", "true");
1007
850
  editor.setAttribute("aria-label", title);
1008
851
  editor.tabIndex = -1;
1009
- const heading = element("div", "pm-modal-heading");
1010
- if (back) heading.append(pageIcon(text("Back", "返回"), "back", back));
1011
- heading.append(
1012
- element("h2", "", title),
1013
- pageIcon(text("Close", "关闭"), "x", () => closeEditor()),
1014
- );
1015
- editor.append(heading);
852
+ const heading = config.ui.heading(editor, {
853
+ title, closeLabel:text("Close", "关闭"), onClose:()=>closeEditor(),
854
+ back:back ? pageIcon(text("Back", "返回"), "back", back) : null,
855
+ });
856
+ heading.classList.add("pm-modal-heading");
1016
857
  editorLayer.append(editor);
1017
858
  editor.focus();
1018
859
  return editor;
1019
860
  }
1020
- document.addEventListener(
1021
- "keydown",
1022
- (event) => {
1023
- if (!editorLayer || !editor || pagePopup) return;
1024
- if (event.key === "Escape") {
1025
- event.preventDefault();
1026
- event.stopImmediatePropagation();
1027
- closeEditor();
1028
- }
1029
- if (event.key === "Tab") {
1030
- const controls = [
1031
- ...editor.querySelectorAll(
1032
- 'button:not(:disabled),input,select,textarea,[tabindex="0"]',
1033
- ),
1034
- ].filter((x) => x.getBoundingClientRect().width);
1035
- const first = controls[0],
1036
- last = controls.at(-1);
1037
- if (
1038
- event.shiftKey &&
1039
- (document.activeElement === first || document.activeElement === editor)
1040
- ) {
1041
- event.preventDefault();
1042
- last?.focus();
1043
- } else if (!event.shiftKey && document.activeElement === last) {
1044
- event.preventDefault();
1045
- first?.focus();
1046
- }
1047
- }
1048
- },
1049
- { signal, capture: true },
1050
- );
1051
861
  function editPrompt(item = null) {
1052
862
  const target = editorShell(
1053
863
  item ? text("Edit prompt", "编辑提示词") : text("New prompt", "新建提示词"),
@@ -1115,7 +925,7 @@ function editPrompt(item = null) {
1115
925
  emojiButton.replaceChildren(
1116
926
  categoryIcon({ categoryId: category.value || null, emoji: emoji.value }),
1117
927
  );
1118
- emojiButton.title = text("Choose icon", "选择图标");
928
+ emojiButton.setAttribute("data-minecodex-tooltip", text("Choose icon", "选择图标"));
1119
929
  }
1120
930
  refreshAppearance();
1121
931
  const body = element("textarea");
@@ -1132,7 +942,7 @@ function editPrompt(item = null) {
1132
942
  field(text("Category", "分类"), categoryButton),
1133
943
  field(text("Prompt", "提示词"), body),
1134
944
  );
1135
- const footer = element("div", "pm-footer");
945
+ const footer = config.ui.footer(element("div", "pm-footer"));
1136
946
  if (item) {
1137
947
  const remove = pageControl(text("Delete", "删除"), async () => {
1138
948
  if (remove.dataset.confirm !== "true") {
@@ -1149,13 +959,13 @@ function editPrompt(item = null) {
1149
959
  pageError(error);
1150
960
  }
1151
961
  });
1152
- remove.classList.add("pm-danger");
962
+ config.ui.action(remove, "danger");
1153
963
  remove.style.marginRight = "auto";
1154
964
  footer.append(remove);
1155
965
  }
1156
966
  const save = pageControl(text("Save", "保存"), () => {}, true);
1157
967
  save.type = "submit";
1158
- footer.append(pageControl(text("Cancel", "取消"), renderPage), save);
968
+ footer.append(pageControl(text("Cancel", "取消"), renderPage, false, "ghost"), save);
1159
969
  form.append(footer);
1160
970
  target.append(form);
1161
971
  form.addEventListener("submit", async (event) => {
@@ -1263,7 +1073,7 @@ function openEmojiPicker(trigger, selected, refresh) {
1263
1073
  }
1264
1074
  function manageCategories() {
1265
1075
  const target = editorShell(text("Categories", "分类"));
1266
- const footer = element("div", "pm-footer");
1076
+ const footer = config.ui.footer(element("div", "pm-footer"));
1267
1077
  footer.append(
1268
1078
  pageControl(text("New category", "新建分类"), () => editCategory(), true),
1269
1079
  );
@@ -1311,7 +1121,7 @@ function editCategory(category = null) {
1311
1121
  ).documentElement;
1312
1122
  btn.replaceChildren(svg);
1313
1123
  btn.setAttribute("aria-pressed", String(key === selectedIcon));
1314
- btn.title = appearanceLabel(key);
1124
+ btn.setAttribute("data-minecodex-tooltip", appearanceLabel(key));
1315
1125
  btn.setAttribute("aria-label", appearanceLabel(key));
1316
1126
  icons.append(btn);
1317
1127
  }
@@ -1334,7 +1144,7 @@ function editCategory(category = null) {
1334
1144
  swatch.append(element("span", "pm-swatch"));
1335
1145
  btn.replaceChildren(swatch);
1336
1146
  btn.setAttribute("aria-pressed", String(key === selectedColor));
1337
- btn.title = appearanceLabel(key);
1147
+ btn.setAttribute("data-minecodex-tooltip", appearanceLabel(key));
1338
1148
  btn.setAttribute("aria-label", appearanceLabel(key));
1339
1149
  colors.append(btn);
1340
1150
  }
@@ -1345,7 +1155,7 @@ function editCategory(category = null) {
1345
1155
  element("div", "", text("Color", "颜色")),
1346
1156
  colors,
1347
1157
  );
1348
- const footer = element("div", "pm-footer");
1158
+ const footer = config.ui.footer(element("div", "pm-footer"));
1349
1159
  if (category) {
1350
1160
  const remove = pageControl(text("Delete", "删除"), () => {
1351
1161
  const count = items.filter(
@@ -1377,25 +1187,25 @@ function editCategory(category = null) {
1377
1187
  ),
1378
1188
  ),
1379
1189
  );
1380
- const actions = element("div", "pm-footer");
1190
+ const actions = config.ui.footer(element("div", "pm-footer"));
1381
1191
  const confirm = pageControl(
1382
1192
  text("Delete", "删除"),
1383
1193
  () => void destroy(confirm),
1384
1194
  );
1385
- confirm.classList.add("pm-danger");
1195
+ config.ui.action(confirm, "danger");
1386
1196
  actions.append(
1387
- pageControl(text("Cancel", "取消"), () => editCategory(category)),
1197
+ pageControl(text("Cancel", "取消"), () => editCategory(category), false, "ghost"),
1388
1198
  confirm,
1389
1199
  );
1390
1200
  confirmation.append(actions);
1391
1201
  });
1392
- remove.classList.add("pm-danger");
1202
+ config.ui.action(remove, "danger");
1393
1203
  remove.style.marginRight = "auto";
1394
1204
  footer.append(remove);
1395
1205
  }
1396
1206
  const save = pageControl(text("Save", "保存"), () => {}, true);
1397
1207
  save.type = "submit";
1398
- footer.append(pageControl(text("Cancel", "取消"), manageCategories), save);
1208
+ footer.append(pageControl(text("Cancel", "取消"), manageCategories, false, "ghost"), save);
1399
1209
  form.append(footer);
1400
1210
  target.append(form);
1401
1211
  form.addEventListener("submit", async (event) => {
@@ -1425,26 +1235,17 @@ function editCategory(category = null) {
1425
1235
  }
1426
1236
  // 原生导航挂载与卸载
1427
1237
  function syncNavigation() {
1428
- const rail = document.querySelector("[data-app-navigation-rail]");
1238
+ const rail = config.codex.rail();
1429
1239
  if (!rail) {
1430
1240
  navButton?.remove();
1431
1241
  navButton = null;
1432
1242
  if (page) closePage();
1433
1243
  return;
1434
1244
  }
1435
- const source =
1436
- rail.querySelector('[data-sidebar-destination="builtin:projects"]') ??
1437
- rail.querySelector('[data-sidebar-destination="builtin:library"]');
1438
- if (!source) return;
1439
- // 固定扩展区是独立容器;不依赖 Projects 的固定状态或文字。
1440
- const scroll = rail.querySelector(".overflow-y-auto");
1441
- if (!scroll) return;
1442
- let group = [...scroll.children].find(
1443
- (node) =>
1444
- node !== ownNavGroup &&
1445
- node.classList.contains("flex-col") &&
1446
- node.classList.contains("shrink-0"),
1447
- );
1245
+ const slot = config.codex.railSlot(ownNavGroup);
1246
+ if (!slot) return;
1247
+ const { template:source, scroll } = slot;
1248
+ let group = slot.group;
1448
1249
  if (!group) {
1449
1250
  if (!ownNavGroup) {
1450
1251
  ownNavGroup = element("div", "flex shrink-0 flex-col items-center gap-2");
@@ -1454,19 +1255,26 @@ function syncNavigation() {
1454
1255
  group = ownNavGroup;
1455
1256
  }
1456
1257
  if (!navButton?.isConnected) {
1258
+ navigationRail?.restore();
1457
1259
  navButton = source.cloneNode(false);
1260
+ navButton.removeAttribute("id");
1261
+ navButton.removeAttribute("title");
1262
+ navButton.removeAttribute("aria-describedby");
1458
1263
  navButton.removeAttribute("data-sidebar-destination");
1459
1264
  navButton.removeAttribute("data-selected");
1460
1265
  navButton.removeAttribute("aria-current");
1461
1266
  navButton.removeAttribute("data-suppress-active-style");
1462
1267
  navButton.setAttribute(MARKER, "nav");
1463
1268
  navButton.setAttribute("aria-label", text("Prompts", "提示词"));
1464
- navButton.title = text("Prompts", "提示词");
1269
+ navButton.setAttribute("data-minecodex-tooltip", text("Prompts", "提示词"));
1465
1270
  const inner = element("span", source.firstElementChild?.className ?? "");
1466
1271
  const glyph = icon("prompt-nav");
1467
1272
  glyph.setAttribute("aria-hidden", "true");
1468
1273
  inner.append(glyph, element("span", "sr-only", text("Prompts", "提示词")));
1469
1274
  navButton.append(inner);
1275
+ navigationRail = config.ui.navigationRail(navButton, {
1276
+ defaultIcon:glyph, selectedIcon:icon("prompt-nav-selected"),
1277
+ });
1470
1278
  navButton.addEventListener("click", openPage);
1471
1279
  }
1472
1280
  if (navButton.parentElement !== group || navButton !== group.lastElementChild)
@@ -1508,6 +1316,10 @@ observer.observe(document.documentElement, {
1508
1316
  "aria-expanded",
1509
1317
  "data-selected",
1510
1318
  "aria-current",
1319
+ "class",
1320
+ "hidden",
1321
+ "inert",
1322
+ "aria-hidden",
1511
1323
  ],
1512
1324
  });
1513
1325
  document.addEventListener(
@@ -0,0 +1,13 @@
1
+ {
2
+ "prompt": "<svg\n class=\"lucide lucide-book-open\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"-1.5 -1.5 27 27\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n>\n <path vector-effect=\"non-scaling-stroke\" d=\"M12 7v14\" />\n <path vector-effect=\"non-scaling-stroke\" 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\" />\n</svg>",
3
+ "chevron": "<!-- @license lucide-static v0.577.0 - ISC -->\n<svg\n class=\"lucide lucide-chevron-down\"\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=\"m6 9 6 6 6-6\" />\n</svg>\n",
4
+ "search": "<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=\"M7.32849 1.91016C10.2877 1.91016 12.6867 4.30938 12.6869 7.26855C12.6869 8.57363 12.2192 9.7688 11.4437 10.6982L14.0433 13.2979C14.2481 13.5028 14.2481 13.8351 14.0433 14.04C13.8384 14.245 13.5062 14.2449 13.3011 14.04L10.6957 11.4346C9.77536 12.1793 8.60462 12.627 7.32849 12.627C4.36931 12.6268 1.97009 10.2278 1.97009 7.26855C1.97027 4.30949 4.36942 1.91033 7.32849 1.91016ZM7.32849 2.96094C4.94932 2.96111 3.02105 4.88939 3.02087 7.26855C3.02087 9.64787 4.94921 11.577 7.32849 11.5771C9.70792 11.5771 11.6371 9.64798 11.6371 7.26855C11.6369 4.88928 9.70781 2.96094 7.32849 2.96094Z\" fill=\"currentColor\"/></svg>",
5
+ "arrow-down-wide-narrow": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path d=\"M4.66722 3.14218C4.95711 3.14225 5.19261 3.37767 5.19261 3.66757V11.6295L6.95824 9.82578C7.16093 9.61896 7.49327 9.61573 7.70043 9.81796C7.90753 10.0208 7.91092 10.353 7.70824 10.5602L5.27953 13.0406L5.2141 13.1012C4.89729 13.3642 4.43507 13.3645 4.1184 13.1012L4.05297 13.0406L1.62523 10.5602C1.4228 10.353 1.42608 10.0207 1.63304 9.81796C1.8401 9.61558 2.17245 9.61912 2.37523 9.82578L4.14183 11.6295V3.66757C4.14183 3.37762 4.37727 3.14218 4.66722 3.14218Z\" fill=\"currentColor\"/> <path d=\"M10.7854 2.98203C11.1021 2.71876 11.5643 2.719 11.8811 2.98203L11.9465 3.04257L14.3752 5.52304C14.5779 5.73021 14.5745 6.06242 14.3674 6.26523C14.1603 6.46786 13.828 6.46437 13.6252 6.25742L11.8586 4.45175V12.3336C11.8584 12.6233 11.623 12.859 11.3332 12.859C11.0436 12.8588 10.8081 12.6232 10.8078 12.3336V4.45371L9.04222 6.25742C8.83948 6.46447 8.50723 6.46775 8.30004 6.26523C8.093 6.06236 8.08941 5.73017 8.29222 5.52304L10.72 3.04257L10.7854 2.98203Z\" fill=\"currentColor\"/></svg>",
6
+ "check": "<!-- @license lucide-static v0.577.0 - ISC -->\n<svg\n class=\"lucide lucide-check\"\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=\"M20 6 9 17l-5-5\" />\n</svg>\n",
7
+ "x": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path d=\"M10.962 4.29539C11.1669 4.09057 11.4992 4.09076 11.7042 4.29539C11.9092 4.50039 11.9091 4.83255 11.7042 5.03758L8.74229 7.99949L11.7052 10.9624C11.9097 11.1673 11.9097 11.4996 11.7052 11.7046C11.5003 11.9095 11.168 11.9093 10.963 11.7046L8.0001 8.74168L5.03721 11.7046C4.83216 11.9093 4.49994 11.9095 4.29502 11.7046C4.09047 11.4996 4.09045 11.1673 4.29502 10.9624L7.25791 7.99949L4.296 5.03758C4.09101 4.83255 4.09099 4.5004 4.296 4.29539C4.50105 4.09079 4.8333 4.09054 5.03819 4.29539L8.0001 7.2573L10.962 4.29539Z\" fill=\"currentColor\"/></svg>",
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
+ "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
+ "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
+ }