@airalogy/aimd-editor 1.7.1 → 1.9.0
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/LICENSE +201 -0
- package/README.md +6 -1
- package/README.zh-CN.md +6 -1
- package/dist/AimdEditorTopBar.vue_vue_type_script_setup_true_lang-DUzstO_i.js +1145 -0
- package/dist/AimdSourceEditor.vue_vue_type_script_setup_true_lang-ttzw5IdG.js +4976 -0
- package/dist/{AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-B8o1VbUH.js → AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-BYUea59a.js} +663 -656
- package/dist/aimd-editor.css +1 -1
- package/dist/embedded.js +2 -2
- package/dist/index.js +3 -3
- package/dist/vue.js +3 -3
- package/dist/wysiwyg.js +1 -1
- package/package.json +12 -12
- package/src/vue/AimdEditor.vue +7 -0
- package/src/vue/AimdFieldDialog.vue +58 -15
- package/src/vue/AimdSourceEditor.vue +39 -0
- package/src/vue/aimdDiagnostics.ts +39 -0
- package/src/vue/aimdInlineMarkdownNormalization.ts +2 -1
- package/src/vue/locales.ts +3 -0
- package/src/vue/types.ts +21 -11
- package/src/vue/useEditorContent.ts +7 -1
- package/dist/AimdEditorTopBar.vue_vue_type_script_setup_true_lang-gbfMDZSh.js +0 -1131
- package/dist/AimdSourceEditor.vue_vue_type_script_setup_true_lang-t_sUoXky.js +0 -274
package/dist/aimd-editor.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.ProseMirror .tableWrapper{overflow-x:auto}.ProseMirror table{border-collapse:collapse;table-layout:fixed;width:100%;overflow:hidden}.ProseMirror td,.ProseMirror th{vertical-align:top;box-sizing:border-box;position:relative}.ProseMirror td:not([data-colwidth]):not(.column-resize-dragging),.ProseMirror th:not([data-colwidth]):not(.column-resize-dragging){min-width:var(--default-cell-min-width)}.ProseMirror .column-resize-handle{position:absolute;right:-2px;top:0;bottom:0;width:4px;z-index:20;background-color:#adf;pointer-events:none}.ProseMirror.resize-cursor{cursor:ew-resize;cursor:col-resize}.ProseMirror .selectedCell:after{z-index:2;position:absolute;content:"";inset:0;background:#c8c8ff66;pointer-events:none}.aimd-dialog-overlay{position:fixed;inset:0;background:#00000059;display:flex;align-items:center;justify-content:center;z-index:10000;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.aimd-dialog{background:#fff;border-radius:12px;box-shadow:0 20px 60px #0003;width:520px;max-width:90vw;max-height:85vh;display:flex;flex-direction:column;overflow:hidden}.aimd-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #f0f0f0}.aimd-dialog-title{font-size:16px;font-weight:600;color:#1a1a2e;display:flex;align-items:center;gap:8px}.aimd-dialog-icon{display:flex;align-items:center}.aimd-dialog-icon svg{width:20px;height:20px}.aimd-dialog-close{width:32px;height:32px;border:none;background:transparent;cursor:pointer;font-size:22px;color:#999;border-radius:6px;display:flex;align-items:center;justify-content:center}.aimd-dialog-close:hover{background:#f0f2f5;color:#333}.aimd-dialog-type-tabs{display:flex;flex-wrap:wrap;align-items:center;border-bottom:1px solid #f0f0f0;padding:10px 12px;gap:8px}.aimd-type-tab{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #e5e7eb;border-radius:999px;background:#fff;cursor:pointer;font-size:12px;color:#6b7280;white-space:nowrap;transition:all .15s}.aimd-type-tab:hover{color:#374151;border-color:#d1d5db;background:#f8fafc}.aimd-type-tab-icon{display:inline-flex;align-items:center;vertical-align:middle}.aimd-type-tab-icon svg{width:14px;height:14px}.aimd-type-tab.active{font-weight:600}.aimd-dialog-body{padding:20px;display:flex;flex-direction:column;gap:14px;overflow-y:auto}.aimd-field-row{display:flex;flex-direction:column;gap:4px}.aimd-field-label{font-size:13px;font-weight:500;color:#444}.aimd-field-label em{color:#dc2626;font-style:normal}.aimd-field-input{padding:8px 12px;border:1px solid #e0e0e0;border-radius:6px;font-size:14px;outline:none;transition:border-color .2s;font-family:inherit}.aimd-field-input:focus{border-color:#1a73e8;box-shadow:0 0 0 2px #1a73e81a}.aimd-field-textarea{min-height:72px;resize:vertical}.aimd-field-hint{font-size:11px;color:#999}.aimd-var-type-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px}.aimd-var-type-card{display:flex;flex-direction:column;align-items:flex-start;gap:4px;padding:10px 12px;border:1px solid #e5e7eb;border-radius:10px;background:#fff;cursor:pointer;text-align:left;transition:border-color .15s,box-shadow .15s,background .15s,transform .15s}.aimd-var-type-card:hover{border-color:#93c5fd;background:#f8fbff;transform:translateY(-1px)}.aimd-var-type-card:focus-visible{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px #2563eb26}.aimd-var-type-card.active{border-color:#2563eb;background:#eff6ff;box-shadow:0 0 0 2px #2563eb1f}.aimd-var-type-card-title{font-size:13px;font-weight:600;color:#1f2937}.aimd-var-type-card-desc{font-size:12px;line-height:1.4;color:#6b7280}.aimd-collection-editor{display:flex;flex-direction:column;gap:8px}.aimd-collection-row{display:grid;grid-template-columns:90px 1fr auto;gap:8px;align-items:center}.aimd-option-row{grid-template-columns:26px 96px 90px 1fr auto}.aimd-option-row-dragover{background:#eff6ff;border-radius:6px}.aimd-option-answer-toggle{display:flex;align-items:center;gap:6px;color:#4b5563;font-size:12px;-webkit-user-select:none;user-select:none}.aimd-option-answer-toggle input{margin:0}.aimd-drag-handle{color:#9ca3af;cursor:grab;-webkit-user-select:none;user-select:none;text-align:center;font-size:14px;line-height:1}.aimd-drag-handle:active{cursor:grabbing}.aimd-mini-btn{border:1px solid #d8dee8;background:#fff;color:#4b5563;border-radius:6px;padding:6px 10px;font-size:12px;cursor:pointer}.aimd-mini-btn:hover{background:#f8fafc}.aimd-mini-btn:disabled{opacity:.5;cursor:not-allowed}.aimd-mini-btn-add{align-self:flex-start}.aimd-dialog-preview{padding:12px 14px;background:#f8f9fa;border-radius:6px;border:1px solid #e8e8e8;display:flex;flex-direction:column;align-items:stretch;gap:10px}.aimd-preview-header{display:block;font-size:12px;color:#6b7280;font-weight:600;letter-spacing:.04em}.aimd-preview-panel{display:block;width:100%;margin:0;padding:10px 12px;border-radius:6px;border:1px solid #dbe3ef;background:#fff;overflow:auto;max-height:280px;line-height:1.5}.aimd-preview-code{display:block;font-family:SF Mono,Fira Code,monospace;font-size:13px;color:#2563eb;word-break:break-word;white-space:pre-wrap}.aimd-dialog-error{padding:10px 12px;border:1px solid #fecaca;background:#fef2f2;color:#b91c1c;border-radius:6px;font-size:12px;line-height:1.5}.aimd-dialog-footer{display:flex;justify-content:flex-end;gap:8px;padding:14px 20px;border-top:1px solid #f0f0f0}.aimd-btn{padding:8px 20px;border-radius:6px;font-size:13px;font-weight:500;cursor:pointer;transition:all .15s}.aimd-btn-cancel{border:1px solid #e0e0e0;background:#fff;color:#666}.aimd-btn-cancel:hover{background:#f5f5f5}.aimd-btn-primary{border:none;background:#1a73e8;color:#fff}.aimd-btn-primary:hover{background:#1557b0}.aimd-editor{display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}.aimd-editor-toolbar{display:flex;align-items:center;gap:2px;padding:4px 8px;background:#fafbfc;border-bottom:1px solid #e0e0e0;overflow-x:auto;flex-wrap:wrap}.aimd-editor-mode-switch{display:flex;background:#eceef1;border-radius:5px;padding:2px;gap:1px;flex-shrink:0}.aimd-editor-mode-btn{display:flex;align-items:center;gap:4px;padding:4px 10px;border:none;border-radius:4px;background:transparent;cursor:pointer;font-size:12px;font-weight:500;color:#777;transition:all .15s;white-space:nowrap}.aimd-editor-mode-btn:hover{color:#444;background:#ffffff80}.aimd-editor-mode-btn.active{background:#fff;color:#1a73e8;box-shadow:0 1px 2px #00000014}.aimd-editor-mode-icon{display:flex;align-items:center}.aimd-editor-toolbar-sep{width:1px;height:20px;background:#ddd;margin:0 4px;flex-shrink:0}.aimd-editor-toolbar-divider{width:1px;height:22px;background:#ccc;margin:0 6px;flex-shrink:0}.aimd-editor-fmt-btn{width:28px;height:28px;border:1px solid transparent;border-radius:4px;background:transparent;cursor:pointer;color:#555;display:flex;align-items:center;justify-content:center;transition:all .12s;padding:0;flex-shrink:0}.aimd-editor-fmt-btn:hover{background:#eceef1;color:#222}.aimd-editor-fmt-btn:active{background:#dfe1e5}.aimd-editor-fmt-btn svg{width:16px;height:16px;display:block}.aimd-editor-aimd-btn{width:auto;gap:3px;padding:0 7px;border:1px solid color-mix(in srgb,var(--aimd-color, #2563eb) 18%,transparent);background:color-mix(in srgb,var(--aimd-color, #2563eb) 4%,transparent);border-radius:4px}.aimd-editor-aimd-btn:hover{background:color-mix(in srgb,var(--aimd-color, #2563eb) 12%,transparent);border-color:color-mix(in srgb,var(--aimd-color, #2563eb) 35%,transparent);color:var(--aimd-color, #2563eb)}.aimd-editor-aimd-btn-icon{display:flex;align-items:center}.aimd-editor-aimd-btn-icon svg{width:13px;height:13px}.aimd-editor-aimd-btn-label{font-size:11px;font-weight:500}.aimd-editor-panel{background:#fff;overflow:hidden}.aimd-editor-source-mode{overflow:hidden}.aimd-editor-container{height:100%}.aimd-editor-loading{padding:40px;text-align:center;color:#888}.aimd-editor-wysiwyg-mode .milkdown{height:100%}.aimd-editor-wysiwyg-mode .milkdown-editor-content{padding:0 20px 6px;min-height:100%;outline:none;font-size:15px;line-height:1.8}.aimd-editor-wysiwyg-mode .milkdown-editor-content .ProseMirror{outline:none}.aimd-editor-wysiwyg-mode h1{font-size:1.8em;margin:.6em 0 .3em;font-weight:700}.aimd-editor-wysiwyg-mode h2{font-size:1.4em;margin:.5em 0 .3em;color:#333;font-weight:600;border-bottom:1px solid #eee;padding-bottom:.2em}.aimd-editor-wysiwyg-mode h3{font-size:1.2em;margin:.4em 0 .2em;font-weight:600}.aimd-editor-wysiwyg-mode p{margin:.5em 0}.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}.aimd-editor-wysiwyg-mode ul,.aimd-editor-wysiwyg-mode ol{padding-left:24px;margin:4px 0}.aimd-editor-wysiwyg-mode code{background:#f0f2f5;padding:2px 6px;border-radius:3px;font-size:.9em;font-family:SF Mono,Fira Code,monospace}.aimd-editor-wysiwyg-mode pre{background:#1e1e2e;color:#cdd6f4;padding:16px;border-radius:8px;overflow-x:auto}.aimd-editor-wysiwyg-mode pre code{background:none;padding:0;color:inherit}.aimd-editor-wysiwyg-mode hr{border:none;border-top:2px solid #e8e8e8;margin:16px 0}.aimd-editor-wysiwyg-mode img{max-width:100%;border-radius:4px}.aimd-editor-wysiwyg-mode a{color:#1a73e8;text-decoration:none}.aimd-editor-wysiwyg-mode a:hover{text-decoration:underline}.aimd-editor-wysiwyg-mode li{margin:2px 0}.aimd-editor-wysiwyg-mode .tableWrapper{overflow-x:auto;margin:12px 0}.aimd-block-handle{position:absolute;z-index:10;transition:top .15s ease,left .15s ease}.aimd-block-handle[data-show=false]{opacity:0;pointer-events:none}.aimd-block-handle-btn{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:4px;background:#fff;border:1px solid #e0e0e0;color:#aaa;cursor:pointer;transition:all .15s;box-shadow:0 1px 2px #0000000d}.aimd-block-handle-btn:hover{background:#f0f2f5;border-color:silver;color:#555}.aimd-block-add-menu{position:fixed;z-index:10000;background:#fff;border:1px solid #e0e0e0;border-radius:8px;box-shadow:0 4px 16px #0000001f;padding:4px;min-width:200px;max-height:400px;overflow-y:auto}.aimd-block-add-menu-group-label{padding:6px 12px 2px;font-size:11px;font-weight:600;color:#999;text-transform:uppercase;letter-spacing:.5px}.aimd-block-add-menu-divider{height:1px;background:#eee;margin:4px 8px}.aimd-block-add-menu-item{display:flex;align-items:center;gap:10px;width:100%;padding:8px 12px;border:none;background:none;border-radius:5px;cursor:pointer;font-size:13px;color:#333;text-align:left;transition:background .1s}.aimd-block-add-menu-item:hover{background:#f0f2f5}.aimd-block-add-menu-icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:4px;background:#f0f2f5;color:#666;flex-shrink:0}.aimd-block-add-menu-icon svg{width:14px;height:14px}.milkdown-table-block{display:block;margin:4px 0}.milkdown-table-block table{margin:0!important;border-radius:0!important}.milkdown-table-block .handle{position:absolute;z-index:50;cursor:grab;font-size:14px}.milkdown-table-block .cell-handle{left:-999px;top:-999px;z-index:100;height:24px;transition:opacity .2s ease .2s;background-color:#a8d4ff;padding:0 8px;border-radius:8px;position:absolute}.milkdown-table-block .cell-handle[data-role=col-drag-handle]{transform:translateY(50%)}.milkdown-table-block .cell-handle[data-role=row-drag-handle]{transform:translate(50%)}.milkdown-table-block .cell-handle[data-show=false],.milkdown-table-block .line-handle[data-show=false]{opacity:0}.milkdown-table-block .handle:hover{opacity:1}.milkdown-table-block .cell-handle .button-group{position:absolute;height:30px;top:-32px;display:flex;transform:translate(-50%);left:50%;background-color:#fff;border:1px solid #ccc;border-radius:4px}.milkdown-table-block .cell-handle .button-group[data-show=false]{display:none}.milkdown-table-block .cell-handle .button-group button{padding-left:8px;padding-right:8px;width:max-content;border:none;background:none;cursor:pointer}.milkdown-table-block .cell-handle .button-group button:hover{background-color:#f0f0f0}.milkdown-table-block .line-handle{background-color:#a8d4ff;transition:opacity .2s ease-in-out}.milkdown-table-block .line-handle[data-role=x-line-drag-handle]{height:2px}.milkdown-table-block .line-handle[data-role=y-line-drag-handle]{width:2px}.milkdown-table-block .line-handle .add-button{color:#1a73e8;border:1px solid #a8d4ff;padding:0 4px;border-radius:8px;width:max-content;background:#fff;cursor:pointer}.milkdown-table-block .line-handle[data-role=x-line-drag-handle] .add-button{position:absolute;transform:translate(-100%);top:-12px;height:24px}.milkdown-table-block .line-handle[data-role=y-line-drag-handle] .add-button{position:absolute;transform:translate(-50%);top:-24px;height:24px}.milkdown-table-block .line-handle[data-display-type=indicator] .add-button{display:none}.milkdown-table-block .drag-preview{position:absolute;z-index:100;border:1px solid #ccc;opacity:.5;display:flex;flex-direction:column}.milkdown-table-block .drag-preview table{margin:0}.milkdown-table-block .drag-preview[data-show=false]{display:none}.aimd-editor-wysiwyg-mode th,.aimd-editor-wysiwyg-mode td{border:1px solid #ddd;padding:4px 16px;position:relative}.aimd-editor-wysiwyg-mode th{background:#f5f7fa;font-weight:600}.aimd-editor-wysiwyg-mode .ProseMirror-selectednode{outline:none!important}.milkdown-table-block .ProseMirror-selectednode{outline:none!important;background-color:transparent!important}.aimd-editor-wysiwyg-mode td:has(.ProseMirror-selectednode),.aimd-editor-wysiwyg-mode th:has(.ProseMirror-selectednode){outline:1px solid #a8a8a8;outline-offset:-1px}.aimd-editor-wysiwyg-mode .selectedCell{position:relative}.aimd-editor-wysiwyg-mode .selectedCell:after{content:"";position:absolute;inset:0;background-color:#d5d5d5;opacity:.4;pointer-events:none}.aimd-editor-wysiwyg-mode .selectedCell::selection{background:transparent}.milkdown-table-block .cell-handle .button-group button svg{width:18px;height:18px;fill:currentColor}.milkdown-table-block .cell-handle svg{width:14px;height:14px;fill:currentColor}.milkdown-table-block .line-handle .add-button svg{width:16px;height:16px;fill:currentColor}.aimd-placeholder{position:relative}.aimd-placeholder:before{content:attr(data-placeholder);position:absolute;color:#aaa;pointer-events:none;font-style:italic}.aimd-editor-wysiwyg-mode aimd-field{display:inline-flex;align-items:center;gap:3px;padding:1px 8px 1px 6px;border-radius:4px;font-size:13px;font-family:SF Mono,Fira Code,Consolas,monospace;line-height:1.6;vertical-align:baseline;border:1px solid rgba(37,99,235,.2);background:#2563eb0d;color:#2563eb}
|
|
1
|
+
.ProseMirror .tableWrapper{overflow-x:auto}.ProseMirror table{border-collapse:collapse;table-layout:fixed;width:100%;overflow:hidden}.ProseMirror td,.ProseMirror th{vertical-align:top;box-sizing:border-box;position:relative}.ProseMirror td:not([data-colwidth]):not(.column-resize-dragging),.ProseMirror th:not([data-colwidth]):not(.column-resize-dragging){min-width:var(--default-cell-min-width)}.ProseMirror .column-resize-handle{position:absolute;right:-2px;top:0;bottom:0;width:4px;z-index:20;background-color:#adf;pointer-events:none}.ProseMirror.resize-cursor{cursor:ew-resize;cursor:col-resize}.ProseMirror .selectedCell:after{z-index:2;position:absolute;content:"";inset:0;background:#c8c8ff66;pointer-events:none}.aimd-dialog-overlay{position:fixed;inset:0;background:#00000059;display:flex;align-items:center;justify-content:center;z-index:10000;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.aimd-dialog{background:#fff;border-radius:12px;box-shadow:0 20px 60px #0003;width:520px;max-width:90vw;max-height:85vh;display:flex;flex-direction:column;overflow:hidden}.aimd-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #f0f0f0}.aimd-dialog-title{font-size:16px;font-weight:600;color:#1a1a2e;display:flex;align-items:center;gap:8px}.aimd-dialog-icon{display:flex;align-items:center}.aimd-dialog-icon svg{width:20px;height:20px}.aimd-dialog-close{width:32px;height:32px;border:none;background:transparent;cursor:pointer;font-size:22px;color:#999;border-radius:6px;display:flex;align-items:center;justify-content:center}.aimd-dialog-close:hover{background:#f0f2f5;color:#333}.aimd-dialog-type-tabs{display:flex;flex-wrap:wrap;align-items:center;border-bottom:1px solid #f0f0f0;padding:10px 12px;gap:8px}.aimd-type-tab{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #e5e7eb;border-radius:999px;background:#fff;cursor:pointer;font-size:12px;color:#6b7280;white-space:nowrap;transition:all .15s}.aimd-type-tab:hover{color:#374151;border-color:#d1d5db;background:#f8fafc}.aimd-type-tab-icon{display:inline-flex;align-items:center;vertical-align:middle}.aimd-type-tab-icon svg{width:14px;height:14px}.aimd-type-tab.active{font-weight:600}.aimd-dialog-body{padding:20px;display:flex;flex-direction:column;gap:14px;overflow-y:auto}.aimd-field-row{display:flex;flex-direction:column;gap:4px}.aimd-field-label{font-size:13px;font-weight:500;color:#444}.aimd-field-label em{color:#dc2626;font-style:normal}.aimd-field-input{padding:8px 12px;border:1px solid #e0e0e0;border-radius:6px;font-size:14px;outline:none;transition:border-color .2s;font-family:inherit}.aimd-field-input:focus{border-color:#1a73e8;box-shadow:0 0 0 2px #1a73e81a}.aimd-field-textarea{min-height:72px;resize:vertical}.aimd-field-hint{font-size:11px;color:#999}.aimd-var-type-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px}.aimd-var-type-card{display:flex;flex-direction:column;align-items:flex-start;gap:4px;padding:10px 12px;border:1px solid #e5e7eb;border-radius:10px;background:#fff;cursor:pointer;text-align:left;transition:border-color .15s,box-shadow .15s,background .15s,transform .15s}.aimd-var-type-card:hover{border-color:#93c5fd;background:#f8fbff;transform:translateY(-1px)}.aimd-var-type-card:focus-visible{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px #2563eb26}.aimd-var-type-card.active{border-color:#2563eb;background:#eff6ff;box-shadow:0 0 0 2px #2563eb1f}.aimd-var-type-card-title{font-size:13px;font-weight:600;color:#1f2937}.aimd-var-type-card-desc{font-size:12px;line-height:1.4;color:#6b7280}.aimd-collection-editor{display:flex;flex-direction:column;gap:8px}.aimd-collection-row{display:grid;grid-template-columns:90px 1fr auto;gap:8px;align-items:center}.aimd-option-row{grid-template-columns:26px 60px 1fr 80px auto}.aimd-option-row-dragover{background:#eff6ff;border-radius:6px}.aimd-option-answer-toggle{display:flex;align-items:center;gap:6px;color:#4b5563;font-size:12px;-webkit-user-select:none;user-select:none}.aimd-option-answer-toggle input{margin:0}.aimd-drag-handle{color:#9ca3af;cursor:grab;-webkit-user-select:none;user-select:none;text-align:center;font-size:14px;line-height:1}.aimd-drag-handle:active{cursor:grabbing}.aimd-mini-btn{border:1px solid #d8dee8;background:#fff;color:#4b5563;border-radius:6px;padding:6px 10px;font-size:12px;cursor:pointer}.aimd-mini-btn:hover{background:#f8fafc}.aimd-mini-btn:disabled{opacity:.5;cursor:not-allowed}.aimd-mini-btn-add{align-self:flex-start}.aimd-dialog-preview{padding:12px 14px;background:#f8f9fa;border-radius:6px;border:1px solid #e8e8e8;display:flex;flex-direction:column;align-items:stretch;gap:10px}.aimd-preview-header{display:block;font-size:12px;color:#6b7280;font-weight:600;letter-spacing:.04em}.aimd-preview-panel{display:block;width:100%;margin:0;padding:10px 12px;border-radius:6px;border:1px solid #dbe3ef;background:#fff;overflow:auto;max-height:280px;line-height:1.5}.aimd-preview-code{display:block;font-family:SF Mono,Fira Code,monospace;font-size:13px;color:#2563eb;word-break:break-word;white-space:pre-wrap}.aimd-dialog-error{padding:10px 12px;border:1px solid #fecaca;background:#fef2f2;color:#b91c1c;border-radius:6px;font-size:12px;line-height:1.5}.aimd-dialog-footer{display:flex;justify-content:flex-end;gap:8px;padding:14px 20px;border-top:1px solid #f0f0f0}.aimd-btn{padding:8px 20px;border-radius:6px;font-size:13px;font-weight:500;cursor:pointer;transition:all .15s}.aimd-btn-cancel{border:1px solid #e0e0e0;background:#fff;color:#666}.aimd-btn-cancel:hover{background:#f5f5f5}.aimd-btn-primary{border:none;background:#1a73e8;color:#fff}.aimd-btn-primary:hover{background:#1557b0}.aimd-editor{display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}.aimd-editor-toolbar{display:flex;align-items:center;gap:2px;padding:4px 8px;background:#fafbfc;border-bottom:1px solid #e0e0e0;overflow-x:auto;flex-wrap:wrap}.aimd-editor-mode-switch{display:flex;background:#eceef1;border-radius:5px;padding:2px;gap:1px;flex-shrink:0}.aimd-editor-mode-btn{display:flex;align-items:center;gap:4px;padding:4px 10px;border:none;border-radius:4px;background:transparent;cursor:pointer;font-size:12px;font-weight:500;color:#777;transition:all .15s;white-space:nowrap}.aimd-editor-mode-btn:hover{color:#444;background:#ffffff80}.aimd-editor-mode-btn.active{background:#fff;color:#1a73e8;box-shadow:0 1px 2px #00000014}.aimd-editor-mode-icon{display:flex;align-items:center}.aimd-editor-toolbar-sep{width:1px;height:20px;background:#ddd;margin:0 4px;flex-shrink:0}.aimd-editor-toolbar-divider{width:1px;height:22px;background:#ccc;margin:0 6px;flex-shrink:0}.aimd-editor-fmt-btn{width:28px;height:28px;border:1px solid transparent;border-radius:4px;background:transparent;cursor:pointer;color:#555;display:flex;align-items:center;justify-content:center;transition:all .12s;padding:0;flex-shrink:0}.aimd-editor-fmt-btn:hover{background:#eceef1;color:#222}.aimd-editor-fmt-btn:active{background:#dfe1e5}.aimd-editor-fmt-btn svg{width:16px;height:16px;display:block}.aimd-editor-aimd-btn{width:auto;gap:3px;padding:0 7px;border:1px solid color-mix(in srgb,var(--aimd-color, #2563eb) 18%,transparent);background:color-mix(in srgb,var(--aimd-color, #2563eb) 4%,transparent);border-radius:4px}.aimd-editor-aimd-btn:hover{background:color-mix(in srgb,var(--aimd-color, #2563eb) 12%,transparent);border-color:color-mix(in srgb,var(--aimd-color, #2563eb) 35%,transparent);color:var(--aimd-color, #2563eb)}.aimd-editor-aimd-btn-icon{display:flex;align-items:center}.aimd-editor-aimd-btn-icon svg{width:13px;height:13px}.aimd-editor-aimd-btn-label{font-size:11px;font-weight:500}.aimd-editor-panel{background:#fff;overflow:hidden}.aimd-editor-source-mode{overflow:hidden}.aimd-editor-container{height:100%}.aimd-editor-loading{padding:40px;text-align:center;color:#888}.aimd-editor-wysiwyg-mode .milkdown{height:100%}.aimd-editor-wysiwyg-mode .milkdown-editor-content{padding:0 20px 6px;min-height:100%;outline:none;font-size:15px;line-height:1.8}.aimd-editor-wysiwyg-mode .milkdown-editor-content .ProseMirror{outline:none}.aimd-editor-wysiwyg-mode h1{font-size:1.8em;margin:.6em 0 .3em;font-weight:700}.aimd-editor-wysiwyg-mode h2{font-size:1.4em;margin:.5em 0 .3em;color:#333;font-weight:600;border-bottom:1px solid #eee;padding-bottom:.2em}.aimd-editor-wysiwyg-mode h3{font-size:1.2em;margin:.4em 0 .2em;font-weight:600}.aimd-editor-wysiwyg-mode p{margin:.5em 0}.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}.aimd-editor-wysiwyg-mode ul,.aimd-editor-wysiwyg-mode ol{padding-left:24px;margin:4px 0}.aimd-editor-wysiwyg-mode code{background:#f0f2f5;padding:2px 6px;border-radius:3px;font-size:.9em;font-family:SF Mono,Fira Code,monospace}.aimd-editor-wysiwyg-mode pre{background:#1e1e2e;color:#cdd6f4;padding:16px;border-radius:8px;overflow-x:auto}.aimd-editor-wysiwyg-mode pre code{background:none;padding:0;color:inherit}.aimd-editor-wysiwyg-mode hr{border:none;border-top:2px solid #e8e8e8;margin:16px 0}.aimd-editor-wysiwyg-mode img{max-width:100%;border-radius:4px}.aimd-editor-wysiwyg-mode a{color:#1a73e8;text-decoration:none}.aimd-editor-wysiwyg-mode a:hover{text-decoration:underline}.aimd-editor-wysiwyg-mode li{margin:2px 0}.aimd-editor-wysiwyg-mode .tableWrapper{overflow-x:auto;margin:12px 0}.aimd-block-handle{position:absolute;z-index:10;transition:top .15s ease,left .15s ease}.aimd-block-handle[data-show=false]{opacity:0;pointer-events:none}.aimd-block-handle-btn{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:4px;background:#fff;border:1px solid #e0e0e0;color:#aaa;cursor:pointer;transition:all .15s;box-shadow:0 1px 2px #0000000d}.aimd-block-handle-btn:hover{background:#f0f2f5;border-color:silver;color:#555}.aimd-block-add-menu{position:fixed;z-index:10000;background:#fff;border:1px solid #e0e0e0;border-radius:8px;box-shadow:0 4px 16px #0000001f;padding:4px;min-width:200px;max-height:400px;overflow-y:auto}.aimd-block-add-menu-group-label{padding:6px 12px 2px;font-size:11px;font-weight:600;color:#999;text-transform:uppercase;letter-spacing:.5px}.aimd-block-add-menu-divider{height:1px;background:#eee;margin:4px 8px}.aimd-block-add-menu-item{display:flex;align-items:center;gap:10px;width:100%;padding:8px 12px;border:none;background:none;border-radius:5px;cursor:pointer;font-size:13px;color:#333;text-align:left;transition:background .1s}.aimd-block-add-menu-item:hover{background:#f0f2f5}.aimd-block-add-menu-icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:4px;background:#f0f2f5;color:#666;flex-shrink:0}.aimd-block-add-menu-icon svg{width:14px;height:14px}.milkdown-table-block{display:block;margin:4px 0}.milkdown-table-block table{margin:0!important;border-radius:0!important}.milkdown-table-block .handle{position:absolute;z-index:50;cursor:grab;font-size:14px}.milkdown-table-block .cell-handle{left:-999px;top:-999px;z-index:100;height:24px;transition:opacity .2s ease .2s;background-color:#a8d4ff;padding:0 8px;border-radius:8px;position:absolute}.milkdown-table-block .cell-handle[data-role=col-drag-handle]{transform:translateY(50%)}.milkdown-table-block .cell-handle[data-role=row-drag-handle]{transform:translate(50%)}.milkdown-table-block .cell-handle[data-show=false],.milkdown-table-block .line-handle[data-show=false]{opacity:0}.milkdown-table-block .handle:hover{opacity:1}.milkdown-table-block .cell-handle .button-group{position:absolute;height:30px;top:-32px;display:flex;transform:translate(-50%);left:50%;background-color:#fff;border:1px solid #ccc;border-radius:4px}.milkdown-table-block .cell-handle .button-group[data-show=false]{display:none}.milkdown-table-block .cell-handle .button-group button{padding-left:8px;padding-right:8px;width:max-content;border:none;background:none;cursor:pointer}.milkdown-table-block .cell-handle .button-group button:hover{background-color:#f0f0f0}.milkdown-table-block .line-handle{background-color:#a8d4ff;transition:opacity .2s ease-in-out}.milkdown-table-block .line-handle[data-role=x-line-drag-handle]{height:2px}.milkdown-table-block .line-handle[data-role=y-line-drag-handle]{width:2px}.milkdown-table-block .line-handle .add-button{color:#1a73e8;border:1px solid #a8d4ff;padding:0 4px;border-radius:8px;width:max-content;background:#fff;cursor:pointer}.milkdown-table-block .line-handle[data-role=x-line-drag-handle] .add-button{position:absolute;transform:translate(-100%);top:-12px;height:24px}.milkdown-table-block .line-handle[data-role=y-line-drag-handle] .add-button{position:absolute;transform:translate(-50%);top:-24px;height:24px}.milkdown-table-block .line-handle[data-display-type=indicator] .add-button{display:none}.milkdown-table-block .drag-preview{position:absolute;z-index:100;border:1px solid #ccc;opacity:.5;display:flex;flex-direction:column}.milkdown-table-block .drag-preview table{margin:0}.milkdown-table-block .drag-preview[data-show=false]{display:none}.aimd-editor-wysiwyg-mode th,.aimd-editor-wysiwyg-mode td{border:1px solid #ddd;padding:4px 16px;position:relative}.aimd-editor-wysiwyg-mode th{background:#f5f7fa;font-weight:600}.aimd-editor-wysiwyg-mode .ProseMirror-selectednode{outline:none!important}.milkdown-table-block .ProseMirror-selectednode{outline:none!important;background-color:transparent!important}.aimd-editor-wysiwyg-mode td:has(.ProseMirror-selectednode),.aimd-editor-wysiwyg-mode th:has(.ProseMirror-selectednode){outline:1px solid #a8a8a8;outline-offset:-1px}.aimd-editor-wysiwyg-mode .selectedCell{position:relative}.aimd-editor-wysiwyg-mode .selectedCell:after{content:"";position:absolute;inset:0;background-color:#d5d5d5;opacity:.4;pointer-events:none}.aimd-editor-wysiwyg-mode .selectedCell::selection{background:transparent}.milkdown-table-block .cell-handle .button-group button svg{width:18px;height:18px;fill:currentColor}.milkdown-table-block .cell-handle svg{width:14px;height:14px;fill:currentColor}.milkdown-table-block .line-handle .add-button svg{width:16px;height:16px;fill:currentColor}.aimd-placeholder{position:relative}.aimd-placeholder:before{content:attr(data-placeholder);position:absolute;color:#aaa;pointer-events:none;font-style:italic}.aimd-editor-wysiwyg-mode aimd-field{display:inline-flex;align-items:center;gap:3px;padding:1px 8px 1px 6px;border-radius:4px;font-size:13px;font-family:SF Mono,Fira Code,Consolas,monospace;line-height:1.6;vertical-align:baseline;border:1px solid rgba(37,99,235,.2);background:#2563eb0d;color:#2563eb}
|
package/dist/embedded.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as s } from "./AimdSourceEditor.vue_vue_type_script_setup_true_lang-
|
|
2
|
-
import { _ as i, D as d, i as o, j as t, k as A, r as m } from "./AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-
|
|
1
|
+
import { _ as s } from "./AimdSourceEditor.vue_vue_type_script_setup_true_lang-ttzw5IdG.js";
|
|
2
|
+
import { _ as i, D as d, i as o, j as t, k as A, r as m } from "./AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-BYUea59a.js";
|
|
3
3
|
export {
|
|
4
4
|
s as AimdSourceEditor,
|
|
5
5
|
i as AimdWysiwygEditor,
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { A as i, a as s, b as d, D as o, K as m, M as r, c as t, d as A, e as n, f as l, g as D, l as E, s as T } from "./theme-B8dCnOx-.js";
|
|
2
|
-
import { _, a as c, b as f, c as u, u as M } from "./AimdEditorTopBar.vue_vue_type_script_setup_true_lang-
|
|
3
|
-
import { _ as p } from "./AimdSourceEditor.vue_vue_type_script_setup_true_lang-
|
|
4
|
-
import { A as k, a as x, _ as L, D as O, M as S, b as y, c as b, d as N, e as P, f as R, g as w, h, i as B, j as C, k as v, l as K, m as V, n as Y, r as j } from "./AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-
|
|
2
|
+
import { _, a as c, b as f, c as u, u as M } from "./AimdEditorTopBar.vue_vue_type_script_setup_true_lang-DUzstO_i.js";
|
|
3
|
+
import { _ as p } from "./AimdSourceEditor.vue_vue_type_script_setup_true_lang-ttzw5IdG.js";
|
|
4
|
+
import { A as k, a as x, _ as L, D as O, M as S, b as y, c as b, d as N, e as P, f as R, g as w, h, i as B, j as C, k as v, l as K, m as V, n as Y, r as j } from "./AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-BYUea59a.js";
|
|
5
5
|
export {
|
|
6
6
|
k as AIMD_FIELD_TYPES,
|
|
7
7
|
x as AIMD_FIELD_TYPE_DEFINITIONS,
|
package/dist/vue.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { _ as e, a as d, b as s, c as r, u as o } from "./AimdEditorTopBar.vue_vue_type_script_setup_true_lang-
|
|
2
|
-
import { _ as m } from "./AimdSourceEditor.vue_vue_type_script_setup_true_lang-
|
|
3
|
-
import { A as E, a as l, _ as I, D as _, M as T, b as D, c as F, d as M, e as u, f as c, g as n, h as g, i as L, j as O, k as p, l as S, m as y, n as b, r as f } from "./AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-
|
|
1
|
+
import { _ as e, a as d, b as s, c as r, u as o } from "./AimdEditorTopBar.vue_vue_type_script_setup_true_lang-DUzstO_i.js";
|
|
2
|
+
import { _ as m } from "./AimdSourceEditor.vue_vue_type_script_setup_true_lang-ttzw5IdG.js";
|
|
3
|
+
import { A as E, a as l, _ as I, D as _, M as T, b as D, c as F, d as M, e as u, f as c, g as n, h as g, i as L, j as O, k as p, l as S, m as y, n as b, r as f } from "./AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-BYUea59a.js";
|
|
4
4
|
export {
|
|
5
5
|
E as AIMD_FIELD_TYPES,
|
|
6
6
|
l as AIMD_FIELD_TYPE_DEFINITIONS,
|
package/dist/wysiwyg.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as a, D as r, i, j as d, k as t, r as A } from "./AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-
|
|
1
|
+
import { _ as a, D as r, i, j as d, k as t, r as A } from "./AimdWysiwygEditor.vue_vue_type_script_setup_true_lang-BYUea59a.js";
|
|
2
2
|
export {
|
|
3
3
|
a as AimdWysiwygEditor,
|
|
4
4
|
r as DEFAULT_AIMD_EDITOR_LOCALE,
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@airalogy/aimd-editor",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "AIMD editor with Monaco source mode and Milkdown WYSIWYG mode",
|
|
4
|
+
"version": "1.9.0",
|
|
5
|
+
"description": "AIMD (Airalogy Markdown) editor with Monaco source mode and Milkdown WYSIWYG mode",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -53,20 +53,14 @@
|
|
|
53
53
|
"dist",
|
|
54
54
|
"src"
|
|
55
55
|
],
|
|
56
|
-
"scripts": {
|
|
57
|
-
"type-check": "tsc --noEmit",
|
|
58
|
-
"build": "vite build",
|
|
59
|
-
"dev": "vite build --watch",
|
|
60
|
-
"test": "node --test ./tests/*.test.mjs"
|
|
61
|
-
},
|
|
62
56
|
"dependencies": {
|
|
63
|
-
"@airalogy/aimd-core": "workspace:^",
|
|
64
|
-
"@airalogy/aimd-renderer": "workspace:^",
|
|
65
57
|
"@codingame/monaco-vscode-editor-api": "^20.2.1",
|
|
66
58
|
"@codingame/monaco-vscode-standalone-languages": "^20.2.1",
|
|
67
59
|
"@milkdown/kit": "^7.18.0",
|
|
68
60
|
"@milkdown/theme-nord": "^7.18.0",
|
|
69
|
-
"@milkdown/vue": "^7.18.0"
|
|
61
|
+
"@milkdown/vue": "^7.18.0",
|
|
62
|
+
"@airalogy/aimd-core": "^2.6.0",
|
|
63
|
+
"@airalogy/aimd-renderer": "^2.5.0"
|
|
70
64
|
},
|
|
71
65
|
"peerDependencies": {
|
|
72
66
|
"monaco-editor": ">=0.50.0",
|
|
@@ -86,5 +80,11 @@
|
|
|
86
80
|
"vite-tsconfig-paths": "^5.1.4",
|
|
87
81
|
"vue": "^3.5.17",
|
|
88
82
|
"@vitejs/plugin-vue": "^6.0.1"
|
|
83
|
+
},
|
|
84
|
+
"scripts": {
|
|
85
|
+
"type-check": "tsc --noEmit",
|
|
86
|
+
"build": "vite build",
|
|
87
|
+
"dev": "vite build --watch",
|
|
88
|
+
"test": "node --test ./tests/*.test.mjs"
|
|
89
89
|
}
|
|
90
|
-
}
|
|
90
|
+
}
|
package/src/vue/AimdEditor.vue
CHANGED
|
@@ -107,6 +107,12 @@ const refSuggestions = computed(() => {
|
|
|
107
107
|
return []
|
|
108
108
|
})
|
|
109
109
|
|
|
110
|
+
const existingQuizIds = computed(() => {
|
|
111
|
+
const fields = extractedFields.value
|
|
112
|
+
if (!fields?.quiz) return []
|
|
113
|
+
return fields.quiz.map(q => q.id)
|
|
114
|
+
})
|
|
115
|
+
|
|
110
116
|
function openAimdDialog(type: string) {
|
|
111
117
|
aimdDialogType.value = type
|
|
112
118
|
showAimdDialog.value = true
|
|
@@ -221,6 +227,7 @@ defineExpose({
|
|
|
221
227
|
:messages="resolvedMessages"
|
|
222
228
|
:ref-suggestions="refSuggestions"
|
|
223
229
|
:var-type-plugins="varTypePlugins"
|
|
230
|
+
:existing-quiz-ids="existingQuizIds"
|
|
224
231
|
@update:visible="showAimdDialog = $event"
|
|
225
232
|
@insert="onDialogInsert"
|
|
226
233
|
/>
|
|
@@ -17,6 +17,7 @@ const props = defineProps<{
|
|
|
17
17
|
refSuggestions?: string[]
|
|
18
18
|
varTypePlugins?: AimdVarTypePresetOption[]
|
|
19
19
|
allowedTypes?: string[]
|
|
20
|
+
existingQuizIds?: string[]
|
|
20
21
|
}>()
|
|
21
22
|
|
|
22
23
|
const emit = defineEmits<{
|
|
@@ -42,8 +43,12 @@ function resolveDialogType(type?: string): string {
|
|
|
42
43
|
return localizedFieldTypes.value[0]?.type ?? type ?? 'var'
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
const existingQuizIdsSet = computed(() => {
|
|
47
|
+
return new Set(props.existingQuizIds || [])
|
|
48
|
+
})
|
|
49
|
+
|
|
45
50
|
const dialogType = ref(resolveDialogType(props.initialType))
|
|
46
|
-
const fields = ref<Record<string, string>>(getDefaultAimdFields(dialogType.value, props.messages))
|
|
51
|
+
const fields = ref<Record<string, string>>(getDefaultAimdFields(dialogType.value, props.messages, existingQuizIdsSet.value))
|
|
47
52
|
|
|
48
53
|
interface ChoiceOptionItem {
|
|
49
54
|
key: string
|
|
@@ -77,12 +82,21 @@ function selectVarTypePreset(value: string) {
|
|
|
77
82
|
fields.value.type = value
|
|
78
83
|
}
|
|
79
84
|
|
|
85
|
+
function generateUniqueQuizId(quizType: string, existingIds: Set<string>): string {
|
|
86
|
+
const base = `quiz_${quizType}`
|
|
87
|
+
let suffix = 1
|
|
88
|
+
while (existingIds.has(`${base}_${suffix}`)) {
|
|
89
|
+
suffix += 1
|
|
90
|
+
}
|
|
91
|
+
return `${base}_${suffix}`
|
|
92
|
+
}
|
|
93
|
+
|
|
80
94
|
function parseChoiceOptions(input: string): ChoiceOptionItem[] {
|
|
81
95
|
const parts = input.split(',').map(s => s.trim()).filter(Boolean)
|
|
82
96
|
if (parts.length === 0) {
|
|
83
97
|
return [
|
|
84
|
-
{ key: 'A', text:
|
|
85
|
-
{ key: 'B', text:
|
|
98
|
+
{ key: 'A', text: '' },
|
|
99
|
+
{ key: 'B', text: '' },
|
|
86
100
|
]
|
|
87
101
|
}
|
|
88
102
|
|
|
@@ -104,7 +118,7 @@ function serializeChoiceOptions(items: ChoiceOptionItem[]): string {
|
|
|
104
118
|
.filter(item => item.key && item.text)
|
|
105
119
|
|
|
106
120
|
if (normalized.length === 0) {
|
|
107
|
-
return
|
|
121
|
+
return 'A:, B:'
|
|
108
122
|
}
|
|
109
123
|
|
|
110
124
|
return normalized.map(item => `${item.key}:${item.text}`).join(', ')
|
|
@@ -113,7 +127,7 @@ function serializeChoiceOptions(items: ChoiceOptionItem[]): string {
|
|
|
113
127
|
function parseBlankItems(input: string): BlankItem[] {
|
|
114
128
|
const parts = input.split(',').map(s => s.trim()).filter(Boolean)
|
|
115
129
|
if (parts.length === 0) {
|
|
116
|
-
return [{ key: 'b1', answer: '
|
|
130
|
+
return [{ key: 'b1', answer: '' }]
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
return parts.map((part, index) => {
|
|
@@ -133,7 +147,7 @@ function serializeBlankItems(items: BlankItem[]): string {
|
|
|
133
147
|
.filter(item => item.key)
|
|
134
148
|
|
|
135
149
|
if (normalized.length === 0) {
|
|
136
|
-
return 'b1:
|
|
150
|
+
return 'b1:'
|
|
137
151
|
}
|
|
138
152
|
|
|
139
153
|
return normalized.map(item => `${item.key}:${item.answer}`).join(', ')
|
|
@@ -199,7 +213,7 @@ function nextChoiceKey(): string {
|
|
|
199
213
|
|
|
200
214
|
function addChoiceOption() {
|
|
201
215
|
const key = nextChoiceKey()
|
|
202
|
-
quizChoiceOptions.value.push({ key, text:
|
|
216
|
+
quizChoiceOptions.value.push({ key, text: '' })
|
|
203
217
|
}
|
|
204
218
|
|
|
205
219
|
function removeChoiceOption(index: number) {
|
|
@@ -327,7 +341,7 @@ function validateBeforeInsert(): string | null {
|
|
|
327
341
|
watch(() => props.initialType, (t) => {
|
|
328
342
|
const resolvedType = resolveDialogType(t)
|
|
329
343
|
dialogType.value = resolvedType
|
|
330
|
-
fields.value = getDefaultAimdFields(resolvedType, props.messages)
|
|
344
|
+
fields.value = getDefaultAimdFields(resolvedType, props.messages, existingQuizIdsSet.value)
|
|
331
345
|
hydrateQuizDraftsFromFields()
|
|
332
346
|
formError.value = ''
|
|
333
347
|
})
|
|
@@ -339,14 +353,14 @@ watch(localizedFieldTypes, () => {
|
|
|
339
353
|
}
|
|
340
354
|
|
|
341
355
|
dialogType.value = resolvedType
|
|
342
|
-
fields.value = getDefaultAimdFields(resolvedType, props.messages)
|
|
356
|
+
fields.value = getDefaultAimdFields(resolvedType, props.messages, existingQuizIdsSet.value)
|
|
343
357
|
hydrateQuizDraftsFromFields()
|
|
344
358
|
formError.value = ''
|
|
345
359
|
})
|
|
346
360
|
|
|
347
361
|
watch(() => props.visible, (v) => {
|
|
348
362
|
if (v) {
|
|
349
|
-
fields.value = getDefaultAimdFields(dialogType.value, props.messages)
|
|
363
|
+
fields.value = getDefaultAimdFields(dialogType.value, props.messages, existingQuizIdsSet.value)
|
|
350
364
|
hydrateQuizDraftsFromFields()
|
|
351
365
|
formError.value = ''
|
|
352
366
|
}
|
|
@@ -355,7 +369,7 @@ watch(() => props.visible, (v) => {
|
|
|
355
369
|
function switchType(type: string) {
|
|
356
370
|
const resolvedType = resolveDialogType(type)
|
|
357
371
|
dialogType.value = resolvedType
|
|
358
|
-
fields.value = getDefaultAimdFields(resolvedType, props.messages)
|
|
372
|
+
fields.value = getDefaultAimdFields(resolvedType, props.messages, existingQuizIdsSet.value)
|
|
359
373
|
hydrateQuizDraftsFromFields()
|
|
360
374
|
formError.value = ''
|
|
361
375
|
}
|
|
@@ -388,6 +402,35 @@ const referencedPlaceholder = computed(() => {
|
|
|
388
402
|
watch(() => [dialogType.value, fields.value.quizType], ([type, quizType], [prevType, prevQuizType]) => {
|
|
389
403
|
if (type !== 'quiz') return
|
|
390
404
|
if (type !== prevType || quizType !== prevQuizType) {
|
|
405
|
+
if (quizType !== prevQuizType && prevQuizType) {
|
|
406
|
+
fields.value.id = generateUniqueQuizId(quizType, existingQuizIdsSet.value)
|
|
407
|
+
|
|
408
|
+
if (quizType === 'choice') {
|
|
409
|
+
fields.value.stem = props.messages.defaults.questionStem
|
|
410
|
+
fields.value.mode = 'single'
|
|
411
|
+
fields.value.options = 'A:, B:'
|
|
412
|
+
fields.value.answer = 'A'
|
|
413
|
+
fields.value.blanks = ''
|
|
414
|
+
fields.value.rubric = ''
|
|
415
|
+
}
|
|
416
|
+
else if (quizType === 'blank') {
|
|
417
|
+
fields.value.stem = props.messages.defaults.fillQuestionStem
|
|
418
|
+
fields.value.blanks = 'b1:'
|
|
419
|
+
fields.value.options = ''
|
|
420
|
+
fields.value.answer = ''
|
|
421
|
+
fields.value.mode = ''
|
|
422
|
+
fields.value.rubric = ''
|
|
423
|
+
quizBlankItems.value = [{ key: 'b1', answer: '' }]
|
|
424
|
+
}
|
|
425
|
+
else if (quizType === 'open') {
|
|
426
|
+
fields.value.stem = props.messages.defaults.fillQuestionStem
|
|
427
|
+
fields.value.rubric = ''
|
|
428
|
+
fields.value.options = ''
|
|
429
|
+
fields.value.answer = ''
|
|
430
|
+
fields.value.blanks = ''
|
|
431
|
+
fields.value.mode = ''
|
|
432
|
+
}
|
|
433
|
+
}
|
|
391
434
|
hydrateQuizDraftsFromFields()
|
|
392
435
|
}
|
|
393
436
|
})
|
|
@@ -598,6 +641,8 @@ function close() {
|
|
|
598
641
|
>
|
|
599
642
|
⋮⋮
|
|
600
643
|
</span>
|
|
644
|
+
<input v-model="option.key" :placeholder="props.messages.placeholders.optionKey" class="aimd-field-input" />
|
|
645
|
+
<input v-model="option.text" :placeholder="props.messages.placeholders.optionText" class="aimd-field-input" />
|
|
601
646
|
<label class="aimd-option-answer-toggle">
|
|
602
647
|
<input
|
|
603
648
|
v-if="fields.mode === 'single'"
|
|
@@ -614,10 +659,8 @@ function close() {
|
|
|
614
659
|
:value="option.key.trim()"
|
|
615
660
|
:disabled="!option.key.trim()"
|
|
616
661
|
/>
|
|
617
|
-
<span>{{
|
|
662
|
+
<span>{{ props.messages.dialog.correctAnswer }}</span>
|
|
618
663
|
</label>
|
|
619
|
-
<input v-model="option.key" :placeholder="props.messages.placeholders.optionKey" class="aimd-field-input" />
|
|
620
|
-
<input v-model="option.text" :placeholder="props.messages.placeholders.optionText" class="aimd-field-input" />
|
|
621
664
|
<button
|
|
622
665
|
type="button"
|
|
623
666
|
class="aimd-mini-btn"
|
|
@@ -955,7 +998,7 @@ function close() {
|
|
|
955
998
|
}
|
|
956
999
|
|
|
957
1000
|
.aimd-option-row {
|
|
958
|
-
grid-template-columns: 26px
|
|
1001
|
+
grid-template-columns: 26px 60px 1fr 80px auto;
|
|
959
1002
|
}
|
|
960
1003
|
|
|
961
1004
|
.aimd-option-row-dragover {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
|
|
3
3
|
import type { AimdEditorMessages } from './locales'
|
|
4
|
+
import { collectAimdDiagnostics } from './aimdDiagnostics'
|
|
4
5
|
|
|
5
6
|
const props = defineProps<{
|
|
6
7
|
content: string
|
|
@@ -22,6 +23,7 @@ const SERVER_ASSIGNER_FENCE = /^\s*(```|~~~)\s*assigner(?:\s+.*)?\s*$/
|
|
|
22
23
|
const QUIZ_FENCE = /^\s*(```|~~~)\s*quiz(?:\s+.*)?\s*$/
|
|
23
24
|
const GENERIC_CODE_FENCE = /^\s*(```|~~~)\s*((?:\w|[/#-])+)(?:\s+.*)?\s*$/
|
|
24
25
|
const EMPTY_CODE_FENCE = /^\s*(```|~~~)\s*$/
|
|
26
|
+
const AIMD_DIAGNOSTIC_OWNER = 'airalogy-aimd'
|
|
25
27
|
|
|
26
28
|
const editorContainer = ref<HTMLElement | null>(null)
|
|
27
29
|
|
|
@@ -207,6 +209,40 @@ function registerAimdLanguage(monaco: any) {
|
|
|
207
209
|
})
|
|
208
210
|
}
|
|
209
211
|
|
|
212
|
+
function updateAimdDiagnostics() {
|
|
213
|
+
if (!monacoModule || !monacoEditorInstance) return
|
|
214
|
+
|
|
215
|
+
const model = monacoEditorInstance.getModel()
|
|
216
|
+
if (!model) return
|
|
217
|
+
|
|
218
|
+
const markers = collectAimdDiagnostics(model.getValue()).map((diagnostic) => {
|
|
219
|
+
const start = model.getPositionAt(diagnostic.startOffset)
|
|
220
|
+
const end = model.getPositionAt(diagnostic.endOffset)
|
|
221
|
+
return {
|
|
222
|
+
severity: diagnostic.severity === 'error'
|
|
223
|
+
? monacoModule.MarkerSeverity.Error
|
|
224
|
+
: monacoModule.MarkerSeverity.Warning,
|
|
225
|
+
message: diagnostic.message,
|
|
226
|
+
source: 'AIMD',
|
|
227
|
+
startLineNumber: start.lineNumber,
|
|
228
|
+
startColumn: start.column,
|
|
229
|
+
endLineNumber: end.lineNumber,
|
|
230
|
+
endColumn: end.column,
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
monacoModule.editor.setModelMarkers(model, AIMD_DIAGNOSTIC_OWNER, markers)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function clearAimdDiagnostics() {
|
|
238
|
+
if (!monacoModule || !monacoEditorInstance) return
|
|
239
|
+
|
|
240
|
+
const model = monacoEditorInstance.getModel()
|
|
241
|
+
if (model) {
|
|
242
|
+
monacoModule.editor.setModelMarkers(model, AIMD_DIAGNOSTIC_OWNER, [])
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
210
246
|
function createEditor(monaco: any) {
|
|
211
247
|
if (!editorContainer.value || monacoEditorInstance) return
|
|
212
248
|
const MONACO_OPTIONS_BLACKLIST = new Set(['value', 'language', 'model'])
|
|
@@ -230,10 +266,12 @@ function createEditor(monaco: any) {
|
|
|
230
266
|
...safeMonacoOptions,
|
|
231
267
|
})
|
|
232
268
|
monacoEditorInstance.onDidChangeModelContent(() => {
|
|
269
|
+
updateAimdDiagnostics()
|
|
233
270
|
if (!isSyncing) {
|
|
234
271
|
emit('content-change', monacoEditorInstance.getValue())
|
|
235
272
|
}
|
|
236
273
|
})
|
|
274
|
+
updateAimdDiagnostics()
|
|
237
275
|
emit('ready', monacoEditorInstance)
|
|
238
276
|
emit('monaco-loaded', monaco, monacoEditorInstance)
|
|
239
277
|
}
|
|
@@ -251,6 +289,7 @@ onMounted(async () => {
|
|
|
251
289
|
})
|
|
252
290
|
|
|
253
291
|
onBeforeUnmount(() => {
|
|
292
|
+
clearAimdDiagnostics()
|
|
254
293
|
monacoEditorInstance?.dispose()
|
|
255
294
|
})
|
|
256
295
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { parseVarDefinition, validateVarDefinition } from '@airalogy/aimd-core/parser'
|
|
2
|
+
|
|
3
|
+
export type AimdDiagnosticSeverity = 'warning' | 'error'
|
|
4
|
+
|
|
5
|
+
export interface AimdDiagnostic {
|
|
6
|
+
message: string
|
|
7
|
+
severity: AimdDiagnosticSeverity
|
|
8
|
+
startOffset: number
|
|
9
|
+
endOffset: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const AIMD_VAR_TEMPLATE_PATTERN = /\{\{\s*(var_table|var)\s*\|([\s\S]*?)\}\}/g
|
|
13
|
+
|
|
14
|
+
export function collectAimdDiagnostics(content: string): AimdDiagnostic[] {
|
|
15
|
+
const diagnostics: AimdDiagnostic[] = []
|
|
16
|
+
|
|
17
|
+
for (const match of content.matchAll(AIMD_VAR_TEMPLATE_PATTERN)) {
|
|
18
|
+
const startOffset = match.index ?? 0
|
|
19
|
+
const raw = match[0]
|
|
20
|
+
const fieldContent = match[2] ?? ''
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const warnings = validateVarDefinition(parseVarDefinition(fieldContent))
|
|
24
|
+
if (warnings.length > 0) {
|
|
25
|
+
diagnostics.push({
|
|
26
|
+
message: warnings.join('\n'),
|
|
27
|
+
severity: 'warning',
|
|
28
|
+
startOffset,
|
|
29
|
+
endOffset: startOffset + raw.length,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
// Keep source editing permissive; syntax-level parser failures should not
|
|
34
|
+
// block Monaco from rendering or reporting other diagnostics.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return diagnostics
|
|
39
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
const
|
|
1
|
+
const AIMD_INLINE_TEMPLATE_TYPE_PATTERN = String.raw`(?:var(?:\\?_table)?|step|check|ref\\?_(?:step|var|fig)|cite)`
|
|
2
|
+
const AIMD_INLINE_TEMPLATE_PATTERN = new RegExp(String.raw`\{\{${AIMD_INLINE_TEMPLATE_TYPE_PATTERN}\s*\|[^}]+?\}\}`, 'g')
|
|
2
3
|
const MARKDOWN_ESCAPABLE_PATTERN = /\\([\\!"#$%&'()*+,./:;<=>?@[\\\]^_`{|}~-])/g
|
|
3
4
|
|
|
4
5
|
function normalizeSingleAimdInlineTemplate(template: string): string {
|
package/src/vue/locales.ts
CHANGED
|
@@ -104,6 +104,7 @@ export interface AimdEditorMessages {
|
|
|
104
104
|
dragToReorder: string
|
|
105
105
|
answer: string
|
|
106
106
|
correct: string
|
|
107
|
+
correctAnswer: string
|
|
107
108
|
optionsHint: string
|
|
108
109
|
blanks: string
|
|
109
110
|
blanksHint: string
|
|
@@ -318,6 +319,7 @@ const EN_US_MESSAGES: AimdEditorMessages = {
|
|
|
318
319
|
dragToReorder: 'Drag to reorder',
|
|
319
320
|
answer: 'Answer',
|
|
320
321
|
correct: 'Correct',
|
|
322
|
+
correctAnswer: 'Correct Answer',
|
|
321
323
|
optionsHint: 'Use unique keys (A/B/C), then mark answer directly in each row.',
|
|
322
324
|
blanks: 'Blanks',
|
|
323
325
|
blanksHint: 'Use keys like b1, b2 and refer to them in stem as [[b1]], [[b2]].',
|
|
@@ -514,6 +516,7 @@ const ZH_CN_MESSAGES: AimdEditorMessages = {
|
|
|
514
516
|
dragToReorder: '拖拽排序',
|
|
515
517
|
answer: '答案',
|
|
516
518
|
correct: '正确',
|
|
519
|
+
correctAnswer: '正确答案',
|
|
517
520
|
optionsHint: '请使用唯一键(A/B/C),并直接在每一行标记答案。',
|
|
518
521
|
blanks: '填空项',
|
|
519
522
|
blanksHint: '请使用 b1、b2 这样的键,并在题干中写成 [[b1]]、[[b2]]。',
|
package/src/vue/types.ts
CHANGED
|
@@ -261,7 +261,7 @@ function parseQuizOptions(
|
|
|
261
261
|
function parseBlankItems(input: string): Array<{ key: string, answer: string }> {
|
|
262
262
|
const parts = input.split(',').map(s => s.trim()).filter(Boolean)
|
|
263
263
|
if (parts.length === 0) {
|
|
264
|
-
return [{ key: 'b1', answer: '
|
|
264
|
+
return [{ key: 'b1', answer: '' }]
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
return parts.map((part, index) => {
|
|
@@ -288,20 +288,30 @@ function parseOptionalScore(value: string): string | null {
|
|
|
288
288
|
export function getDefaultAimdFields(
|
|
289
289
|
type: string,
|
|
290
290
|
messages?: Pick<AimdEditorMessages, 'defaults'>,
|
|
291
|
+
existingQuizIds?: Set<string>,
|
|
291
292
|
): Record<string, string> {
|
|
292
293
|
switch (type) {
|
|
293
294
|
case 'var': return { name: '', type: 'str', default: '', title: '' }
|
|
294
295
|
case 'var_table': return { name: '', subvars: '' }
|
|
295
|
-
case 'quiz':
|
|
296
|
-
|
|
297
|
-
quizType
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
296
|
+
case 'quiz': {
|
|
297
|
+
const quizType = 'choice'
|
|
298
|
+
const base = `quiz_${quizType}`
|
|
299
|
+
let suffix = 1
|
|
300
|
+
while (existingQuizIds?.has(`${base}_${suffix}`)) {
|
|
301
|
+
suffix += 1
|
|
302
|
+
}
|
|
303
|
+
const id = `${base}_${suffix}`
|
|
304
|
+
return {
|
|
305
|
+
id,
|
|
306
|
+
quizType,
|
|
307
|
+
mode: 'single',
|
|
308
|
+
stem: messages?.defaults.questionStem || DEFAULT_EDITOR_MESSAGES.defaults.questionStem,
|
|
309
|
+
options: `A:${getDefaultOptionText('A', messages)}, B:${getDefaultOptionText('B', messages)}`,
|
|
310
|
+
answer: 'A',
|
|
311
|
+
blanks: 'b1:',
|
|
312
|
+
rubric: '',
|
|
313
|
+
score: '',
|
|
314
|
+
}
|
|
305
315
|
}
|
|
306
316
|
case 'step': return { name: '', level: '1' }
|
|
307
317
|
case 'check': return { name: '' }
|
|
@@ -139,9 +139,15 @@ export function useEditorContent(options: UseEditorContentOptions) {
|
|
|
139
139
|
function insertTextIntoActiveEditor(text: string) {
|
|
140
140
|
if (editorMode.value === 'source' && monacoEditor.value) {
|
|
141
141
|
const selection = monacoEditor.value.getSelection()
|
|
142
|
+
const model = monacoEditor.value.getModel()
|
|
143
|
+
const position = monacoEditor.value.getPosition()
|
|
144
|
+
const lineContent = model.getLineContent(position.lineNumber)
|
|
145
|
+
const beforeCursor = lineContent.substring(0, position.column - 1)
|
|
146
|
+
const needsLeadingNewline = beforeCursor.trim() !== '' && !text.startsWith('\n')
|
|
147
|
+
const finalText = needsLeadingNewline ? '\n' + text : text
|
|
142
148
|
monacoEditor.value.executeEdits('toolbar', [{
|
|
143
149
|
range: selection,
|
|
144
|
-
text,
|
|
150
|
+
text: finalText,
|
|
145
151
|
forceMoveMarkers: true,
|
|
146
152
|
}])
|
|
147
153
|
monacoEditor.value.focus()
|