@kevin5251984/guild 0.2.19 → 0.2.21

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.
@@ -167,12 +167,7 @@
167
167
  <a class="model-choice" href="/settings" data-i18n="model.settings">狀態欄</a>
168
168
  </div>
169
169
  </div>
170
- <select id="model-reasoning" hidden>
171
- <option value="minimal" data-i18n="reason.min">最低</option>
172
- <option value="low" data-i18n="reason.low">低</option>
173
- <option value="medium" data-i18n="reason.mid">中</option>
174
- <option value="high" data-i18n="reason.high">高</option>
175
- </select>
170
+ <select id="model-reasoning" hidden></select>
176
171
  <input type="checkbox" id="model-fast" hidden />
177
172
  </div>
178
173
  </div>
@@ -266,6 +261,13 @@
266
261
  </div>
267
262
  <script>
268
263
  const COLORS = ["#7c6af7", "#5b8def", "#2ea887", "#e07a3d", "#d4537e"];
264
+ const SEAT_HUE = {
265
+ infra: "#5b8def",
266
+ pm: "#2ea887",
267
+ rd: "#e07a3d",
268
+ design: "#7c6af7",
269
+ marketing: "#d4537e",
270
+ };
269
271
  const CHANNEL_ROSTER_CAP = 6;
270
272
  function isGeneralChannel(ch) {
271
273
  return Boolean(ch && (ch.id === "channel-general" || ch.name === "general"));
@@ -321,6 +323,8 @@
321
323
  };
322
324
 
323
325
  function hue(key) {
326
+ const seat = SEAT_HUE[String(key).toLowerCase()];
327
+ if (seat) return seat;
324
328
  let n = 0;
325
329
  for (const ch of String(key)) n = (n + ch.charCodeAt(0) * 17) % COLORS.length;
326
330
  return COLORS[n];
@@ -1036,14 +1040,48 @@
1036
1040
  }
1037
1041
  return out;
1038
1042
  }
1043
+ function deferredHandles(text) {
1044
+ const raw = String(text || "");
1045
+ const out = [];
1046
+ const add = (name) => {
1047
+ const key = String(name || "").toLowerCase();
1048
+ if (key && out.indexOf(key) < 0) out.push(key);
1049
+ };
1050
+ const scan = raw.replace(/```[\s\S]*?```/g, " ");
1051
+ const cue =
1052
+ /(?:完成後|通過後|最後(?:才|由)?|才需要|才叫|才交)[^@\n]{0,24}@([A-Za-z0-9_-]+)/gi;
1053
+ const bare =
1054
+ /(?:完成後|才需要|才叫)\s*(?:call|ask|由)\s*`?@?([A-Za-z0-9_-]+)`?/gi;
1055
+ let row;
1056
+ while ((row = cue.exec(scan))) add(row[1]);
1057
+ while ((row = bare.exec(scan))) add(row[1]);
1058
+ const later =
1059
+ /^[ \t]*(?:\d+[.)、]\s*|[-*•]\s+)(?:通過後|最後|然後|之後|完成後)\s*`?@([A-Za-z0-9_-]+)`?/i;
1060
+ const lines = raw.split(/\r?\n/);
1061
+ for (let i = 0; i < lines.length; i++) {
1062
+ const hit = lines[i].match(later);
1063
+ if (hit) add(hit[1]);
1064
+ }
1065
+ return out;
1066
+ }
1039
1067
  function summonedBotIds(text) {
1040
1068
  const raw = String(text || "");
1069
+ const deferred = new Set(deferredHandles(raw));
1070
+ const members = new Set(
1071
+ state.kind === "channel"
1072
+ ? ((currentChannel() && currentChannel().memberIds) || [])
1073
+ : state.kind === "dm"
1074
+ ? [state.id]
1075
+ : [],
1076
+ );
1041
1077
  const known = new Map(
1042
- (state.bots || []).map((bot) => [String(bot.handle).toLowerCase(), bot.id]),
1078
+ (state.bots || [])
1079
+ .filter((bot) => !members.size || members.has(bot.id))
1080
+ .map((bot) => [String(bot.handle).toLowerCase(), bot.id]),
1043
1081
  );
1044
1082
  const out = [];
1045
- const push = (id) => {
1046
- if (id && !out.includes(id)) out.push(id);
1083
+ const push = (id, handle) => {
1084
+ if (id && !deferred.has(handle) && !out.includes(id)) out.push(id);
1047
1085
  };
1048
1086
  let fence = false;
1049
1087
  const lines = raw.split(/\r?\n/);
@@ -1060,11 +1098,21 @@
1060
1098
  if (!m) continue;
1061
1099
  const names = m[1].match(/@([A-Za-z0-9_-]+)/g) || [];
1062
1100
  for (let n = 0; n < names.length; n++) {
1063
- push(known.get(names[n].slice(1).toLowerCase()));
1101
+ const handle = names[n].slice(1).toLowerCase();
1102
+ push(known.get(handle), handle);
1064
1103
  }
1065
1104
  }
1066
1105
  if (out.length) return out;
1067
- return mentionedBotIds(raw).slice(0, 1);
1106
+ return mentionedBotIds(raw)
1107
+ .filter((id) => {
1108
+ const bot = (state.bots || []).find((row) => row.id === id);
1109
+ return (
1110
+ bot &&
1111
+ (!members.size || members.has(id)) &&
1112
+ !deferred.has(String(bot.handle).toLowerCase())
1113
+ );
1114
+ })
1115
+ .slice(0, 1);
1068
1116
  }
