@airalogy/aimd-editor 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +59 -0
  2. package/README.zh-CN.md +43 -0
  3. package/dist/AimdEditorTopBar.vue_vue_type_script_setup_true_lang-gbfMDZSh.js +1131 -0
  4. package/dist/AimdSourceEditor.vue_vue_type_script_setup_true_lang-t_sUoXky.js +274 -0
  5. package/dist/AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-B8o1VbUH.js +25012 -0
  6. package/dist/aimd-editor.css +1 -0
  7. package/dist/embedded.js +11 -0
  8. package/dist/index.js +44 -0
  9. package/dist/monaco.js +16 -0
  10. package/dist/theme-B8dCnOx-.js +583 -0
  11. package/dist/vue.js +30 -0
  12. package/dist/wysiwyg.js +9 -0
  13. package/package.json +90 -0
  14. package/src/__tests__/editor.test.ts +296 -0
  15. package/src/embedded.ts +18 -0
  16. package/src/index.ts +10 -0
  17. package/src/language-config.ts +152 -0
  18. package/src/monaco.ts +19 -0
  19. package/src/theme.ts +166 -0
  20. package/src/tokens.ts +120 -0
  21. package/src/vue/AimdEditor.vue +715 -0
  22. package/src/vue/AimdEditorToolbar.vue +83 -0
  23. package/src/vue/AimdEditorTopBar.vue +39 -0
  24. package/src/vue/AimdFieldDialog.vue +1102 -0
  25. package/src/vue/AimdSourceEditor.vue +330 -0
  26. package/src/vue/AimdWysiwygEditor.vue +569 -0
  27. package/src/vue/aimdInlineMarkdownNormalization.ts +10 -0
  28. package/src/vue/comparableAimdMarkdown.ts +6 -0
  29. package/src/vue/env.d.ts +7 -0
  30. package/src/vue/index.ts +45 -0
  31. package/src/vue/locales.ts +667 -0
  32. package/src/vue/milkdown-aimd-plugin.ts +378 -0
  33. package/src/vue/programmaticMarkdownSyncGuard.ts +66 -0
  34. package/src/vue/types.ts +449 -0
  35. package/src/vue/useEditorContent.ts +252 -0
  36. package/src/wysiwyg.ts +17 -0
