@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.
@@ -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-t_sUoXky.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-B8o1VbUH.js";
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-gbfMDZSh.js";
3
- import { _ as p } from "./AimdSourceEditor.vue_vue_type_script_setup_true_lang-t_sUoXky.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-B8o1VbUH.js";
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-gbfMDZSh.js";
2
- import { _ as m } from "./AimdSourceEditor.vue_vue_type_script_setup_true_lang-t_sUoXky.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-B8o1VbUH.js";
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-B8o1VbUH.js";
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.7.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
+ }
@@ -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: props.messages.defaults.optionText('A') },
85
- { key: 'B', text: props.messages.defaults.optionText('B') },
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 `A:${props.messages.defaults.optionText('A')}, B:${props.messages.defaults.optionText('B')}`
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: '21%' }]
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:21%'
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: props.messages.defaults.optionText(key) })
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>{{ fields.mode === 'single' ? props.messages.dialog.answer : props.messages.dialog.correct }}</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 96px 90px 1fr auto;
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 AIMD_INLINE_TEMPLATE_PATTERN = /\{\{(var_table|var|step|check|ref_step|ref_var|ref_fig|cite)\s*\|[^}]+?\}\}/g
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 {
@@ -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: '21%' }]
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': return {
296
- id: 'quiz_choice_1',
297
- quizType: 'choice',
298
- mode: 'single',
299
- stem: messages?.defaults.questionStem || DEFAULT_EDITOR_MESSAGES.defaults.questionStem,
300
- options: `A:${getDefaultOptionText('A', messages)}, B:${getDefaultOptionText('B', messages)}`,
301
- answer: 'A',
302
- blanks: 'b1:21%',
303
- rubric: '',
304
- score: '',
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()