1069
1117
  function lastBotAuthor() {
1070
1118
  const msgs = state.messages || [];
@@ -1244,7 +1292,20 @@
1244
1292
  }
1245
1293
  function botModelMeta(bot) {
1246
1294
  const model = botModelLabel(bot) || "—";
1247
- const reason = reasonLabel((modelCfg && modelCfg.reasoning) || "medium");
1295
+ const ref = botModelRef(bot);
1296
+ const spec = (() => {
1297
+ if (!ref) return null;
1298
+ const hit = allModels().find(
1299
+ (row) => row.provider === ref.provider && row.model === ref.model,
1300
+ );
1301
+ return (hit && hit.reasoning) || null;
1302
+ })();
1303
+ const reasonVal = clampEffort(
1304
+ ref && ref.reasoning,
1305
+ spec,
1306
+ Boolean(modelCfg && modelCfg.fast),
1307
+ );
1308
+ const reason = reasonVal ? reasonLabel(reasonVal) : "—";
1248
1309
  const provider = botProviderLabel(bot) || "—";
1249
1310
  return model + " · " + reason + " · " + provider;
1250
1311
  }
@@ -1652,6 +1713,7 @@
1652
1713
  thinking: row.thinking || "",
1653
1714
  steps: Array.isArray(row.steps) ? row.steps.slice(-5) : [],
1654
1715
  startedAt: row.startedAt || "",
1716
+ paused: Boolean(row.paused),
1655
1717
  }));
1656
1718
  }
1657
1719
  function rebuildBusyBots() {
@@ -1680,6 +1742,7 @@
1680
1742
  thinking: row.thinking || "",
1681
1743
  steps: Array.isArray(row.steps) ? row.steps.slice(-5) : [],
1682
1744
  startedAt: row.startedAt || "",
1745
+ paused: Boolean(row.paused),
1683
1746
  room: { kind: r.kind, id: r.id },
1684
1747
  }));
1685
1748
  state.lives = others.concat(tagged);
@@ -1730,24 +1793,38 @@
1730
1793
  ? '<span class="clock">' + Math.floor(elapsed / 1000) + "s</span>"
1731
1794
  : "";
1732
1795
  const botId = live && live.botId ? live.botId : "";
1796
+ const paused = Boolean(live && live.paused);
1733
1797
  const actions = botId
1734
1798
  ? '<div class="turn-actions">' +
1799
+ (paused
1800
+ ? '<button type="button" class="turn-continue" data-live-continue="' +
1801
+ escapeHtml(botId) +
1802
+ '">' +
1803
+ t("live.continue") +
1804
+ "</button>"
1805
+ : '<button type="button" class="turn-pause" data-live-pause="' +
1806
+ escapeHtml(botId) +
1807
+ '">' +
1808
+ t("live.pause") +
1809
+ "</button>") +
1735
1810
  '<button type="button" class="turn-stop" data-live-stop="' +
1736
1811
  escapeHtml(botId) +
1737
1812
  '">' +
1738
1813
  t("live.stop") +
1739
1814
  "</button>" +
1740
- '<button type="button" class="turn-steer" data-live-steer="' +
1741
- escapeHtml(botId) +
1742
- '">' +
1743
- t("live.steer") +
1744
- "</button>" +
1815
+ (paused
1816
+ ? ""
1817
+ : '<button type="button" class="turn-steer" data-live-steer="' +
1818
+ escapeHtml(botId) +
1819
+ '">' +
1820
+ t("live.steer") +
1821
+ "</button>") +
1745
1822
  "</div>"
