@airalogy/aimd-editor 1.8.0 → 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.
@@ -118,6 +118,7 @@ const jy = {
118
118
  dragToReorder: "Drag to reorder",
119
119
  answer: "Answer",
120
120
  correct: "Correct",
121
+ correctAnswer: "Correct Answer",
121
122
  optionsHint: "Use unique keys (A/B/C), then mark answer directly in each row.",
122
123
  blanks: "Blanks",
123
124
  blanksHint: "Use keys like b1, b2 and refer to them in stem as [[b1]], [[b2]].",
@@ -312,6 +313,7 @@ const jy = {
312
313
  dragToReorder: "拖拽排序",
313
314
  answer: "答案",
314
315
  correct: "正确",
316
+ correctAnswer: "正确答案",
315
317
  optionsHint: "请使用唯一键(A/B/C),并直接在每一行标记答案。",
316
318
  blanks: "填空项",
317
319
  blanksHint: "请使用 b1、b2 这样的键,并在题干中写成 [[b1]]、[[b2]]。",
@@ -560,7 +562,7 @@ function n1(t, e) {
560
562
  }
561
563
  function r1(t) {
562
564
  const e = t.split(",").map((n) => n.trim()).filter(Boolean);
563
- return e.length === 0 ? [{ key: "b1", answer: "21%" }] : e.map((n, r) => {
565
+ return e.length === 0 ? [{ key: "b1", answer: "" }] : e.map((n, r) => {
564
566
  const i = n.indexOf(":");
565
567
  if (i > 0) {
566
568
  const o = n.slice(0, i).trim() || `b${r + 1}`, s = n.slice(i + 1).trim() || "";
@@ -576,24 +578,29 @@ function i1(t) {
576
578
  const n = Number(e);
577
579
  return Number.isNaN(n) || n < 0 ? null : String(n);
578
580
  }
579
- function dI(t, e) {
581
+ function dI(t, e, n) {
580
582
  switch (t) {
581
583
  case "var":
582
584
  return { name: "", type: "str", default: "", title: "" };
583
585
  case "var_table":
584
586
  return { name: "", subvars: "" };
585
- case "quiz":
587
+ case "quiz": {
588
+ const r = "choice", i = `quiz_${r}`;
589
+ let o = 1;
590
+ for (; n?.has(`${i}_${o}`); )
591
+ o += 1;
586
592
  return {
587
- id: "quiz_choice_1",
588
- quizType: "choice",
593
+ id: `${i}_${o}`,
594
+ quizType: r,
589
595
  mode: "single",
590
596
  stem: e?.defaults.questionStem || hn.defaults.questionStem,
591
597
  options: `A:${tr("A", e)}, B:${tr("B", e)}`,
592
598
  answer: "A",
593
- blanks: "b1:21%",
599
+ blanks: "b1:",
594
600
  rubric: "",
595
601
  score: ""
596
602
  };
603
+ }
597
604
  case "step":
598
605
  return { name: "", level: "1" };
599
606
  case "check":
@@ -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
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-tRm4S2Nv.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-yYY-Yq1g.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
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-tRm4S2Nv.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-yYY-Yq1g.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
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-tRm4S2Nv.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-tRm4S2Nv.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,7 +1,7 @@
1
1
  {
2
2
  "name": "@airalogy/aimd-editor",
3
3
  "type": "module",
4
- "version": "1.8.0",
4
+ "version": "1.9.0",
5
5
  "description": "AIMD (Airalogy Markdown) editor with Monaco source mode and Milkdown WYSIWYG mode",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -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 {
@@ -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()