minecodex 2.0.11 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/apps/dashboard/app.js +37 -4
- package/apps/dashboard/assets/data-transfer.js +129 -0
- package/apps/dashboard/assets/download.svg +15 -0
- package/apps/dashboard/assets/upload.svg +15 -0
- package/apps/dashboard/index.html +10 -0
- package/apps/dashboard/styles.css +52 -1
- package/features/file-navigation/assets/book-open.svg +16 -0
- package/features/file-navigation/codex-feature.json +27 -0
- package/features/file-navigation/src/adapters/codex/modules.mjs +67 -0
- package/features/file-navigation/src/adapters/codex/navigation.mjs +84 -0
- package/features/file-navigation/src/domain/navigation.mjs +12 -0
- package/features/file-navigation/src/entry.mjs +34 -0
- package/features/file-navigation/src/frontend/navigation.mjs +140 -0
- package/features/file-navigation/src/injection-source.mjs +9 -0
- package/features/file-navigation/src/navigation.css +16 -0
- package/features/file-navigation/vendor/lucide-LICENSE +43 -0
- package/features/markdown-reading/assets/book-open.svg +16 -0
- package/features/markdown-reading/codex-feature.json +27 -0
- package/features/markdown-reading/src/adapters/codex/links.mjs +280 -0
- package/features/markdown-reading/src/adapters/codex/lists.mjs +168 -0
- package/features/markdown-reading/src/adapters/codex/markdown-native.mjs +28 -0
- package/features/markdown-reading/src/adapters/codex/preview.mjs +38 -0
- package/features/markdown-reading/src/adapters/codex/providers.mjs +18 -0
- package/features/markdown-reading/src/adapters/codex/reading-runtime.mjs +152 -0
- package/features/markdown-reading/src/adapters/codex/rich-text.mjs +561 -0
- package/features/markdown-reading/src/adapters/codex/table-actions.mjs +270 -0
- package/features/markdown-reading/src/adapters/codex/tables.mjs +493 -0
- package/features/markdown-reading/src/adapters/codex/toc-native.mjs +131 -0
- package/features/markdown-reading/src/adapters/codex/toc.mjs +334 -0
- package/features/markdown-reading/src/domain/footnotes.mjs +56 -0
- package/features/markdown-reading/src/injection-source.mjs +43 -0
- package/features/markdown-reading/src/markdown-reading.mjs +82 -0
- package/features/markdown-reading/src/reading.css +346 -0
- package/features/markdown-reading/vendor/lucide-LICENSE +43 -0
- package/features/model-slider/codex-feature.json +1 -0
- package/features/model-slider/src/adapters/codex/controller.mjs +7 -5
- package/features/model-slider/src/backend/favorites.mjs +54 -0
- package/features/model-slider/src/domain/model-rules.mjs +6 -1
- package/features/model-slider/src/frontend/install.mjs +41 -18
- package/features/notes/assets/prompt-previews.json +26 -0
- package/features/notes/codex-feature.json +2 -1
- package/features/notes/src/backend/transfer.mjs +97 -0
- package/features/notes/src/frontend/entry.mjs +380 -10
- package/features/notes/src/frontend/icons.json +4 -1
- package/features/notes/src/frontend/styles.css +63 -0
- package/features/session-status/src/domain/session-stats.mjs +22 -8
- package/features/session-status/src/session-status.mjs +2 -2
- package/features/sites/src/backend/sites-service.mjs +20 -5
- package/features/sites/src/backend/transfer.mjs +43 -0
- package/package.json +11 -3
- package/packages/cli/src/config.mjs +4 -2
- package/packages/cli/src/control-server.mjs +23 -4
- package/packages/cli/src/data-transfer/service.mjs +42 -0
- package/packages/cli/src/runtime-manager.mjs +3 -0
- package/packages/runtime-host/src/codex-injection.mjs +1 -0
- package/packages/runtime-host/src/codex-runtime.mjs +11 -0
- package/packages/runtime-host/src/feature-registry.mjs +1 -0
- package/packages/ui/src/native/theme.mjs +22 -1
- package/packages/ui/src/native/tokens.mjs +4 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/* Pages 正文角色;仅标记的原生 Preview 生效。布局尺寸是本功能的面板适配规则。 */
|
|
2
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-link {
|
|
3
|
+
color: var(--color-text-accent, var(--color-token-text-link-foreground));
|
|
4
|
+
text-decoration: underline;
|
|
5
|
+
cursor: var(--cursor-interaction, pointer);
|
|
6
|
+
overflow-wrap: anywhere;
|
|
7
|
+
}
|
|
8
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-link-definition {
|
|
9
|
+
font-size: 0;
|
|
10
|
+
line-height: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
min-height: 0;
|
|
13
|
+
}
|
|
14
|
+
.cm-editor[data-minecodex-markdown-reading] {
|
|
15
|
+
--file-editor-font-size: var(--mcx-text-document, 14px);
|
|
16
|
+
--mcx-reading-width: 800px;
|
|
17
|
+
--mcx-reading-gutter: clamp(16px, 4cqi, 40px);
|
|
18
|
+
--mcx-reading-table-reading-width: calc(var(--thread-content-max-width, 768px) - var(--padding-toolbar, 16px) * 2);
|
|
19
|
+
--mcx-reading-table-wide-margin: min(calc(var(--spacing, .25rem) * 16), max(0px, (100cqi - var(--mcx-reading-table-reading-width)) / 2));
|
|
20
|
+
container-type: inline-size;
|
|
21
|
+
}
|
|
22
|
+
.cm-editor[data-minecodex-markdown-reading] > .cm-scroller {
|
|
23
|
+
font-family: var(--mcx-font-content, sans-serif);
|
|
24
|
+
font-size: var(--mcx-text-document, 14px);
|
|
25
|
+
line-height: var(--mcx-leading-relaxed, 1.625);
|
|
26
|
+
padding-block: calc(24px + var(--mcx-reading-metadata-height, 0px)) 64px;
|
|
27
|
+
padding-inline: max(var(--mcx-reading-gutter), calc((100% - var(--mcx-reading-width)) / 2));
|
|
28
|
+
}
|
|
29
|
+
[data-mcx-reading-metadata-frame] {
|
|
30
|
+
position: relative;
|
|
31
|
+
min-height: 0;
|
|
32
|
+
overflow: hidden;
|
|
33
|
+
}
|
|
34
|
+
[data-mcx-reading-metadata-frame] > [data-mcx-reading-metadata] {
|
|
35
|
+
position: absolute;
|
|
36
|
+
inset: 0 0 auto;
|
|
37
|
+
z-index: 1;
|
|
38
|
+
max-height: none;
|
|
39
|
+
overflow: visible;
|
|
40
|
+
}
|
|
41
|
+
.cm-editor[data-minecodex-markdown-reading] > .cm-scroller > .cm-content {
|
|
42
|
+
font-family: inherit;
|
|
43
|
+
font-size: inherit;
|
|
44
|
+
line-height: inherit;
|
|
45
|
+
padding: 0;
|
|
46
|
+
}
|
|
47
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line {
|
|
48
|
+
padding-block: 0;
|
|
49
|
+
}
|
|
50
|
+
/* 空行继续占位,保留原生文档位置和光标契约;不插入或删除段落。 */
|
|
51
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line:has(> br:only-child) {
|
|
52
|
+
line-height: 1em;
|
|
53
|
+
}
|
|
54
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line:has(.file-editor-heading) {
|
|
55
|
+
padding-block-start: 1em;
|
|
56
|
+
}
|
|
57
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line:first-child:has(.file-editor-heading) {
|
|
58
|
+
padding-block-start: 0;
|
|
59
|
+
}
|
|
60
|
+
.cm-editor[data-minecodex-markdown-reading] .file-editor-heading {
|
|
61
|
+
font-weight: var(--mcx-font-weight-semibold, 600);
|
|
62
|
+
}
|
|
63
|
+
.cm-editor[data-minecodex-markdown-reading] .file-editor-heading-1 {
|
|
64
|
+
font-size: calc(var(--mcx-text-document, 14px) * 1.5);
|
|
65
|
+
line-height: calc(var(--mcx-text-document, 14px) * 2);
|
|
66
|
+
}
|
|
67
|
+
.cm-editor[data-minecodex-markdown-reading] .file-editor-heading-2 {
|
|
68
|
+
font-size: calc(var(--mcx-text-document, 14px) * 1.25);
|
|
69
|
+
line-height: calc(var(--mcx-text-document, 14px) * 1.75);
|
|
70
|
+
}
|
|
71
|
+
.cm-editor[data-minecodex-markdown-reading] .file-editor-heading-3 {
|
|
72
|
+
font-size: calc(var(--mcx-text-document, 14px) * 1.125);
|
|
73
|
+
line-height: calc(var(--mcx-text-document, 14px) * 1.75);
|
|
74
|
+
}
|
|
75
|
+
.cm-editor[data-minecodex-markdown-reading] :is(.file-editor-heading-4, .file-editor-heading-5, .file-editor-heading-6) {
|
|
76
|
+
font-size: var(--mcx-text-document, 14px);
|
|
77
|
+
line-height: calc(var(--mcx-text-document, 14px) * 1.5);
|
|
78
|
+
}
|
|
79
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-markdown-list-item {
|
|
80
|
+
padding-block: .125em;
|
|
81
|
+
}
|
|
82
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-markdown-blockquote {
|
|
83
|
+
padding-block: .25em;
|
|
84
|
+
}
|
|
85
|
+
/* 代码内部继续使用原生等宽与高亮,不随正文行高扩张。 */
|
|
86
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-markdown-code-line {
|
|
87
|
+
line-height: 20px;
|
|
88
|
+
}
|
|
89
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-markdown-code-line-first {
|
|
90
|
+
padding-block-start: 12px;
|
|
91
|
+
}
|
|
92
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-markdown-code-line-last {
|
|
93
|
+
padding-block-end: 12px;
|
|
94
|
+
}
|
|
95
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-markdown-table-cell {
|
|
96
|
+
padding: 8px 12px;
|
|
97
|
+
}
|
|
98
|
+
/* 原生分隔行必须保持零高,避免覆盖 CodeMirror 的表格语义装饰。 */
|
|
99
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-markdown-table-separator {
|
|
100
|
+
padding: 0;
|
|
101
|
+
line-height: 0;
|
|
102
|
+
}
|
|
103
|
+
/* Pages 列表的标记列与正文列;装饰由原生 CodeMirror 持有。 */
|
|
104
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line.mcx-reading-list-row {
|
|
105
|
+
position: relative;
|
|
106
|
+
padding-inline-start: calc(var(--mcx-list-indent) + var(--mcx-list-marker-width));
|
|
107
|
+
padding-block: .125em;
|
|
108
|
+
}
|
|
109
|
+
/* 标记固定在首行的 1lh 容器中居中,不随整项的多行高度移动。 */
|
|
110
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-list-marker {
|
|
111
|
+
display: inline-flex;
|
|
112
|
+
inline-size: var(--mcx-list-marker-width);
|
|
113
|
+
position: absolute;
|
|
114
|
+
inset-inline-start: var(--mcx-list-indent);
|
|
115
|
+
inset-block-start: .125em;
|
|
116
|
+
block-size: 1lh;
|
|
117
|
+
justify-content: flex-start;
|
|
118
|
+
align-items: center;
|
|
119
|
+
color: inherit;
|
|
120
|
+
user-select: none;
|
|
121
|
+
}
|
|
122
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-list-marker:is(.mcx-reading-disc,.mcx-reading-circle,.mcx-reading-square)::before {
|
|
123
|
+
content: '';
|
|
124
|
+
inline-size: 5px;
|
|
125
|
+
block-size: 5px;
|
|
126
|
+
margin-inline-start: 7px;
|
|
127
|
+
align-self: center;
|
|
128
|
+
background: currentColor;
|
|
129
|
+
border-radius: 50%;
|
|
130
|
+
}
|
|
131
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-list-marker.mcx-reading-circle::before {
|
|
132
|
+
background: transparent;
|
|
133
|
+
border: 1px solid currentColor;
|
|
134
|
+
}
|
|
135
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-list-marker.mcx-reading-square::before { border-radius: 0; }
|
|
136
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-list-marker:is(.mcx-reading-task-empty,.mcx-reading-task-checked) {
|
|
137
|
+
color: var(--color-text-tertiary);
|
|
138
|
+
}
|
|
139
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-list-marker:is(.mcx-reading-task-empty,.mcx-reading-task-checked) > span {
|
|
140
|
+
box-sizing: border-box;
|
|
141
|
+
inline-size: 16px;
|
|
142
|
+
block-size: 16px;
|
|
143
|
+
border: .9px solid var(--color-text-tertiary);
|
|
144
|
+
border-radius: 2px;
|
|
145
|
+
display: inline-grid;
|
|
146
|
+
place-content: center;
|
|
147
|
+
}
|
|
148
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-list-marker.mcx-reading-task-checked > span { background: currentColor; border-color: currentColor; }
|
|
149
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-task-checked > span::before {
|
|
150
|
+
content: '';
|
|
151
|
+
inline-size: 4px;
|
|
152
|
+
block-size: 8px;
|
|
153
|
+
border-inline-end: 2px solid var(--color-text-inverse);
|
|
154
|
+
border-block-end: 2px solid var(--color-text-inverse);
|
|
155
|
+
transform: translateY(-1px) rotate(45deg);
|
|
156
|
+
}
|
|
157
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-task-done {
|
|
158
|
+
color: var(--color-text-tertiary);
|
|
159
|
+
text-decoration: line-through;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Pages 细网格与顶对齐;宽表格只在本表内浏览,各行横向位置由 Adapter 同步。 */
|
|
163
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line.cm-markdown-table-row.mcx-reading-table-row {
|
|
164
|
+
position: relative;
|
|
165
|
+
overflow-x: hidden;
|
|
166
|
+
scroll-behavior: auto;
|
|
167
|
+
animation-range: 1px calc(100% - 1px);
|
|
168
|
+
scrollbar-width: none;
|
|
169
|
+
border-block-start: 0;
|
|
170
|
+
border-inline-start: var(--border-width-hairline, .5px) solid transparent;
|
|
171
|
+
font-variant-numeric: tabular-nums;
|
|
172
|
+
font-weight: var(--font-weight-normal, 400);
|
|
173
|
+
font-feature-settings: "liga" 0;
|
|
174
|
+
align-items: stretch;
|
|
175
|
+
}
|
|
176
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line.cm-markdown-table-header.mcx-reading-table-row {
|
|
177
|
+
border-block-start: var(--border-width-hairline, .5px) solid transparent;
|
|
178
|
+
--mcx-table-top-inset: 16px;
|
|
179
|
+
}
|
|
180
|
+
/* 边框只包住单元格,顶部操作区和底部滚动条留白不属于表格。 */
|
|
181
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-row::before {
|
|
182
|
+
content: '';
|
|
183
|
+
position: absolute;
|
|
184
|
+
inset: var(--mcx-table-top-inset, 0px) 0 var(--mcx-table-bottom-inset, 0px);
|
|
185
|
+
border-inline-start: var(--border-width-hairline, .5px) solid var(--color-border);
|
|
186
|
+
pointer-events: none;
|
|
187
|
+
}
|
|
188
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-markdown-table-header.mcx-reading-table-row::before {
|
|
189
|
+
border-block-start: var(--border-width-hairline, .5px) solid var(--color-border);
|
|
190
|
+
}
|
|
191
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-row::after {
|
|
192
|
+
content: '';
|
|
193
|
+
position: absolute;
|
|
194
|
+
inset: 0 auto auto 0;
|
|
195
|
+
width: var(--mcx-table-scroll-width, 0px);
|
|
196
|
+
height: 1px;
|
|
197
|
+
pointer-events: none;
|
|
198
|
+
}
|
|
199
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-row > span[contenteditable=false]:empty:not(.cm-markdown-table-cell) {
|
|
200
|
+
display: none;
|
|
201
|
+
}
|
|
202
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-row .cm-markdown-table-cell {
|
|
203
|
+
box-sizing: border-box;
|
|
204
|
+
flex: 0 0 auto;
|
|
205
|
+
white-space: break-spaces;
|
|
206
|
+
overflow-wrap: break-word;
|
|
207
|
+
word-break: normal;
|
|
208
|
+
border-inline-end: var(--border-width-hairline, .5px) solid var(--color-border);
|
|
209
|
+
border-block-end: 1px solid var(--color-border);
|
|
210
|
+
text-align: start;
|
|
211
|
+
}
|
|
212
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-markdown-table-header .cm-markdown-table-cell {
|
|
213
|
+
font-weight: var(--mcx-font-weight-semibold, 600);
|
|
214
|
+
}
|
|
215
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line.mcx-reading-table-last {
|
|
216
|
+
padding-block-end: 12px;
|
|
217
|
+
--mcx-table-bottom-inset: 12px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* Pages full-width 表格:正文宽度不限制宽表,测量层不参与滚动和选区。 */
|
|
221
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-measure {
|
|
222
|
+
position: absolute;
|
|
223
|
+
width: calc(100cqi - var(--mcx-reading-table-wide-margin) * 2);
|
|
224
|
+
inset: 0 auto auto 0;
|
|
225
|
+
visibility: hidden;
|
|
226
|
+
pointer-events: none;
|
|
227
|
+
font-weight: var(--font-weight-normal, 400);
|
|
228
|
+
font-feature-settings: "liga" 0;
|
|
229
|
+
contain: layout style;
|
|
230
|
+
overflow: hidden;
|
|
231
|
+
font-family: var(--mcx-font-content, sans-serif);
|
|
232
|
+
font-size: var(--mcx-text-document, 14px);
|
|
233
|
+
line-height: var(--mcx-leading-relaxed, 1.625);
|
|
234
|
+
}
|
|
235
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-measure table {
|
|
236
|
+
inline-size: max-content;
|
|
237
|
+
min-inline-size: 100%;
|
|
238
|
+
max-inline-size: none;
|
|
239
|
+
border-collapse: separate;
|
|
240
|
+
border-spacing: 0;
|
|
241
|
+
border-inline-start: var(--border-width-hairline, .5px) solid var(--color-border);
|
|
242
|
+
font-variant-numeric: tabular-nums;
|
|
243
|
+
}
|
|
244
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-measure :is(td, th) {
|
|
245
|
+
padding: 8px 12px;
|
|
246
|
+
border-inline-end: var(--border-width-hairline, .5px) solid var(--color-border);
|
|
247
|
+
border-block-end: 1px solid var(--color-border);
|
|
248
|
+
white-space: break-spaces;
|
|
249
|
+
overflow-wrap: break-word;
|
|
250
|
+
word-break: normal;
|
|
251
|
+
vertical-align: top;
|
|
252
|
+
text-align: start;
|
|
253
|
+
}
|
|
254
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-measure th { font-weight: var(--mcx-font-weight-semibold, 600); }
|
|
255
|
+
|
|
256
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-scrollbar {
|
|
257
|
+
position: absolute;
|
|
258
|
+
height: 12px;
|
|
259
|
+
overflow-x: scroll;
|
|
260
|
+
overflow-y: hidden;
|
|
261
|
+
scrollbar-width: thin;
|
|
262
|
+
scrollbar-color: var(--color-text-tertiary) transparent;
|
|
263
|
+
z-index: 1;
|
|
264
|
+
}
|
|
265
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-scrollbar > div { height: 1px; }
|
|
266
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-measure code {
|
|
267
|
+
font-family: var(--font-mono);
|
|
268
|
+
font-size: .92em;
|
|
269
|
+
font-weight: 400;
|
|
270
|
+
padding: 1px 6px;
|
|
271
|
+
border-radius: 6px;
|
|
272
|
+
background: color-mix(in srgb, var(--color-background-primary-ghost-hover) 60%, var(--color-text) 6%);
|
|
273
|
+
box-decoration-break: clone;
|
|
274
|
+
overflow-wrap: anywhere;
|
|
275
|
+
word-break: break-word;
|
|
276
|
+
}
|
|
277
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-measure strong { font-weight: var(--mcx-font-weight-semibold, 600); }
|
|
278
|
+
|
|
279
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-scrollbar:focus-visible { outline: 2px solid var(--color-text); outline-offset: 1px; }
|
|
280
|
+
|
|
281
|
+
/* 滚动条跟随整张表的 hover;操作区与聚焦时可达。 */
|
|
282
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-scrollbar {
|
|
283
|
+
opacity: 0;
|
|
284
|
+
pointer-events: none;
|
|
285
|
+
}
|
|
286
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-scrollbar:is([data-visible=true], :hover, :focus-within) {
|
|
287
|
+
opacity: 1;
|
|
288
|
+
pointer-events: auto;
|
|
289
|
+
}
|
|
290
|
+
.cm-editor[data-minecodex-markdown-reading] [data-mcx-reading-table-actions] { position: absolute; inset: 0 auto auto 0; width: 100%; height: 0; pointer-events: none; z-index: 3; }
|
|
291
|
+
@media (hover:none) {
|
|
292
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-table-scrollbar { opacity:1;pointer-events:auto; }
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
/* 快捷操作放在表头上方,避免适配宽度时遮住最后一列标题。 */
|
|
297
|
+
/* 留白必须计入 CodeMirror 的行高,外边距会造成点击坐标累积偏移。 */
|
|
298
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-markdown-table-header.mcx-reading-table-row { padding-block-start:16px; }
|
|
299
|
+
|
|
300
|
+
/* 语法标记行的隐藏不能留下正文行高。 */
|
|
301
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line.mcx-reading-syntax-line {
|
|
302
|
+
padding: 0; min-height: 0; height: 0; font-size: 0; line-height: 0;
|
|
303
|
+
}
|
|
304
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line.mcx-reading-quote {
|
|
305
|
+
border-inline-start: 0;
|
|
306
|
+
padding-inline-start: calc(var(--mcx-quote-indent) + 8px);
|
|
307
|
+
background-image: repeating-linear-gradient(to right, var(--color-border) 0 2px, transparent 2px 20px);
|
|
308
|
+
background-size: var(--mcx-quote-indent) 100%;
|
|
309
|
+
background-repeat: no-repeat;
|
|
310
|
+
color: var(--color-text-secondary);
|
|
311
|
+
}
|
|
312
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line.mcx-reading-quote.mcx-reading-list-row {
|
|
313
|
+
padding-inline-start: calc(var(--mcx-list-indent) + var(--mcx-list-marker-width));
|
|
314
|
+
}
|
|
315
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-alert-title {font-weight:var(--font-weight-semibold,600); color:var(--color-text-primary);}
|
|
316
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-rich-code {margin-block:16px; max-width:100%;}
|
|
317
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-code-block {position:relative; min-width:0;}
|
|
318
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-code-block [data-block-actions] {top:4px; right:4px; transform:none; transition:opacity 120ms linear;}
|
|
319
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-code-block:focus-within [data-block-actions],
|
|
320
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-code-block:hover [data-block-actions] {opacity:1; pointer-events:auto;}
|
|
321
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-code-scroll {max-width:100%; scrollbar-width:thin;}
|
|
322
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-rich-html-block {margin-block:1em;}
|
|
323
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-rich mark {background:color-mix(in srgb, var(--color-content-yellow) 25%, transparent); color:inherit; border-radius:2px; padding-inline:2px;}
|
|
324
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-rich kbd {font-family:var(--font-mono,monospace); font-size:.9em; background:var(--color-token-bg-secondary); border:1px solid var(--color-border); border-radius:4px; padding:1px 4px;}
|
|
325
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-rich a,
|
|
326
|
+
.cm-editor[data-minecodex-markdown-reading] :is(.mcx-reading-rich-footnote-ref,.mcx-reading-footnote-body) button {color:var(--color-text-accent, var(--color-token-text-link-foreground)); cursor:var(--cursor-interaction,pointer);}
|
|
327
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-rich-footnote-ref button {font:inherit; padding:0 2px; background:transparent; border:0;}
|
|
328
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-footnote-body {font-size:.9em; color:var(--color-text-secondary);}
|
|
329
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-footnote-body button {margin-inline-start:8px; background:transparent; border:0;}
|
|
330
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-definition-term {font-weight:var(--font-weight-semibold,600);}
|
|
331
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-definition-body {padding-inline-start:1.75em;}
|
|
332
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-footnote-content > div,
|
|
333
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-rich-html span > div {display:inline;}
|
|
334
|
+
|
|
335
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-code-scroll::-webkit-scrollbar {height:6px;}
|
|
336
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-code-scroll::-webkit-scrollbar-thumb {background:transparent; border-radius:99px;}
|
|
337
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-code-block:hover .mcx-reading-code-scroll::-webkit-scrollbar-thumb,
|
|
338
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-code-block:focus-within .mcx-reading-code-scroll::-webkit-scrollbar-thumb {background:var(--color-text-tertiary);}
|
|
339
|
+
|
|
340
|
+
.cm-editor[data-minecodex-markdown-reading] .cm-line.mcx-reading-callout {
|
|
341
|
+
background:var(--color-token-bg-secondary); padding-inline:16px; color:var(--color-text-primary);
|
|
342
|
+
}
|
|
343
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-callout-first {padding-block-start:12px; border-start-start-radius:12px; border-start-end-radius:12px;}
|
|
344
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-callout-last {padding-block-end:12px; border-end-start-radius:12px; border-end-end-radius:12px;}
|
|
345
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-footnote-content > div {font-size:inherit; line-height:inherit;}
|
|
346
|
+
.cm-editor[data-minecodex-markdown-reading] .mcx-reading-footnote-body button {font:inherit; vertical-align:baseline; padding:0;}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lucide Icons and Contributors
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
The following Lucide icons are derived from the Feather project:
|
|
20
|
+
|
|
21
|
+
airplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out
|
|
22
|
+
|
|
23
|
+
The MIT License (MIT) (for the icons listed above)
|
|
24
|
+
|
|
25
|
+
Copyright (c) 2013-present Cole Bemis
|
|
26
|
+
|
|
27
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
28
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
29
|
+
in the Software without restriction, including without limitation the rights
|
|
30
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
31
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
32
|
+
furnished to do so, subject to the following conditions:
|
|
33
|
+
|
|
34
|
+
The above copyright notice and this permission notice shall be included in all
|
|
35
|
+
copies or substantial portions of the Software.
|
|
36
|
+
|
|
37
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
38
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
39
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
40
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
41
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
42
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
43
|
+
SOFTWARE.
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
export function createModelControllerAdapter(codex,comparableModelLabel) {
|
|
2
2
|
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;}
|
|
3
|
-
function
|
|
4
|
-
|
|
3
|
+
function nativeComposerTriggers() {
|
|
4
|
+
return Array.from(document.querySelectorAll("[data-codex-intelligence-trigger]"))
|
|
5
5
|
.filter((trigger) => trigger.checkVisibility({ checkVisibilityCSS: true, checkOpacity: true }));
|
|
6
|
+
}
|
|
7
|
+
function nativeComposerTrigger() {
|
|
8
|
+
const triggers = nativeComposerTriggers();
|
|
6
9
|
return triggers.find((trigger) => trigger.getAttribute("data-state") === "open")
|
|
7
10
|
?? triggers[0] ?? null;
|
|
8
11
|
}
|
|
9
|
-
function nativeComposerModelController() {
|
|
12
|
+
function nativeComposerModelController(trigger = nativeComposerTrigger()) {
|
|
10
13
|
codex.report("model-controller",false,"task-controller-unavailable");
|
|
11
|
-
const trigger = nativeComposerTrigger();
|
|
12
14
|
let fiber = codex.currentFiber(trigger);
|
|
13
15
|
let powerController = null;
|
|
14
16
|
for (let depth = 0; fiber && depth < 40; depth += 1, fiber = fiber.return) {
|
|
@@ -102,5 +104,5 @@ function selfCheck() {
|
|
|
102
104
|
return {state:available ? "available" : "unavailable",reason:available ? null : "model-controller-unavailable",
|
|
103
105
|
scopes:[{name:"model-trigger",element:trigger}],checks:[{name:"model-trigger",available:true},{name:"model-controller",available}]};
|
|
104
106
|
}
|
|
105
|
-
return {selfCheck,fastIcons,trigger:nativeComposerTrigger,read:nativeComposerModelController,descriptor:nativeModelDescriptorForItem,setOpen:setReactControlledMenuOpen,selectItem:invokeNativeMenuItemSelect};
|
|
107
|
+
return {selfCheck,fastIcons,trigger:nativeComposerTrigger,triggers:nativeComposerTriggers,read:nativeComposerModelController,descriptor:nativeModelDescriptorForItem,setOpen:setReactControlledMenuOpen,selectItem:invokeNativeMenuItemSelect};
|
|
106
108
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { existsSync, mkdirSync, chmodSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
|
|
6
|
+
export function defaultFavoritesPath() {
|
|
7
|
+
const support = process.platform === 'win32' ? path.join(process.env.APPDATA || os.homedir(), 'MineCodex') : path.join(os.homedir(), 'Library', 'Application Support', 'MineCodex');
|
|
8
|
+
return process.env.MINECODEX_MODEL_DATA_FILE || path.join(support, 'model-slider', 'favorites.sqlite');
|
|
9
|
+
}
|
|
10
|
+
export function validateFavorites(data) {
|
|
11
|
+
if (!data || data.version !== 1 || !Array.isArray(data.favorites) || data.favorites.length > 10000 || data.favorites.some(id => typeof id !== 'string' || !id || id.length > 512 || /[\x00-\x1f]/.test(id))) throw new Error('Invalid model favorites');
|
|
12
|
+
return { version: 1, favorites: [...new Set(data.favorites)] };
|
|
13
|
+
}
|
|
14
|
+
export class ModelFavorites {
|
|
15
|
+
constructor(file = defaultFavoritesPath()) { this.file = file; }
|
|
16
|
+
read() {
|
|
17
|
+
if (!existsSync(this.file)) return { version: 1, favorites: [], initialized: false };
|
|
18
|
+
const db = new DatabaseSync(this.file, { readOnly: true });
|
|
19
|
+
try { return { version: 1, favorites: db.prepare('SELECT id FROM favorites ORDER BY id').all().map(row => row.id), initialized: Boolean(db.prepare("SELECT 1 FROM metadata WHERE key='initialized'").get()) }; }
|
|
20
|
+
finally { db.close(); }
|
|
21
|
+
}
|
|
22
|
+
action(payload) {
|
|
23
|
+
if (!payload || !['initialize','change','read','merge'].includes(payload.operation)) throw new Error('Unknown favorites operation');
|
|
24
|
+
if (payload.operation === 'read') return this.read();
|
|
25
|
+
const additions = validateFavorites({ version: 1, favorites: payload.add ?? [] }).favorites;
|
|
26
|
+
const removals = validateFavorites({ version: 1, favorites: payload.remove ?? [] }).favorites;
|
|
27
|
+
mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 });
|
|
28
|
+
const db = new DatabaseSync(this.file);
|
|
29
|
+
chmodSync(this.file, 0o600);
|
|
30
|
+
try {
|
|
31
|
+
db.exec('PRAGMA busy_timeout=3000; CREATE TABLE IF NOT EXISTS favorites(id TEXT PRIMARY KEY); CREATE TABLE IF NOT EXISTS metadata(key TEXT PRIMARY KEY); BEGIN IMMEDIATE');
|
|
32
|
+
const before = new Set(db.prepare('SELECT id FROM favorites').all().map(row => row.id));
|
|
33
|
+
const initialized = db.prepare("SELECT 1 FROM metadata WHERE key='initialized'").get();
|
|
34
|
+
let added = 0;
|
|
35
|
+
if (payload.operation !== 'initialize' || !initialized) {
|
|
36
|
+
for (const id of additions) { if (!before.has(id)) added++; db.prepare('INSERT OR IGNORE INTO favorites VALUES (?)').run(id); }
|
|
37
|
+
if (payload.operation === 'change') for (const id of removals) db.prepare('DELETE FROM favorites WHERE id=?').run(id);
|
|
38
|
+
}
|
|
39
|
+
if (payload.operation === 'initialize') db.prepare("INSERT OR IGNORE INTO metadata VALUES ('initialized')").run();
|
|
40
|
+
const favorites = db.prepare('SELECT id FROM favorites ORDER BY id').all().map(row => row.id);
|
|
41
|
+
db.exec('COMMIT'); return { version: 1, favorites, added, duplicate: additions.length - added, conflict: 0 };
|
|
42
|
+
} catch (error) { if (db.isTransaction) db.exec('ROLLBACK'); throw error; }
|
|
43
|
+
finally { db.close(); }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function favoritesTransfer(file) {
|
|
47
|
+
const store = new ModelFavorites(file);
|
|
48
|
+
return {
|
|
49
|
+
id: 'model-slider', validate: validateFavorites,
|
|
50
|
+
async snapshot() { const data = store.read(); return { data: { version: 1, favorites: data.favorites }, count: data.favorites.length, excluded: 0, initialized: data.initialized }; },
|
|
51
|
+
async preview(data) { const current = new Set(store.read().favorites); const added = data.favorites.filter(id => !current.has(id)).length; return { added, duplicate: data.favorites.length - added, conflict: 0 }; },
|
|
52
|
+
async apply(data) { const { added, duplicate, conflict } = store.action({ operation:'merge',add:data.favorites }); return { added, duplicate, conflict }; },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -110,6 +110,11 @@ export function createModelRules() {
|
|
|
110
110
|
return formatIdentifier(identity.backend).toLowerCase() === nativeLabel ? native : thirdParty;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
function favoriteModelId(raw, { authoritative = false, nativeSlug, catalogSlug } = {}) {
|
|
114
|
+
// 控制器 ID 和 provider/model 不经过显示名称别名匹配,跨机器只认同一完整标识。
|
|
115
|
+
return authoritative || modelIdentity(raw).provider ? raw : nativeSlug ?? catalogSlug ?? raw;
|
|
116
|
+
}
|
|
117
|
+
|
|
113
118
|
function nativeGptSortRank(rawValue) {
|
|
114
119
|
const known = new Map([
|
|
115
120
|
["gpt-6-astra", 1], ["gpt-5.6-sol", 2], ["gpt-5.6-terra", 3], ["gpt-5.6-luna", 4],
|
|
@@ -139,5 +144,5 @@ export function createModelRules() {
|
|
|
139
144
|
);
|
|
140
145
|
});
|
|
141
146
|
}
|
|
142
|
-
return { modelIdentity, formatIdentifier, providerLabel, compactModelLabel, brandForModel, comparableModelLabel, catalogModelFor, resolveModelAlias, modelWithOpenCodexEfforts, nativeGptSortRank, visibleControllerModels };
|
|
147
|
+
return { favoriteModelId, modelIdentity, formatIdentifier, providerLabel, compactModelLabel, brandForModel, comparableModelLabel, catalogModelFor, resolveModelAlias, modelWithOpenCodexEfforts, nativeGptSortRank, visibleControllerModels };
|
|
143
148
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// 专有原生控制器适配与呈现由插件维护;宿主只提供刷新和共用 UI。
|
|
2
|
-
export function installModelSlider(signal, { feature, ui, createIcon, requestRefresh, locale, codex }, rules, createModelControllerAdapter) {
|
|
2
|
+
export function installModelSlider(signal, { feature, ui, createIcon, requestRefresh, locale, codex, requestAction }, rules, createModelControllerAdapter) {
|
|
3
3
|
const modelSelectorFeature = feature;
|
|
4
4
|
const featureUI = ui, createMarkupIcon = createIcon, queueEnsure = requestRefresh;
|
|
5
|
-
const { modelIdentity, formatIdentifier, providerLabel, compactModelLabel, brandForModel, comparableModelLabel, catalogModelFor, resolveModelAlias, modelWithOpenCodexEfforts, nativeGptSortRank, visibleControllerModels } = rules;
|
|
5
|
+
const { favoriteModelId, modelIdentity, formatIdentifier, providerLabel, compactModelLabel, brandForModel, comparableModelLabel, catalogModelFor, resolveModelAlias, modelWithOpenCodexEfforts, nativeGptSortRank, visibleControllerModels } = rules;
|
|
6
6
|
const modelSelectorOriginalToggleLabels = new Map();
|
|
7
7
|
const modelSelectorGenericContainers = new Map();
|
|
8
8
|
const modelSelectorNativeToggleHandlers = new Map();
|
|
@@ -13,8 +13,7 @@ export function installModelSlider(signal, { feature, ui, createIcon, requestRef
|
|
|
13
13
|
let modelSelectorEffortTransactionTimer = null;
|
|
14
14
|
let modelSelectorPendingOutsideDismiss = false;
|
|
15
15
|
let modelSelectorSuppressOutsidePointerSequence = false;
|
|
16
|
-
|
|
17
|
-
let modelSelectorObservedTrigger = null;
|
|
16
|
+
const modelSelectorTriggerObservers = new Map();
|
|
18
17
|
let modelSelectorControllerMenu = null;
|
|
19
18
|
let modelSelectorNativeFastIcons = null;
|
|
20
19
|
// 持久化脏标记:仅在图标实际变化时写一次 localStorage。
|
|
@@ -44,7 +43,7 @@ export function installModelSlider(signal, { feature, ui, createIcon, requestRef
|
|
|
44
43
|
}
|
|
45
44
|
html[data-codex-model-slider-catalog-ready]
|
|
46
45
|
[data-codex-model-slider-trigger]
|
|
47
|
-
[class*="_ModelPickerTriggerModelText_"]:not([data-codex-model-slider-trigger-label]) {
|
|
46
|
+
[class*="_ModelPickerTriggerModelText_"]:not([data-codex-model-slider-trigger-label]):has(+ [data-codex-model-slider-trigger-label]) {
|
|
48
47
|
display: none !important;
|
|
49
48
|
}
|
|
50
49
|
[data-codex-model-slider-trigger-label] {
|
|
@@ -375,7 +374,17 @@ export function installModelSlider(signal, { feature, ui, createIcon, requestRef
|
|
|
375
374
|
}
|
|
376
375
|
}
|
|
377
376
|
|
|
377
|
+
let favoritesWrite = Promise.resolve();
|
|
378
|
+
let favoritesEpoch = 0;
|
|
378
379
|
function saveModelFavorites(selector, favorites) {
|
|
380
|
+
const previous = loadModelFavorites(selector);
|
|
381
|
+
favoritesEpoch++;
|
|
382
|
+
if (requestAction) {
|
|
383
|
+
const add = [...favorites].filter(id => !previous.has(id));
|
|
384
|
+
const remove = [...previous].filter(id => !favorites.has(id));
|
|
385
|
+
favoritesWrite = favoritesWrite.catch(() => {}).then(() => requestAction({ operation: 'change', add, remove }));
|
|
386
|
+
void favoritesWrite.catch(error => console.warn('Model favorites could not be saved for transfer', error));
|
|
387
|
+
}
|
|
379
388
|
try {
|
|
380
389
|
localStorage.setItem(selector.favoriteStorageKey, JSON.stringify(Array.from(favorites)));
|
|
381
390
|
} catch {
|
|
@@ -734,7 +743,7 @@ export function installModelSlider(signal, { feature, ui, createIcon, requestRef
|
|
|
734
743
|
const observedIdentity = modelIdentity(observedRaw);
|
|
735
744
|
const nativeDescriptor = nativeModelDescriptorForItem(item, observedRaw);
|
|
736
745
|
const catalogModel = catalogModelFor(observedIdentity, selector);
|
|
737
|
-
const raw =
|
|
746
|
+
const raw = favoriteModelId(observedRaw, { authoritative: rawValue !== null, nativeSlug: nativeDescriptor?.slug, catalogSlug: catalogModel?.slug });
|
|
738
747
|
// 先刷新 dataset:React 原地复用菜单节点时,raw/排序/收藏键不能基于过期值。
|
|
739
748
|
item.dataset.codexModelSliderRaw = raw;
|
|
740
749
|
item.dataset.codexModelSliderItem = "";
|
|
@@ -1990,14 +1999,11 @@ export function installModelSlider(signal, { feature, ui, createIcon, requestRef
|
|
|
1990
1999
|
return /^(select\s+(model|effort)|选择\s*(模型|思考强度))$/i.test(String(value ?? "").trim());
|
|
1991
2000
|
}
|
|
1992
2001
|
|
|
1993
|
-
function enhanceComposerModelTrigger(selector) {
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
modelSelectorObservedTrigger = trigger;
|
|
1999
|
-
modelSelectorTriggerObserver = new MutationObserver(queueEnsure);
|
|
2000
|
-
modelSelectorTriggerObserver.observe(trigger, { childList: true, subtree: true, characterData: true });
|
|
2002
|
+
function enhanceComposerModelTrigger(trigger, selector) {
|
|
2003
|
+
if (!modelSelectorTriggerObservers.has(trigger)) {
|
|
2004
|
+
const observer = new MutationObserver(queueEnsure);
|
|
2005
|
+
observer.observe(trigger, { childList: true, subtree: true, characterData: true });
|
|
2006
|
+
modelSelectorTriggerObservers.set(trigger, observer);
|
|
2001
2007
|
}
|
|
2002
2008
|
const nativeLabels = Array.from(trigger.querySelectorAll(
|
|
2003
2009
|
'[class*="_ModelPickerTriggerModelText_"]:not([data-codex-model-slider-trigger-label])',
|
|
@@ -2060,7 +2066,14 @@ export function installModelSlider(signal, { feature, ui, createIcon, requestRef
|
|
|
2060
2066
|
pruneGenericModelPickerContainers();
|
|
2061
2067
|
document.documentElement.setAttribute("data-codex-model-slider-catalog-ready", "");
|
|
2062
2068
|
if (!modelSelectorStyle.isConnected) document.head?.append(modelSelectorStyle);
|
|
2063
|
-
|
|
2069
|
+
// 主会话与 side chat 同时维护标签;菜单动作仍跟随原生打开的按钮。
|
|
2070
|
+
const triggers = nativeModel.triggers().filter(trigger => nativeModel.read(trigger));
|
|
2071
|
+
for (const [trigger, observer] of modelSelectorTriggerObservers) {
|
|
2072
|
+
if (triggers.includes(trigger)) continue;
|
|
2073
|
+
observer.disconnect();
|
|
2074
|
+
modelSelectorTriggerObservers.delete(trigger);
|
|
2075
|
+
}
|
|
2076
|
+
for (const trigger of triggers) enhanceComposerModelTrigger(trigger, selector);
|
|
2064
2077
|
const parentMenu = nativeModelPickerMenu();
|
|
2065
2078
|
if (modelSelectorControllerMenu && modelSelectorControllerMenu.parentMenu !== parentMenu) {
|
|
2066
2079
|
closeControllerModelMenu({ immediate: true });
|
|
@@ -2092,7 +2105,8 @@ export function installModelSlider(signal, { feature, ui, createIcon, requestRef
|
|
|
2092
2105
|
closeControllerModelMenu({ immediate: true });
|
|
2093
2106
|
clearTimeout(modelSelectorReturnTimer);
|
|
2094
2107
|
clearTimeout(modelSelectorEffortTransactionTimer);
|
|
2095
|
-
|
|
2108
|
+
for (const observer of modelSelectorTriggerObservers.values()) observer.disconnect();
|
|
2109
|
+
modelSelectorTriggerObservers.clear();
|
|
2096
2110
|
for (const record of modelSelectorPositionObservers.values()) {
|
|
2097
2111
|
record.observer.disconnect();
|
|
2098
2112
|
record.resizeObserver.disconnect();
|
|
@@ -2100,8 +2114,6 @@ export function installModelSlider(signal, { feature, ui, createIcon, requestRef
|
|
|
2100
2114
|
modelSelectorPositionObservers.clear();
|
|
2101
2115
|
for (const frame of modelSelectorMenuSizeFrames.values()) cancelAnimationFrame(frame);
|
|
2102
2116
|
modelSelectorMenuSizeFrames.clear();
|
|
2103
|
-
modelSelectorTriggerObserver = null;
|
|
2104
|
-
modelSelectorObservedTrigger = null;
|
|
2105
2117
|
modelSelectorReturnTimer = null;
|
|
2106
2118
|
modelSelectorEffortTransactionTimer = null;
|
|
2107
2119
|
modelSelectorPendingOutsideDismiss = false;
|
|
@@ -2163,6 +2175,17 @@ export function installModelSlider(signal, { feature, ui, createIcon, requestRef
|
|
|
2163
2175
|
&& !modelSelectorControllerMenu.menu.contains(event.target)
|
|
2164
2176
|
&& !modelSelectorControllerMenu.button.contains(event.target)) closeControllerModelMenu();
|
|
2165
2177
|
}, {capture:true, signal});
|
|
2178
|
+
if (modelSelectorFeature && requestAction) {
|
|
2179
|
+
const selector = modelSelectorFeature.modelSelector;
|
|
2180
|
+
const legacy = [...loadModelFavorites(selector)];
|
|
2181
|
+
// 仅首次接入时迁移旧收藏;后续以后端为准,避免取消收藏后被旧窗口恢复。
|
|
2182
|
+
favoritesWrite = requestAction({ operation: 'initialize', add: legacy }).then(result => {
|
|
2183
|
+
if (signal.aborted) return;
|
|
2184
|
+
if (favoritesEpoch === 0) localStorage.setItem(selector.favoriteStorageKey, JSON.stringify(result.favorites));
|
|
2185
|
+
queueEnsure();
|
|
2186
|
+
});
|
|
2187
|
+
void favoritesWrite.catch(error => console.warn('Model favorites transfer store unavailable', error));
|
|
2188
|
+
}
|
|
2166
2189
|
if (modelSelectorFeature) {
|
|
2167
2190
|
// 启动阶段就解析 Fast 图标:先读持久化的原生克隆,再从 app bundle 静态提取兜底,
|
|
2168
2191
|
// 避免首次会话必须等用户点开弹窗后 DOM 里才出现图标。
|