1746
1823
  : "";
1747
1824
  return (
1748
1825
  '<div class="turn-live">' +
1749
1826
  '<div class="turn-status">' +
1750
- t("deepDiving") +
1827
+ (paused ? t("live.paused") : t("deepDiving")) +
1751
1828
  clock +
1752
1829
  "</div>" +
1753
1830
  liveStepsHtml(live && live.steps) +
@@ -1768,7 +1845,9 @@
1768
1845
  return (
1769
1846
  '<article class="msg bot live" data-bot-id="' +
1770
1847
  escapeHtml(bot.id) +
1771
- '" role="status">' +
1848
+ '"' +
1849
+ (live.paused ? ' data-paused="1"' : "") +
1850
+ ' role="status">' +
1772
1851
  actor.av +
1773
1852
  '<div class="msg-main">' +
1774
1853
  actor.head +
@@ -1805,6 +1884,11 @@
1805
1884
  const block = article.querySelector(".turn-live");
1806
1885
  if (!block) return;
1807
1886
  const status = block.querySelector(".turn-status");
1887
+ const paused = Boolean(live.paused);
1888
+ if ((article.getAttribute("data-paused") === "1") !== paused) {
1889
+ renderThread();
1890
+ return;
1891
+ }
1808
1892
  if (status) {
1809
1893
  const started = live.startedAt ? Date.parse(live.startedAt) : NaN;
1810
1894
  const elapsed = Number.isFinite(started)
@@ -1814,7 +1898,7 @@
1814
1898
  elapsed >= 15000
1815
1899
  ? '<span class="clock">' + Math.floor(elapsed / 1000) + "s</span>"
1816
1900
  : "";
1817
- status.innerHTML = t("deepDiving") + clock;
1901
+ status.innerHTML = (paused ? t("live.paused") : t("deepDiving")) + clock;
1818
1902
  }
1819
1903
  const html = liveStepsHtml(live.steps);
1820
1904
  const existing = block.querySelector(".live-steps");
@@ -2267,6 +2351,18 @@
2267
2351
  ? "/dms/" + encodeURIComponent(r.id) + "/abort"
2268
2352
  : "/channels/" + encodeURIComponent(r.id) + "/abort";
2269
2353
  }
2354
+ function pauseUrl(room) {
2355
+ const r = room || state.busyRoom || { kind: state.kind, id: state.id };
2356
+ return r.kind === "dm"
2357
+ ? "/dms/" + encodeURIComponent(r.id) + "/pause"
2358
+ : "/channels/" + encodeURIComponent(r.id) + "/pause";
2359
+ }
2360
+ function continueUrl(room) {
2361
+ const r = room || state.busyRoom || { kind: state.kind, id: state.id };
2362
+ return r.kind === "dm"
2363
+ ? "/dms/" + encodeURIComponent(r.id) + "/continue"
2364
+ : "/channels/" + encodeURIComponent(r.id) + "/continue";
2365
+ }
2270
2366
  function stopTurn(botId) {
2271
2367
  const room = currentRoomRef();
2272
2368
  (state.aborts || []).forEach((item) => {
@@ -2292,6 +2388,30 @@
2292
2388
  }).catch(() => {});
2293
2389
  setBusy(false, botId ? [botId] : [], { room: room });
2294
2390
  }
2391
+ function pauseTurn(botId) {
2392
+ const room = currentRoomRef();
2393
+ fetch(pauseUrl(room), {
2394
+ method: "POST",
2395
+ headers: { "content-type": "application/json" },
2396
+ body: JSON.stringify(botId ? { botId: botId } : {}),
2397
+ }).catch(() => {});
2398
+ (state.lives || []).forEach((row) => {
2399
+ if (!botId || row.botId === botId) row.paused = true;
2400
+ });
2401
+ if (state.live && (!botId || state.live.botId === botId)) {
2402
+ state.live.paused = true;
2403
+ }
2404
+ renderThread();
2405
+ }
2406
+ async function continueTurn(botId) {
2407
+ const room = currentRoomRef();
2408
+ (state.lives || []).forEach((row) => {
2409
+ if (row.botId === botId) row.paused = false;
2410
+ });
2411
+ if (state.live && state.live.botId === botId) state.live.paused = false;
2412
+ renderThread();
2413
+ await postChat(continueUrl(room), { botId: botId }, room, [botId]);
2414
+ }
2295
2415
  async function insertIntoBotTurn(botId) {
2296
2416
  if (!composerHasPayload()) {
2297
2417
  const draft = document.getElementById("draft");
@@ -2756,6 +2876,22 @@
2756
2876
  stopTurn(liveStop.getAttribute("data-live-stop"));
2757
2877
  return;
2758
2878
  }
2879
+ const livePause = event.target.closest("[data-live-pause]");
2880
+ if (livePause) {
2881
+ event.preventDefault();
2882
+ event.stopImmediatePropagation();
2883
+ pauseTurn(livePause.getAttribute("data-live-pause"));
2884
+ return;
2885
+ }
2886
+ const liveContinue = event.target.closest("[data-live-continue]");
2887
+ if (liveContinue) {
2888
+ event.preventDefault();
2889
+ event.stopImmediatePropagation();
2890
+ continueTurn(liveContinue.getAttribute("data-live-continue")).catch(
2891
+ (err) => alert(err.message),
2892
+ );
2893
+ return;
2894
+ }
2759
2895
  const liveSteer = event.target.closest("[data-live-steer]");
2760
2896
  if (liveSteer) {
2761
2897
  event.preventDefault();
@@ -3023,7 +3159,12 @@
3023
3159
  if (bot) showBotCard(bot, event.currentTarget);
3024
3160
  });
3025
3161
  document.getElementById("thread").addEventListener("click", (event) => {
3026
- if (event.target.closest("[data-live-stop], [data-live-steer]")) return;
3162
+ if (
3163
+ event.target.closest(
3164
+ "[data-live-stop], [data-live-pause], [data-live-continue], [data-live-steer]",
3165
+ )
3166
+ )
3167
+ return;
3027
3168
  const av = event.target.closest(
3028
3169
  ".avatar[data-bot-id], button.name[data-bot-id]",
3029
3170
  );
@@ -4391,6 +4532,9 @@
4391
4532
  }
4392
4533
  setBusy(false, botIds, { room: sent });
4393
4534
  }
4535
+ if (state.kind === sent.kind && state.id === sent.id) {
4536
+ await resumeCurrentLive().catch(() => {});
4537
+ }
4394
4538
  if (ok) await flushQueued(sent);
4395
4539
  }
