dsh-plugin-image-workbench 1.1.11 → 1.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-image-workbench",
3
- "version": "1.1.11",
3
+ "version": "1.1.13",
4
4
  "type": "module",
5
5
  "description": "Bluxe手绘工坊 · 生图工作台(图文 / 摄影):DSH 侧栏图像生成工作台,默认使用内置本地账号池,亦支持任意 OpenAI 兼容接口(Base URL / API Key / 模型);可自定义工作台名称与图片存储目录。",
6
6
  "main": "./host.js",
package/server/pool.py CHANGED
@@ -603,13 +603,16 @@ class PoolManager:
603
603
 
604
604
  # ---------- 并行数(用户可调) ----------
605
605
  def valid_accounts(self) -> int:
606
- """有效账号数 = 状态为可用(非限流/离线)的号。
606
+ """有效账号数 = 能出图的号(可用 + 正在出图中)。
607
607
 
608
- 明细拿不到时退回号池自报的 active(/health 汇总);都拿不到就是 0。
608
+ 「正在出图」也是有效账号,不能因为它在忙就从上限里扣掉:否则一边出图
609
+ 一边上限往下掉,用户刚设好的并行数会被自己压回去。
610
+ 限流 / 已下线 / 未绑定 / 待验证 / 异常这些才算无效。
609
611
  """
610
- ready = sum(1 for a in self.accounts if a.get("status") == "ready")
611
- if ready:
612
- return ready
612
+ if self.accounts:
613
+ return sum(1 for a in self.accounts
614
+ if a.get("status") in ("ready", "busy"))
615
+ # 账号明细拿不到时退回号池自报的可用数
613
616
  try:
614
617
  return int(self.pool_stat.get("active") or 0)
615
618
  except Exception:
@@ -650,10 +653,10 @@ class PoolManager:
650
653
  save_config({"workers": target})
651
654
  if target != old:
652
655
  log(f"并行数 {old} → {target}"
653
- + (f"(请求 {want},按可用账号 {cap} 收紧)" if target != want else ""))
656
+ + (f"(请求 {want},按有效账号 {cap} 收紧)" if target != want else ""))
654
657
  msg = f"并行上限已设为 {target}"
655
658
  if want > cap:
656
- msg = f"最多 {cap} 个:并行数不能超过可用账号数,已按 {cap} 执行"
659
+ msg = f"最多 {cap} 个:并行数不能超过有效账号数,已按 {cap} 执行"
657
660
  return {"ok": True, "workers": target, "max": cap, "requested": want,
658
661
  "prev": old, "clamped": want > cap, "message": msg}
659
662
 
@@ -1003,7 +1003,7 @@
1003
1003
  <button class="btn ghost mini" id="monClose" type="button">关闭</button>
1004
1004
  </div>
1005
1005
  <div class="mon-sum" id="monSum"></div>
1006
- <!-- 并行上限:默认 3,可自行加减。上限跟着可用账号数走,超了自动压回来 -->
1006
+ <!-- 并行上限:默认 3,可自行加减。上限跟着有效账号数走(在出图的号也算),超了自动压回来 -->
1007
1007
  <div class="mon-wbar">
1008
1008
  <span class="wlbl">并行上限</span>
1009
1009
  <div class="wctl">
@@ -1011,7 +1011,7 @@
1011
1011
  <input id="monW" type="number" min="1" step="1" inputmode="numeric" aria-label="并行上限">
1012
1012
  <button type="button" class="wbtn" id="monWUp" title="加 1" aria-label="并行上限加 1">+</button>
1013
1013
  </div>
1014
- <span class="whint" id="monWHint">默认 3,最多不超过可用账号数</span>
1014
+ <span class="whint" id="monWHint">默认 3,最多不超过有效账号数</span>
1015
1015
  </div>
1016
1016
  <div class="mon-sec"><b>账号状态</b><span id="monAccHint"></span><span class="spacer"></span><span id="monCheckHint"></span></div>
1017
1017
  <div class="mon-box">
@@ -3944,7 +3944,7 @@ async function stopAllJobs(){
3944
3944
  const ST_LABEL = {ready:'可用', busy:'出图中', limited:'限流', unhealthy:'已下线', unbound:'未绑定', pending:'待验证', error:'异常'};
3945
3945
  let monTimer = null;
3946
3946
 
3947
- /* ---------- 并行上限:默认 3,用户可调,封顶 = 可用账号数 ---------- */
3947
+ /* ---------- 并行上限:默认 3,用户可调,封顶 = 有效账号数(在出图的号也算有效) ---------- */
3948
3948
  let _wBusy = false, _wLast = null, _wHintTimer = null;
3949
3949
 
3950
3950
  function syncWorkersCtl(d){
@@ -3966,8 +3966,8 @@ function syncWorkersCtl(d){
3966
3966
  const over = cur > cap;
3967
3967
  hint.className = 'whint' + (over ? ' warn' : '');
3968
3968
  hint.textContent = over
3969
- ? `当前 ${cur} 个,已超过可用账号 ${cap} 个,建议调低`
3970
- : `默认 3,最多 ${cap} 个${valid ? `(可用账号 ${valid} 个)` : ''}`;
3969
+ ? `当前 ${cur} 个,已超过有效账号 ${cap} 个,建议调低`
3970
+ : `默认 3,最多 ${cap} 个${valid ? `(有效账号 ${valid} 个)` : ''}`;
3971
3971
  }
3972
3972
 
3973
3973
  function flashWorkersHint(ms){
@@ -4058,7 +4058,7 @@ function renderMonitor(d){
4058
4058
  $('#monSum').innerHTML =
4059
4059
  kpi('账号可用', `${kReady}/${kTotal}`, kReady ? 'ok' : 'err')
4060
4060
  + kpi('并行任务', `${run.length}/${d.workers || 1}`, run.length ? 'warn' : '',
4061
- '并行上限可调:改下面那条「并行上限」的 − / + 或直接填数字,最多不超过可用账号数')
4061
+ '并行上限可调:改下面那条「并行上限」的 − / + 或直接填数字,最多不超过有效账号数')
4062
4062
  + kpi('排队', d.queued || 0, d.queued ? 'warn' : '')
4063
4063
  + kpi('今日成功', hist.today_done || 0, 'ok')
4064
4064
  + kpi('今日失败', hist.today_error || 0, hist.today_error ? 'err' : '')
@@ -4597,12 +4597,16 @@ async function openDrafts(){
4597
4597
  d.items.forEach(it=>{
4598
4598
  const row = document.createElement('div');
4599
4599
  row.className = 'draft-row';
4600
- /* 小字只留「N 条分镜 · 标题开头」,超出按码点截断加省略号;
4601
- 不再把 titles 又拼一遍(封面标题常和文章标题重复,会把小字拖得极长) */
4602
- const brief = safeText(it.title) || safeText((it.titles || []).find(Boolean) || '');
4600
+ /* 名字优先用标题:标题才是这篇东西叫什么,选题只是「想写什么」的一句话,
4601
+ 以前选题优先,标题写好了却看到草稿叫「33333」 */
4602
+ const draftLabel = () => safeText(it.name || it.title || it.topic || '(无标题)');
4603
+ /* 小字只留「N 条分镜 · 跟主名不同的那条信息」:主名已经是标题了,
4604
+ 再拼一遍标题等于占地方;选题也跟主名一样就跳过,试下第一条分镜标题 */
4605
+ const brief = [safeText(it.topic),
4606
+ safeText((it.titles || []).find(Boolean) || '')]
4607
+ .find(t => t && t !== draftLabel()) || '';
4603
4608
  const sub = clipText([it.count ? it.count + ' 条分镜' : '无分镜',
4604
4609
  brief ? clipText(brief, 12) : ''].filter(Boolean).join(' · '), 30);
4605
- const draftLabel = () => safeText(it.name || it.topic || it.title || '(无选题)');
4606
4610
  row.innerHTML = `<span class="dc"><b data-role="label">${esc(draftLabel())}</b>`
4607
4611
  + `<input class="rn-input" data-role="rn" type="text" maxlength="60" placeholder="输入草稿名称,留空恢复默认" style="display:none">`
4608
4612
  + `<span class="ds">${esc(fmtTime(it.updated))} · ${esc(sub)}</span></span>`
@@ -4615,8 +4619,8 @@ async function openDrafts(){
4615
4619
  const labelEl = row.querySelector('[data-role="label"]');
4616
4620
  const rnEl = row.querySelector('[data-role="rn"]');
4617
4621
  /* 回显「当前这个名字」:自定义名优先,没有自定义名就回显它现在显示的默认名
4618
- (选题 / 标题),用户在现有名字上改,而不是对着一个空框重想 */
4619
- const defaultName = () => safeText(it.topic || it.title || '');
4622
+ (标题 / 选题),用户在现有名字上改,而不是对着一个空框重想 */
4623
+ const defaultName = () => safeText(it.title || it.topic || '');
4620
4624
  const currentName = () => safeText(it.name || '') || defaultName();
4621
4625
  rnEl.value = currentName();
4622
4626
  row.querySelector('[data-act="rename"]').onclick = ()=>{
@@ -4634,7 +4638,7 @@ async function openDrafts(){
4634
4638
  labelEl.style.display = 'block';
4635
4639
  if (!save) { rnEl.value = currentName(); return; }
4636
4640
  const val = Array.from(safeText(rnEl.value.trim())).slice(0, 60).join('');
4637
- /* 改完正好等于默认名(选题 / 标题)就存空串,让名字继续跟着选题走,
4641
+ /* 改完正好等于默认名(标题 / 选题)就存空串,让名字继续跟着标题走,
4638
4642
  免得把它写死成一份静态名称 */
4639
4643
  const nextName = val === defaultName() ? '' : val;
4640
4644
  if (nextName === (it.name || '')) { rnEl.value = currentName(); return; }