@@ -0,0 +1,715 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed, watch, onBeforeUnmount } from 'vue'
3
+ import type { Editor } from '@milkdown/kit/core'
4
+ import { replaceAll, getMarkdown } from '@milkdown/kit/utils'
5
+ import { protectAimdInlineTemplates } from '@airalogy/aimd-core'
6
+ import { parseAndExtract } from '@airalogy/aimd-renderer'
7
+
8
+ import '@milkdown/theme-nord/style.css'
9
+ import '@milkdown/kit/prose/tables/style/tables.css'
10
+
11
+ // Internal
12
+ import AimdFieldDialog from './AimdFieldDialog.vue'
13
+ import AimdEditorToolbar from './AimdEditorToolbar.vue'
14
+ import AimdSourceEditor from './AimdSourceEditor.vue'
15
+ import AimdWysiwygEditor from './AimdWysiwygEditor.vue'
16
+ import { createAimdEditorMessages } from './locales'
17
+ import {
18
+ createAimdFieldTypes,
19
+ createMdToolbarItems,
20
+ getQuickAimdSyntax,
21
+ type AimdEditorProps,
22
+ } from './types'
23
+ import { useEditorContent } from './useEditorContent'
24
+
25
+ const props = withDefaults(defineProps<AimdEditorProps>(), {
26
+ modelValue: '',
27
+ messages: () => ({}),
28
+ mode: 'source',
29
+ theme: 'aimd-light',
30
+ showTopBar: true,
31
+ showToolbar: true,
32
+ showAimdToolbar: true,
33
+ showMdToolbar: true,
34
+ enableBlockHandle: true,
35
+ enableSlashMenu: true,
36
+ keepInactiveEditorsMounted: true,
37
+ minHeight: 500,
38
+ readonly: false,
39
+ monacoOptions: () => ({}),
40
+ })
41
+
42
+ const emit = defineEmits<{
43
+ (e: 'update:modelValue', value: string): void
44
+ (e: 'update:mode', mode: 'source' | 'wysiwyg'): void
45
+ (e: 'ready', editor: { monaco?: any; milkdown?: Editor }): void
46
+ }>()
47
+
48
+ // --- Resolved messages ---
49
+ const resolvedMessages = computed(() => createAimdEditorMessages(props.locale, props.messages))
50
+
51
+ // --- Composable for content/mode/insertion logic ---
52
+ const {
53
+ editorMode,
54
+ content,
55
+ commitUserContent,
56
+ monacoEditor,
57
+ monacoInstance,
58
+ milkdownEditorRef,
59
+ extractedFields,
60
+ toMilkdownMarkdown,
61
+ switchMode,
62
+ syncFromProp,
63
+ insertTextIntoActiveEditor,
64
+ handleMdAction,
65
+ onMilkdownMarkdownUpdated,
66
+ onMilkdownReady: composableOnMilkdownReady,
67
+ } = useEditorContent({
68
+ initialContent: props.modelValue,
69
+ initialMode: props.mode,
70
+ resolvedMessages,
71
+ emitModelValue: (val) => emit('update:modelValue', val),
72
+ emitMode: (mode) => emit('update:mode', mode),
73
+ })
74
+
75
+ // Sync external modelValue changes
76
+ watch(() => props.modelValue, (val) => syncFromProp(val))
77
+
78
+ // Sync external mode changes
79
+ watch(() => props.mode, (m) => {
80
+ if (m !== editorMode.value) switchMode(m)
81
+ })
82
+
83
+ // --- Theme ---
84
+ const currentTheme = ref(props.theme)
85
+ function toggleTheme() {
86
+ currentTheme.value = currentTheme.value === 'aimd-light' ? 'aimd-dark' : 'aimd-light'
87
+ }
88
+
89
+ const shouldMountSourceEditor = computed(() => props.keepInactiveEditorsMounted || editorMode.value === 'source')
90
+ const shouldMountWysiwygEditor = computed(() => props.keepInactiveEditorsMounted || editorMode.value === 'wysiwyg')
91
+
92
+ // --- Computed toolbar items ---
93
+ const localizedFieldTypes = computed(() => createAimdFieldTypes(resolvedMessages.value))
94
+ const localizedMdToolbarItems = computed(() => createMdToolbarItems(resolvedMessages.value))
95
+
96
+ // --- AIMD Dialog ---
97
+ const showAimdDialog = ref(false)
98
+ const aimdDialogType = ref('var')
99
+
100
+ const refSuggestions = computed(() => {
101
+ const fields = extractedFields.value
102
+ if (!fields) return []
103
+ const type = aimdDialogType.value
104
+ if (type === 'ref_step') return fields.step || []
105
+ if (type === 'ref_var') return fields.var || []
106
+ if (type === 'ref_fig') return fields.ref_fig || []
107
+ return []
108
+ })
109
+
110
+ function openAimdDialog(type: string) {
111
+ aimdDialogType.value = type
112
+ showAimdDialog.value = true
113
+ }
114
+
115
+ function quickInsertAimd(type: string) {
116
+ insertTextIntoActiveEditor(getQuickAimdSyntax(type, resolvedMessages.value))
117
+ }
118
+
119
+ function onDialogInsert(syntax: string) {
120
+ insertTextIntoActiveEditor(syntax)
121
+ }
122
+
123
+ // --- Source editor refs ---
124
+ const sourceEditorRef = ref<InstanceType<typeof AimdSourceEditor> | null>(null)
125
+ const wysiwygEditorRef = ref<InstanceType<typeof AimdWysiwygEditor> | null>(null)
126
+
127
+ function onSourceContentChange(val: string) {
128
+ commitUserContent(val)
129
+ }
130
+
131
+ function onSourceReady(editor: any) {
132
+ emit('ready', { monaco: editor })
133
+ }
134
+
135
+ function onSourceMonacoLoaded(monaco: any, editor: any) {
136
+ monacoInstance.value = monaco
137
+ monacoEditor.value = editor
138
+ }
139
+
140
+ function onWysiwygReady(editor: Editor) {
141
+ composableOnMilkdownReady(editor)
142
+ emit('ready', { milkdown: editor })
143
+ }
144
+
145
+ // Expose for external access (same public API)
146
+ defineExpose({
147
+ getContent: () => content.value,
148
+ setContent: (val: string) => {
149
+ content.value = val
150
+ if (editorMode.value === 'source' && monacoEditor.value) {
151
+ monacoEditor.value.setValue(val)
152
+ } else if (milkdownEditorRef.value) {
153
+ try { milkdownEditorRef.value.action(replaceAll(toMilkdownMarkdown(val))) } catch {}
154
+ }
155
+ },
156
+ getMode: () => editorMode.value,
157
+ setMode: (mode: 'source' | 'wysiwyg') => switchMode(mode),
158
+ getMonacoEditor: () => monacoEditor.value,
159
+ getMilkdownEditor: () => milkdownEditorRef.value,
160
+ insertText: insertTextIntoActiveEditor,
161
+ })
162
+ </script>
163
+
164
+ <template>
165
+ <div class="aimd-editor">
166
+ <!-- Unified toolbar: mode switch + markdown + aimd -->
167
+ <AimdEditorToolbar
168
+ v-if="showToolbar"
169
+ :show-top-bar="showTopBar"
170
+ :show-md-toolbar="showMdToolbar"
171
+ :show-aimd-toolbar="showAimdToolbar"
172
+ :editor-mode="editorMode"
173
+ :resolved-messages="resolvedMessages"
174
+ :localized-field-types="localizedFieldTypes"
175
+ :localized-md-toolbar-items="localizedMdToolbarItems"
176
+ @switch-mode="switchMode"
177
+ @md-action="handleMdAction"
178
+ @open-aimd-dialog="openAimdDialog"
179
+ @quick-insert-aimd="quickInsertAimd"
180
+ />
181
+
182
+ <!-- Editor area -->
183
+ <div class="aimd-editor-panel" :style="{ minHeight: minHeight + 'px' }">
184
+ <!-- Source mode: Monaco -->
185
+ <div v-if="shouldMountSourceEditor" v-show="editorMode === 'source'">
186
+ <AimdSourceEditor
187
+ ref="sourceEditorRef"
188
+ :content="content"
189
+ :theme="currentTheme"
190
+ :min-height="minHeight"
191
+ :readonly="readonly"
192
+ :monaco-options="monacoOptions"
193
+ :resolved-messages="resolvedMessages"
194
+ @content-change="onSourceContentChange"
195
+ @ready="onSourceReady"
196
+ @monaco-loaded="onSourceMonacoLoaded"
197
+ />
198
+ </div>
199
+
200
+ <!-- WYSIWYG mode: Milkdown -->
201
+ <div v-if="shouldMountWysiwygEditor" v-show="editorMode === 'wysiwyg'">
202
+ <AimdWysiwygEditor
203
+ ref="wysiwygEditorRef"
204
+ :content="content"
205
+ :min-height="minHeight"
206
+ :enable-block-handle="enableBlockHandle"
207
+ :active="editorMode === 'wysiwyg'"
208
+ :resolved-messages="resolvedMessages"
209
+ :localized-field-types="localizedFieldTypes"
210
+ @markdown-updated="onMilkdownMarkdownUpdated"
211
+ @ready="onWysiwygReady"
212
+ @open-aimd-dialog="openAimdDialog"
213
+ />
214
+ </div>
215
+ </div>
216
+
217
+ <!-- AIMD Field Dialog -->
218
+ <AimdFieldDialog
219
+ :visible="showAimdDialog"
220
+ :initial-type="aimdDialogType"
221
+ :messages="resolvedMessages"
222
+ :ref-suggestions="refSuggestions"
223
+ :var-type-plugins="varTypePlugins"
224
+ @update:visible="showAimdDialog = $event"
225
+ @insert="onDialogInsert"
226
+ />
227
+ </div>
228
+ </template>
229
+
230
+ <style>
231
+ .aimd-editor {
232
+ display: flex;
233
+ flex-direction: column;
234
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
235
+ border: 1px solid #e0e0e0;
236
+ border-radius: 8px;
237
+ overflow: hidden;
238
+ }
239
+
240
+ /* --- Unified toolbar --- */
241
+ .aimd-editor-toolbar {
242
+ display: flex;
243
+ align-items: center;
244
+ gap: 2px;
245
+ padding: 4px 8px;
246
+ background: #fafbfc;
247
+ border-bottom: 1px solid #e0e0e0;
248
+ overflow-x: auto;
249
+ flex-wrap: wrap;
250
+ }
251
+
252
+ .aimd-editor-mode-switch {
253
+ display: flex;
254
+ background: #eceef1;
255
+ border-radius: 5px;
256
+ padding: 2px;
257
+ gap: 1px;
258
+ flex-shrink: 0;
259
+ }
260
+
261
+ .aimd-editor-mode-btn {
262
+ display: flex;
263
+ align-items: center;
264
+ gap: 4px;
265
+ padding: 4px 10px;
266
+ border: none;
267
+ border-radius: 4px;
268
+ background: transparent;
269
+ cursor: pointer;
270
+ font-size: 12px;
271
+ font-weight: 500;
272
+ color: #777;
273
+ transition: all 0.15s;
274
+ white-space: nowrap;
275
+ }
276
+
277
+ .aimd-editor-mode-btn:hover {
278
+ color: #444;
279
+ background: rgba(255,255,255,0.5);
280
+ }
281
+
282
+ .aimd-editor-mode-btn.active {
283
+ background: #fff;
284
+ color: #1a73e8;
285
+ box-shadow: 0 1px 2px rgba(0,0,0,0.08);
286
+ }
287
+
288
+ .aimd-editor-mode-icon {
289
+ display: flex;
290
+ align-items: center;
291
+ }
292
+
293
+ .aimd-editor-toolbar-sep {
294
+ width: 1px;
295
+ height: 20px;
296
+ background: #ddd;
297
+ margin: 0 4px;
298
+ flex-shrink: 0;
299
+ }
300
+
301
+ .aimd-editor-toolbar-divider {
302
+ width: 1px;
303
+ height: 22px;
304
+ background: #ccc;
305
+ margin: 0 6px;
306
+ flex-shrink: 0;
307
+ }
308
+
309
+ .aimd-editor-fmt-btn {
310
+ width: 28px;
311
+ height: 28px;
312
+ border: 1px solid transparent;
313
+ border-radius: 4px;
314
+ background: transparent;
315
+ cursor: pointer;
316
+ color: #555;
317
+ display: flex;
318
+ align-items: center;
319
+ justify-content: center;
320
+ transition: all 0.12s;
321
+ padding: 0;
322
+ flex-shrink: 0;
323
+ }
324
+
325
+ .aimd-editor-fmt-btn:hover {
326
+ background: #eceef1;
327
+ color: #222;
328
+ }
329
+
330
+ .aimd-editor-fmt-btn:active {
331
+ background: #dfe1e5;
332
+ }
333
+
334
+ .aimd-editor-fmt-btn svg {
335
+ width: 16px;
336
+ height: 16px;
337
+ display: block;
338
+ }
339
+
340
+ /* AIMD buttons */
341
+ .aimd-editor-aimd-btn {
342
+ width: auto;
343
+ gap: 3px;
344
+ padding: 0 7px;
345
+ border: 1px solid color-mix(in srgb, var(--aimd-color, #2563eb) 18%, transparent);
346
+ background: color-mix(in srgb, var(--aimd-color, #2563eb) 4%, transparent);
347
+ border-radius: 4px;
348
+ }
349
+
350
+ .aimd-editor-aimd-btn:hover {
351
+ background: color-mix(in srgb, var(--aimd-color, #2563eb) 12%, transparent);
352
+ border-color: color-mix(in srgb, var(--aimd-color, #2563eb) 35%, transparent);
353
+ color: var(--aimd-color, #2563eb);
354
+ }
355
+
356
+ .aimd-editor-aimd-btn-icon {
357
+ display: flex;
358
+ align-items: center;
359
+ }
360
+
361
+ .aimd-editor-aimd-btn-icon svg {
362
+ width: 13px;
363
+ height: 13px;
364
+ }
365
+
366
+ .aimd-editor-aimd-btn-label {
367
+ font-size: 11px;
368
+ font-weight: 500;
369
+ }
370
+
371
+ /* --- Editor panel --- */
372
+ .aimd-editor-panel {
373
+ background: #fff;
374
+ overflow: hidden;
375
+ }
376
+
377
+ .aimd-editor-source-mode {
378
+ overflow: hidden;
379
+ }
380
+
381
+ .aimd-editor-container {
382
+ height: 100%;
383
+ }
384
+
385
+ .aimd-editor-loading {
386
+ padding: 40px;
387
+ text-align: center;
388
+ color: #888;
389
+ }
390
+
391
+ /* --- WYSIWYG mode (Milkdown) --- */
392
+ .aimd-editor-wysiwyg-mode .milkdown {
393
+ height: 100%;
394
+ }
395
+
396
+ .aimd-editor-wysiwyg-mode .milkdown-editor-content {
397
+ padding: 0 20px 6px;
398
+ min-height: 100%;
399
+ outline: none;
400
+ font-size: 15px;
401
+ line-height: 1.8;
402
+ }
403
+
404
+ .aimd-editor-wysiwyg-mode .milkdown-editor-content .ProseMirror {
405
+ outline: none;
406
+ }
407
+
408
+ .aimd-editor-wysiwyg-mode h1 { font-size: 1.8em; margin: 0.6em 0 0.3em; font-weight: 700; }
409
+ .aimd-editor-wysiwyg-mode h2 { font-size: 1.4em; margin: 0.5em 0 0.3em; color: #333; font-weight: 600; border-bottom: 1px solid #eee; padding-bottom: 0.2em; }
410
+ .aimd-editor-wysiwyg-mode h3 { font-size: 1.2em; margin: 0.4em 0 0.2em; font-weight: 600; }
411
+ .aimd-editor-wysiwyg-mode p { margin: 0.5em 0; }
412
+ .aimd-editor-wysiwyg-mode blockquote { border-left: 4px solid #dfe2e5; padding: 8px 16px; margin: 8px 0; color: #666; background: #fafafa; border-radius: 0 4px 4px 0; }
413
+ .aimd-editor-wysiwyg-mode ul,
414
+ .aimd-editor-wysiwyg-mode ol { padding-left: 24px; margin: 4px 0; }
415
+ .aimd-editor-wysiwyg-mode code { background: #f0f2f5; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; font-family: 'SF Mono', 'Fira Code', monospace; }
416
+ .aimd-editor-wysiwyg-mode pre { background: #1e1e2e; color: #cdd6f4; padding: 16px; border-radius: 8px; overflow-x: auto; }
417
+ .aimd-editor-wysiwyg-mode pre code { background: none; padding: 0; color: inherit; }
418
+ .aimd-editor-wysiwyg-mode hr { border: none; border-top: 2px solid #e8e8e8; margin: 16px 0; }
419
+ .aimd-editor-wysiwyg-mode img { max-width: 100%; border-radius: 4px; }
420
+ .aimd-editor-wysiwyg-mode a { color: #1a73e8; text-decoration: none; }
421
+ .aimd-editor-wysiwyg-mode a:hover { text-decoration: underline; }
422
+ .aimd-editor-wysiwyg-mode li { margin: 2px 0; }
423
+ .aimd-editor-wysiwyg-mode .tableWrapper { overflow-x: auto; margin: 12px 0; }
424
+
425
+ /* Block handle (BlockProvider - follows cursor line) */
426
+ .aimd-block-handle {
427
+ position: absolute;
428
+ z-index: 10;
429
+ transition: top 0.15s ease, left 0.15s ease;
430
+ }
431
+ .aimd-block-handle[data-show="false"] {
432
+ opacity: 0;
433
+ pointer-events: none;
434
+ }
435
+ .aimd-block-handle-btn {
436
+ width: 24px;
437
+ height: 24px;
438
+ display: flex;
439
+ align-items: center;
440
+ justify-content: center;
441
+ border-radius: 4px;
442
+ background: #fff;
443
+ border: 1px solid #e0e0e0;
444
+ color: #aaa;
445
+ cursor: pointer;
446
+ transition: all 0.15s;
447
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05);
448
+ }
449
+ .aimd-block-handle-btn:hover {
450
+ background: #f0f2f5;
451
+ border-color: #c0c0c0;
452
+ color: #555;
453
+ }
454
+
455
+ /* Block add menu popup */
456
+ .aimd-block-add-menu {
457
+ position: fixed;
458
+ z-index: 10000;
459
+ background: #fff;
460
+ border: 1px solid #e0e0e0;
461
+ border-radius: 8px;
462
+ box-shadow: 0 4px 16px rgba(0,0,0,0.12);
463
+ padding: 4px;
464
+ min-width: 200px;
465
+ max-height: 400px;
466
+ overflow-y: auto;
467
+ }
468
+ .aimd-block-add-menu-group-label {
469
+ padding: 6px 12px 2px;
470
+ font-size: 11px;
471
+ font-weight: 600;
472
+ color: #999;
473
+ text-transform: uppercase;
474
+ letter-spacing: 0.5px;
475
+ }
476
+ .aimd-block-add-menu-divider {
477
+ height: 1px;
478
+ background: #eee;
479
+ margin: 4px 8px;
480
+ }
481
+ .aimd-block-add-menu-item {
482
+ display: flex;
483
+ align-items: center;
484
+ gap: 10px;
485
+ width: 100%;
486
+ padding: 8px 12px;
487
+ border: none;
488
+ background: none;
489
+ border-radius: 5px;
490
+ cursor: pointer;
491
+ font-size: 13px;
492
+ color: #333;
493
+ text-align: left;
494
+ transition: background 0.1s;
495
+ }
496
+ .aimd-block-add-menu-item:hover {
497
+ background: #f0f2f5;
498
+ }
499
+ .aimd-block-add-menu-icon {
500
+ width: 24px;
501
+ height: 24px;
502
+ display: flex;
503
+ align-items: center;
504
+ justify-content: center;
505
+ border-radius: 4px;
506
+ background: #f0f2f5;
507
+ color: #666;
508
+ flex-shrink: 0;
509
+ }
510
+
511
+ .aimd-block-add-menu-icon svg {
512
+ width: 14px;
513
+ height: 14px;
514
+ }
515
+
516
+ /* ── Table block component (official milkdown style) ── */
517
+ .milkdown-table-block {
518
+ display: block;
519
+ margin: 4px 0;
520
+ }
521
+ .milkdown-table-block table {
522
+ margin: 0 !important;
523
+ border-radius: 0 !important;
524
+ }
525
+ .milkdown-table-block .handle {
526
+ position: absolute;
527
+ z-index: 50;
528
+ cursor: grab;
529
+ font-size: 14px;
530
+ }
531
+ .milkdown-table-block .cell-handle {
532
+ left: -999px;
533
+ top: -999px;
534
+ z-index: 100;
535
+ height: 24px;
536
+ transition: opacity 0.2s ease 0.2s;
537
+ background-color: #a8d4ff;
538
+ padding: 0 8px;
539
+ border-radius: 8px;
540
+ position: absolute;
541
+ }
542
+ .milkdown-table-block .cell-handle[data-role="col-drag-handle"] {
543
+ transform: translateY(50%);
544
+ }
545
+ .milkdown-table-block .cell-handle[data-role="row-drag-handle"] {
546
+ transform: translateX(50%);
547
+ }
548
+ .milkdown-table-block .cell-handle[data-show="false"] {
549
+ opacity: 0;
550
+ }
551
+ .milkdown-table-block .line-handle[data-show="false"] {
552
+ opacity: 0;
553
+ }
554
+ .milkdown-table-block .handle:hover {
555
+ opacity: 1;
556
+ }
557
+ .milkdown-table-block .cell-handle .button-group {
558
+ position: absolute;
559
+ height: 30px;
560
+ top: -32px;
561
+ display: flex;
562
+ transform: translateX(-50%);
563
+ left: 50%;
564
+ background-color: #fff;
565
+ border: 1px solid #ccc;
566
+ border-radius: 4px;
567
+ }
568
+ .milkdown-table-block .cell-handle .button-group[data-show="false"] {
569
+ display: none;
570
+ }
571
+ .milkdown-table-block .cell-handle .button-group button {
572
+ padding-left: 8px;
573
+ padding-right: 8px;
574
+ width: max-content;
575
+ border: none;
576
+ background: none;
577
+ cursor: pointer;
578
+ }
579
+ .milkdown-table-block .cell-handle .button-group button:hover {
580
+ background-color: #f0f0f0;
581
+ }
582
+ .milkdown-table-block .line-handle {
583
+ background-color: #a8d4ff;
584
+ transition: opacity 0.2s ease-in-out;
585
+ }
586
+ .milkdown-table-block .line-handle[data-role="x-line-drag-handle"] {
587
+ height: 2px;
588
+ }
589
+ .milkdown-table-block .line-handle[data-role="y-line-drag-handle"] {
590
+ width: 2px;
591
+ }
592
+ .milkdown-table-block .line-handle .add-button {
593
+ color: #1a73e8;
594
+ border: 1px solid #a8d4ff;
595
+ padding: 0 4px;
596
+ border-radius: 8px;
597
+ width: max-content;
598
+ background: #fff;
599
+ cursor: pointer;
600
+ }
601
+ .milkdown-table-block .line-handle[data-role="x-line-drag-handle"] .add-button {
602
+ position: absolute;
603
+ transform: translateX(-100%);
604
+ top: -12px;
605
+ height: 24px;
606
+ }
607
+ .milkdown-table-block .line-handle[data-role="y-line-drag-handle"] .add-button {
608
+ position: absolute;
609
+ transform: translateX(-50%);
610
+ top: -24px;
611
+ height: 24px;
612
+ }
613
+ .milkdown-table-block .line-handle[data-display-type="indicator"] .add-button {
614
+ display: none;
615
+ }
616
+ .milkdown-table-block .drag-preview {
617
+ position: absolute;
618
+ z-index: 100;
619
+ border: 1px solid #ccc;
620
+ opacity: 0.5;
621
+ display: flex;
622
+ flex-direction: column;
623
+ }
624
+ .milkdown-table-block .drag-preview table {
625
+ margin: 0;
626
+ }
627
+ .milkdown-table-block .drag-preview[data-show="false"] {
628
+ display: none;
629
+ }
630
+
631
+ /* Table cell styles */
632
+ .aimd-editor-wysiwyg-mode th,
633
+ .aimd-editor-wysiwyg-mode td {
634
+ border: 1px solid #ddd;
635
+ padding: 4px 16px;
636
+ position: relative;
637
+ }
638
+ .aimd-editor-wysiwyg-mode th {
639
+ background: #f5f7fa;
640
+ font-weight: 600;
641
+ }
642
+ /* Remove browser default blue outline, add custom selection border */
643
+ .aimd-editor-wysiwyg-mode .ProseMirror-selectednode {
644
+ outline: none !important;
645
+ }
646
+ .milkdown-table-block .ProseMirror-selectednode {
647
+ outline: none !important;
648
+ background-color: transparent !important;
649
+ }
650
+ /* Table cell click selection border */
651
+ .aimd-editor-wysiwyg-mode td:has(.ProseMirror-selectednode),
652
+ .aimd-editor-wysiwyg-mode th:has(.ProseMirror-selectednode) {
653
+ outline: 1px solid #a8a8a8;
654
+ outline-offset: -1px;
655
+ }
656
+ .aimd-editor-wysiwyg-mode .selectedCell {
657
+ position: relative;
658
+ }
659
+ .aimd-editor-wysiwyg-mode .selectedCell::after {
660
+ content: '';
661
+ position: absolute;
662
+ inset: 0;
663
+ background-color: #d5d5d5;
664
+ opacity: 0.4;
665
+ pointer-events: none;
666
+ }
667
+ .aimd-editor-wysiwyg-mode .selectedCell::selection {
668
+ background: transparent;
669
+ }
670
+
671
+ /* Table button icon sizing */
672
+ .milkdown-table-block .cell-handle .button-group button svg {
673
+ width: 18px;
674
+ height: 18px;
675
+ fill: currentColor;
676
+ }
677
+ .milkdown-table-block .cell-handle svg {
678
+ width: 14px;
679
+ height: 14px;
680
+ fill: currentColor;
681
+ }
682
+ .milkdown-table-block .line-handle .add-button svg {
683
+ width: 16px;
684
+ height: 16px;
685
+ fill: currentColor;
686
+ }
687
+
688
+ /* Placeholder for empty paragraphs */
689
+ .aimd-placeholder {
690
+ position: relative;
691
+ }
692
+ .aimd-placeholder::before {
693
+ content: attr(data-placeholder);
694
+ position: absolute;
695
+ color: #aaa;
696
+ pointer-events: none;
697
+ font-style: italic;
698
+ }
699
+
700
+ /* AIMD inline field chip styles (fallback) */
701
+ .aimd-editor-wysiwyg-mode aimd-field {
702
+ display: inline-flex;
703
+ align-items: center;
704
+ gap: 3px;
705
+ padding: 1px 8px 1px 6px;
706
+ border-radius: 4px;
707
+ font-size: 13px;
708
+ font-family: 'SF Mono', 'Fira Code', Consolas, monospace;
709
+ line-height: 1.6;
710
+ vertical-align: baseline;
711
+ border: 1px solid rgba(37, 99, 235, 0.2);
712
+ background: rgba(37, 99, 235, 0.05);
713
+ color: #2563eb;
714
+ }
715
+ </style>