4396
4540
  async function flushQueued(sent) {
@@ -5332,6 +5476,7 @@
5332
5476
  group: p.name || p.id,
5333
5477
  ready: Boolean(p.ready),
5334
5478
  kind: p.kind || "key",
5479
+ reasoning: m.reasoning || null,
5335
5480
  });
5336
5481
  }
5337
5482
  }
@@ -5345,10 +5490,45 @@
5345
5490
  return hit ? hit.name : ref.model;
5346
5491
  }
5347
5492
  function reasonLabel(value) {
5493
+ if (value === "none") return t("reason.none");
5348
5494
  if (value === "minimal") return t("reason.min");
5349
5495
  if (value === "low") return t("reason.low");
5496
+ if (value === "medium") return t("reason.mid");
5350
5497
  if (value === "high") return t("reason.high");
5351
- return t("reason.mid");
5498
+ if (value === "xhigh") return t("reason.xhigh");
5499
+ if (value === "max") return t("reason.max");
5500
+ return value || t("reason.mid");
5501
+ }
5502
+ function activeReasoningSpec() {
5503
+ const ref = activeModelRef();
5504
+ if (!ref) return null;
5505
+ const hit = allModels().find(
5506
+ (row) => row.provider === ref.provider && row.model === ref.model,
5507
+ );
5508
+ return (hit && hit.reasoning) || null;
5509
+ }
5510
+ function effortChoices(spec) {
5511
+ const raw = spec && spec.supportedEfforts;
5512
+ if (!raw || !raw.length) return [];
5513
+ return spec.mandatory
5514
+ ? raw.filter((key) => key !== "none")
5515
+ : raw.slice();
5516
+ }
5517
+ function clampEffort(want, spec, fast) {
5518
+ const choices = effortChoices(spec);
5519
+ if (!choices.length) return "";
5520
+ if (fast) {
5521
+ if (choices.indexOf("low") >= 0) return "low";
5522
+ if (choices.indexOf("minimal") >= 0) return "minimal";
5523
+ return choices[choices.length - 1];
5524
+ }
5525
+ if (want && choices.indexOf(want) >= 0) return want;
5526
+ if (spec.defaultEffort && choices.indexOf(spec.defaultEffort) >= 0) {
5527
+ return spec.defaultEffort;
5528
+ }
5529
+ if (choices.indexOf("high") >= 0) return "high";
5530
+ if (choices.indexOf("medium") >= 0) return "medium";
5531
+ return choices[0];
5352
5532
  }
5353
5533
  function speedLabel() {
5354
5534
  return modelCfg.fast ? t("speed.fast") : t("speed.normal");
@@ -5450,25 +5630,45 @@
5450
5630
  }
5451
5631
  document.getElementById("model-results").innerHTML =
5452
5632
  html || '<p class="model-empty">' + t("noModels") + "</p>";
5453
- document.getElementById("model-reasoning").value = modelCfg.reasoning || "medium";
5454
5633
  document.getElementById("model-fast").checked = Boolean(modelCfg.fast);
5455
- const reason = reasonLabel(modelCfg.reasoning || "medium");
5634
+ const spec = activeReasoningSpec();
5635
+ const choices = effortChoices(spec);
5636
+ const currentRef = activeModelRef();
5637
+ const reasonVal = clampEffort(
5638
+ currentRef && currentRef.reasoning,
5639
+ spec,
5640
+ Boolean(modelCfg.fast),
5641
+ );
5642
+ const reasonSel = document.getElementById("model-reasoning");
5643
+ reasonSel.innerHTML = choices
5644
+ .map(
5645
+ (value) =>
5646
+ '<option value="' +
5647
+ value +
5648
+ '">' +
5649
+ reasonLabel(value) +
5650
+ "</option>",
5651
+ )
5652
+ .join("");
5653
+ if (reasonVal) reasonSel.value = reasonVal;
5654
+ const reasonRow = document.querySelector('.model-nav [data-sheet="reasoning"]');
5655
+ if (reasonRow) reasonRow.hidden = !choices.length;
5656
+ if (!choices.length) {
5657
+ const pane = document.getElementById("model-sheet-reasoning");
5658
+ if (pane && !pane.hidden) showModelSheet("models");
5659
+ }
5660
+ const reason = choices.length ? reasonLabel(reasonVal) : "";
5456
5661
  const speed = speedLabel();
5457
5662
  const name = displayName(current);
5458
5663
  document.getElementById("model-btn-label").textContent = current
5459
- ? name + " " + reason
5664
+ ? name + (reason ? " " + reason : "")
5460
5665
  : name;
5461
5666
  document.getElementById("model-row-model").textContent = name;
5462
- document.getElementById("model-row-reason").textContent = reason;
5667
+ document.getElementById("model-row-reason").textContent = reason || "—";
5463
5668
  document.getElementById("model-row-speed").textContent = speed;
5464
- document.getElementById("model-reason-list").innerHTML = [
5465
- ["minimal", t("reason.min")],
5466
- ["low", t("reason.low")],
5467
- ["medium", t("reason.mid")],
5468
- ["high", t("reason.high")],
5469
- ]
5470
- .map(([value, label]) => {
5471
- const on = (modelCfg.reasoning || "medium") === value ? " on" : "";
5669
+ document.getElementById("model-reason-list").innerHTML = choices
5670
+ .map((value) => {
5671
+ const on = reasonVal === value ? " on" : "";
5472
5672
  const tick = on ? '<span class="tick">✓</span>' : "";
5473
5673
  return (
5474
5674
  '<button type="button" class="model-choice' +
@@ -5476,7 +5676,7 @@
5476
5676
  '" data-reason="' +
5477
5677
  value +
5478
5678
  '">' +
5479
- label +
5679
+ reasonLabel(value) +
5480
5680
  tick +
5481
5681
  "</button>"
5482
5682
  );
@@ -5505,11 +5705,24 @@
5505
5705
  async function applyChatModel(ref) {
5506
5706
  if (state.kind !== "dm") return;
5507
5707
  const bot = botById(state.id);
5508
- if (!bot) return;
5708
+ if (!bot || !ref || !ref.provider || !ref.model) return;
5709
+ const spec = (() => {
5710
+ const hit = allModels().find(
5711
+ (row) => row.provider === ref.provider && row.model === ref.model,
5712
+ );
5713
+ return (hit && hit.reasoning) || null;
5714
+ })();
5715
+ const reason = clampEffort(
5716
+ document.getElementById("model-reasoning").value || ref.reasoning,
5717
+ spec,
5718
+ Boolean(document.getElementById("model-fast").checked),
5719
+ );
5720
+ const next = { provider: ref.provider, model: ref.model };
5721
+ if (reason) next.reasoning = reason;
5509
5722
  const res = await fetch("/bots/" + encodeURIComponent(bot.id), {
5510
5723
  method: "PATCH",
5511
5724
  headers: { "content-type": "application/json" },
5512
- body: JSON.stringify({ model: ref }),
5725
+ body: JSON.stringify({ model: next }),
5513
5726
  });
5514
5727
  if (!res.ok) {
5515
5728
  const body = await res.json().catch(() => ({}));
@@ -5519,16 +5732,16 @@
5519
5732
  const updated = await res.json();
5520
5733
  const i = state.bots.findIndex((item) => item.id === bot.id);
5521
5734
  if (i >= 0) state.bots[i] = { ...state.bots[i], model: updated.model };
5522
- await fetch("/settings/models", {
5523
- method: "PUT",
5524
- headers: { "content-type": "application/json" },
5525
- body: JSON.stringify({
5526
- reasoning: document.getElementById("model-reasoning").value,
5527
- fast: document.getElementById("model-fast").checked,
5528
- }),
5529
- }).then(async (r) => {
5530
- if (r.ok) modelCfg = await r.json();
5531
- });
5735
+ const fast = document.getElementById("model-fast").checked;
5736
+ if (Boolean(modelCfg.fast) !== Boolean(fast)) {
5737
+ await fetch("/settings/models", {
5738
+ method: "PUT",
5739
+ headers: { "content-type": "application/json" },
5740
+ body: JSON.stringify({ fast: fast }),
5741
+ }).then(async (r) => {
5742
+ if (r.ok) modelCfg = await r.json();
5743
+ });
5744
+ }
5532
5745
  renderModelList();
5533
5746
  renderNav();
5534
5747
  }
@@ -90,6 +90,9 @@ var I18N_ROWS = [
90
90
  ["stop", "停止", "Stop"],
91
91
  ["steer.hint", "Enter 排隊 · {mod}↩ 插入這輪", "Enter to queue · {mod}↩ to steer this turn"],
92
92
  ["live.stop", "停止", "Stop"],
93
+ ["live.pause", "暫停", "Pause"],
94
+ ["live.continue", "繼續", "Continue"],
95
+ ["live.paused", "已暫停", "Paused"],
93
96
  ["live.steer", "插入這輪", "Insert into turn"],
94
97
  ["steer.queue", "排隊", "Queue"],
95
98
  ["steer.insert", "插入引導", "Steer"],
@@ -119,10 +122,13 @@ var I18N_ROWS = [
119
122
  ["local", "本地", "Local"],
120
123
  ["you", "你", "You"],
121
124
  ["emptyParen", "(空)", "(empty)"],
125
+ ["reason.none", "關閉", "Off"],
122
126
  ["reason.min", "最低", "Min"],
123
127
  ["reason.low", "低", "Low"],
124
128
  ["reason.mid", "中", "Mid"],
125
129
  ["reason.high", "高", "High"],
130
+ ["reason.xhigh", "極高", "Extra high"],
131
+ ["reason.max", "最大", "Max"],
126
132
  ["speed.normal", "標準", "Standard"],
127
133
  ["speed.fast", "快速", "Fast"],
128
134
  ["model", "模型", "Model"],
package/src/public/md.js CHANGED
@@ -327,6 +327,23 @@ function renderMarkdown(raw) {
327
327
  return html.join("");
328
328
  }
329
329
 
330
+ function hydrateHtmlPreviews(root) {
331
+ if (!root || typeof root.querySelectorAll !== "function") return;
332
+ root.querySelectorAll(".md-html-preview").forEach((box) => {
333
+ const src = box.querySelector(".md-html-src");
334
+ const frame = box.querySelector(".md-html-frame");
335
+ if (!src || !frame || frame.dataset.ready) return;
336
+ frame.dataset.ready = "1";
337
+ const raw = src.value;
338
+ const lang = (box.querySelector(".md-fence-lang") || {}).textContent || "";
339
+ frame.srcdoc = /svg/i.test(lang)
340
+ ? '<!doctype html><html><body style="margin:0;background:#fff">' +
341
+ raw +
342
+ "</body></html>"
343
+ : raw;
344
+ });
345
+ }
346
+
330
347
  if (typeof module !== "undefined" && module.exports) {
331
- module.exports = { renderMarkdown, inlineMd };
348
+ module.exports = { renderMarkdown, inlineMd, hydrateHtmlPreviews };
332
349
  }