@musnows/scriverse 0.4.6 → 0.4.7

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.
@@ -2764,7 +2764,13 @@ function renderTree() {
2764
2764
  button.addEventListener("click", () => openChapterDialog(button.dataset.newChapterVolume));
2765
2765
  });
2766
2766
  $("#novel-tree").querySelectorAll("[data-chapter-id]").forEach((button) => {
2767
- button.addEventListener("click", () => selectChapter(button.dataset.chapterId));
2767
+ button.addEventListener("click", () => {
2768
+ selectChapter(button.dataset.chapterId);
2769
+ if (isMobileViewport()) {
2770
+ panelLayout.leftCollapsed = true;
2771
+ applyPanelLayout(true);
2772
+ }
2773
+ });
2768
2774
  button.addEventListener("contextmenu", (event) => {
2769
2775
  if (!canEditProse()) return;
2770
2776
  event.preventDefault();
@@ -3273,13 +3279,16 @@ async function renderCharacters(page = characterListPage) {
3273
3279
  state.characters = characterPage.items;
3274
3280
  const layout = readModuleLayout();
3275
3281
  const characterActions = (item) => recordCardEditButton("edit-character", item.id, `角色“${item.name}”`);
3282
+ const characterLockBadge = (item) => item.lockedFields.length
3283
+ ? `<span class="character-lock-badge" aria-label="${item.lockedFields.length} 个锁定字段" title="锁定字段:${esc(item.lockedFields.join("、"))}"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="5" y="10" width="14" height="10" rx="2"></rect><path d="M8 10V7a4 4 0 0 1 8 0v3"></path></svg><span>${item.lockedFields.length}</span></span>`
3284
+ : "";
3276
3285
  const characterCards = () => `<div class="card-grid">${state.characters.map((item) => {
3277
3286
  const details = normalizeCharacterDetails(item.attributes?.details);
3278
3287
  return `
3279
- <article class="record-card character-card preview-record-card has-card-edit" data-open-character="${esc(item.id)}" role="button" tabindex="0" aria-label="查看角色 ${esc(item.name)}">${recordCardEditButton("edit-character", item.id, `角色“${item.name}”`)}${item.lockedFields.length ? `<small>锁定 ${item.lockedFields.length} 项</small>` : ""}
3280
- <h3>${esc(item.name)}</h3>
3288
+ <article class="record-card character-card preview-record-card has-card-edit" data-open-character="${esc(item.id)}" role="button" tabindex="0" aria-label="查看角色 ${esc(item.name)}">${recordCardEditButton("edit-character", item.id, `角色“${item.name}”`)}
3289
+ <div class="character-card-heading"><h3>${esc(item.name)}</h3>${characterLockBadge(item)}</div>
3281
3290
  ${item.attributes?.identity ? `<p class="character-identity">${esc(item.attributes.identity)}</p>` : ""}
3282
- ${item.aliases.length ? `<div class="character-aliases">${item.aliases.map((alias) => `<span class="pill">${esc(alias)}</span>`).join("")}</div>` : ""}
3291
+ ${item.aliases.length ? `<div class="character-aliases"><b>别名</b>${item.aliases.map((alias) => `<span class="pill">${esc(alias)}</span>`).join("")}</div>` : ""}
3283
3292
  ${item.code ? `<div class="character-code"><b>编号</b><span class="pill">${esc(item.code)}</span></div>` : ""}
3284
3293
  ${item.species ? `<div class="character-species"><b>种族</b><span class="pill">${esc(racePathLabel(item.race) || item.species)}</span></div>` : ""}
3285
3294
  ${details.length ? `<dl class="character-detail-list">${details.slice(0, 4).map((detail) => `<div><dt>${esc(detail.label)}</dt><dd>${esc(detail.value)}</dd></div>`).join("")}</dl>` : ""}
@@ -3298,9 +3307,8 @@ async function renderCharacters(page = characterListPage) {
3298
3307
  ].filter(Boolean).join(" · ");
3299
3308
  const line = meta ? `${meta} · ${preview}` : preview;
3300
3309
  return `
3301
- <article class="record-card module-row character-card preview-record-card" data-open-character="${esc(item.id)}" role="button" tabindex="0" aria-label="查看角色 ${esc(item.name)}">
3302
- ${item.lockedFields.length ? `<small>锁定 ${item.lockedFields.length} 项</small>` : ""}
3303
- <h3>${esc(item.name)}</h3>
3310
+ <article class="record-card module-row character-row character-card preview-record-card" data-open-character="${esc(item.id)}" role="button" tabindex="0" aria-label="查看角色 ${esc(item.name)}">
3311
+ <div class="character-card-heading"><h3>${esc(item.name)}</h3>${characterLockBadge(item)}</div>
3304
3312
  <p class="module-row-preview" title="${esc(line)}">${esc(line)}</p>
3305
3313
  <div class="card-actions">${characterActions(item)}</div>
3306
3314
  </article>`;
@@ -5714,16 +5722,28 @@ function openTaskDialog() {
5714
5722
  const chapterOptions = state.work.volumes.flatMap((volume) => volume.chapters.map((chapter) => [chapter.id, `${volume.title} / ${chapter.title}`]));
5715
5723
  const defaultTaskType = ANALYSIS_TYPES[0].value;
5716
5724
  const taskTypeField = `<div class="form-field analysis-type-field"><label>分析类型<select name="taskType" aria-describedby="analysis-type-description">${ANALYSIS_TYPES.map(({ value, label }) => `<option value="${esc(value)}" ${value === defaultTaskType ? "selected" : ""}>${esc(label)}</option>`).join("")}</select></label><p id="analysis-type-description" class="analysis-type-description" aria-live="polite">${esc(analysisTypeDescription(defaultTaskType))}</p></div>`;
5717
- openDialog("开始 AI 分析", taskTypeField + field("scopeType", "分析范围", "select", "chapter", [["chapter", "指定章节"], ["book", "全书"]]) + field("chapterId", "章节", "select", chapterOptions[0]?.[0] ?? "", chapterOptions), async (form) => {
5725
+ const chapterField = `<label class="task-chapter-field">章节<select name="chapterId">${chapterOptions.map(([key, text], index) => `<option value="${esc(key)}" ${index === 0 ? "selected" : ""}>${esc(text)}</option>`).join("")}</select></label>`;
5726
+ openDialog("开始 AI 分析", taskTypeField + field("scopeType", "分析范围", "select", "chapter", [["chapter", "指定章节"], ["book", "全书"]]) + chapterField, async (form) => {
5718
5727
  const scope = form.get("taskType") === "character-identity-audit" || form.get("scopeType") === "book" ? { type: "book" } : { type: "chapter", chapterId: form.get("chapterId") };
5719
5728
  await api(`/api/works/${state.work.id}/tasks`, { method: "POST", body: { taskType: form.get("taskType"), scope } });
5720
5729
  await renderTasks();
5721
5730
  });
5722
5731
  const taskTypeSelect = $("#dialog-fields").querySelector('select[name="taskType"]');
5732
+ const scopeTypeSelect = $("#dialog-fields").querySelector('select[name="scopeType"]');
5733
+ const chapterSelect = $("#dialog-fields").querySelector('select[name="chapterId"]');
5734
+ const chapterFieldElement = chapterSelect.closest(".task-chapter-field");
5723
5735
  const description = $("#analysis-type-description");
5736
+ const syncChapterField = () => {
5737
+ const disabled = scopeTypeSelect.value === "book";
5738
+ chapterSelect.disabled = disabled;
5739
+ chapterFieldElement.classList.toggle("is-disabled", disabled);
5740
+ chapterFieldElement.setAttribute("aria-disabled", String(disabled));
5741
+ };
5724
5742
  taskTypeSelect.addEventListener("change", () => {
5725
5743
  description.textContent = analysisTypeDescription(taskTypeSelect.value);
5726
5744
  });
5745
+ scopeTypeSelect.addEventListener("change", syncChapterField);
5746
+ syncChapterField();
5727
5747
  }
5728
5748
 
5729
5749
  function openProviderDialog(item) {
@@ -6615,7 +6635,7 @@ $("#module-nav").addEventListener("click", (event) => {
6615
6635
  }
6616
6636
  if (button.dataset.module) {
6617
6637
  void showModule(button.dataset.module).finally(() => {
6618
- if (window.matchMedia("(max-width: 850px)").matches) {
6638
+ if (isMobileViewport()) {
6619
6639
  panelLayout.leftCollapsed = true;
6620
6640
  applyPanelLayout(true);
6621
6641
  }
@@ -10,7 +10,7 @@
10
10
  <link rel="icon" href="/icon.svg?v=20260712" type="image/svg+xml">
11
11
  <link rel="manifest" href="/site.webmanifest">
12
12
  <link rel="stylesheet" href="/vendor/vditor/dist/index.css?v=3.11.2">
13
- <link rel="stylesheet" href="/styles.css?v=20260725-develop-galaxy-ripple">
13
+ <link rel="stylesheet" href="/styles.css?v=20260725-mobile-workspace-unify">
14
14
  </head>
15
15
  <body class="auth-pending">
16
16
  <section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
@@ -25,7 +25,7 @@
25
25
  </div>
26
26
  <form id="login-form" class="auth-form">
27
27
  <label>用户名<input name="username" autocomplete="username" maxlength="100" required></label>
28
- <label>密码<span class="password-field"><input id="login-password" name="password" type="password" autocomplete="current-password" maxlength="200" required aria-describedby="login-lock-hint"><button class="password-toggle" type="button" data-password-toggle="login-password" aria-label="显示密码" aria-pressed="false" title="显示密码"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M2.5 12s3.3-5.5 9.5-5.5S21.5 12 21.5 12 18.2 17.5 12 17.5 2.5 12 2.5 12Z"/><circle cx="12" cy="12" r="2.8"/></svg></button></span></label>
28
+ <label>密码<input id="login-password" name="password" type="password" autocomplete="current-password" maxlength="200" required aria-describedby="login-lock-hint"></label>
29
29
  <p id="login-lock-hint" class="auth-security-hint">5 分钟内连续输错 5 次密码,登录将锁定 30 分钟。</p>
30
30
  <div class="auth-captcha">
31
31
  <label>验证码<input name="captchaAnswer" autocomplete="off" inputmode="text" maxlength="8" spellcheck="false" required aria-describedby="login-captcha-hint"></label>
@@ -699,6 +699,6 @@
699
699
  <div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
700
700
  <script id="vditorIconScript" src="/vendor/vditor/dist/js/icons/ant.js?v=3.11.2"></script>
701
701
  <script src="/vendor/vditor/dist/index.min.js?v=3.11.2"></script>
702
- <script type="module" src="/app.js?v=20260725-develop-galaxy-ripple"></script>
702
+ <script type="module" src="/app.js?v=20260725-mobile-workspace-unify"></script>
703
703
  </body>
704
704
  </html>
@@ -503,10 +503,11 @@ body.is-panel-resizing { cursor: col-resize; user-select: none; }
503
503
  .app-shell.left-panel-collapsed .panel-heading { display: none; }
504
504
  .app-shell.left-panel-collapsed .brand-block { padding: 0 4px; }.app-shell.left-panel-collapsed .brand-block > div { display: none; }
505
505
  .main-panel { min-width: 0; min-height: 0; overflow: hidden; }
506
- .app-shell.shelf-mode { grid-template-columns: 1fr; }
506
+ .app-shell.shelf-mode { grid-template-columns: minmax(0, 1fr); }
507
507
  .app-shell.shelf-mode .topbar { grid-template-columns: 280px 1fr auto; }
508
508
  .app-shell.shelf-mode .left-panel, .app-shell.shelf-mode .ai-panel { display: none; }
509
509
  .app-shell.shelf-mode .save-state { display: none; }
510
+ .app-shell.shelf-mode #top-search-button { display: none; }
510
511
  .app-shell.shelf-mode .main-panel { grid-column: 1 / -1; }
511
512
 
512
513
  .shelf-view { display: flex; flex-direction: column; height: 100%; overflow-y: auto; padding: 48px clamp(32px, 7vw, 110px) 90px; }
@@ -877,7 +878,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
877
878
  .chapter-add-button { flex: none; }
878
879
  .volume-chapters { display: block; }
879
880
  .volume-node.is-collapsed .volume-chapters { display: none; }
880
- .chapter-node { display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: 8px; width: calc(100% + 6px); padding: 9px 8px 9px 20px; border: 0; background: transparent; border-radius: 4px; text-align: left; font-size: 12px; }
881
+ .chapter-node { display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: 8px; width: calc(100% + 6px); padding: 9px 8px 9px 20px; border: 0; background: transparent; border-radius: 4px; text-align: left; font-size: 12px; }
881
882
  .chapter-node:hover, .chapter-node.active { background: var(--row-active); }
882
883
  .chapter-node.active { color: var(--accent-dark); font-weight: 700; }
883
884
  .chapter-node > span:first-child { min-width: 0; overflow-wrap: anywhere; line-height: 1.45; white-space: normal; }
@@ -982,6 +983,13 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
982
983
  .setting-row .card-actions, .module-row .card-actions { margin-top: 0; flex-wrap: wrap; justify-content: flex-end; }
983
984
  .module-row .card-actions > .record-card-edit { position: static; }
984
985
  .character-card { cursor: pointer; }
986
+ .character-row { grid-template-columns: minmax(140px, .28fr) minmax(0, 1fr) auto; }
987
+ .character-card-heading { display: flex; min-width: 0; align-items: center; gap: 7px; }
988
+ .character-card-heading h3 { min-width: 0; }
989
+ .record-card.has-card-edit .character-card-heading { padding-right: 34px; }
990
+ .record-card.has-card-edit .character-card-heading h3 { padding-right: 0; }
991
+ .character-lock-badge { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 3px; min-height: 20px; padding: 2px 6px; border: 1px solid color-mix(in srgb, var(--accent) 42%, var(--line)); border-radius: 10px; background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--accent-dark); font-size: 9px; font-variant-numeric: tabular-nums; line-height: 1; }
992
+ .character-lock-badge svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.8; }
985
993
  .character-filter-toolbar { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)) auto; gap: 12px; align-items: start; width: 100%; min-width: 0; box-sizing: border-box; margin-bottom: 18px; padding: 12px 14px; border: 1px solid var(--line); background: var(--surface-soft); }
986
994
  .character-filter-dropdown { position: relative; min-width: 0; }
987
995
  .character-filter-dropdown summary { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 38px; padding: 0 11px; border: 1px solid var(--line); border-radius: 4px; background: var(--surface); color: var(--ink); cursor: pointer; list-style: none; }
@@ -1633,6 +1641,9 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
1633
1641
  .merge-dialog-note { margin: 0; padding: 10px 12px; border-left: 2px solid var(--accent); background: var(--surface-soft); color: var(--muted); font-size: 11px; line-height: 1.6; }
1634
1642
  .form-field { display: grid; gap: 6px; color: var(--muted); font-size: 11px; }
1635
1643
  .analysis-type-description { margin: 0; color: var(--muted); font-size: 10px; line-height: 1.55; }
1644
+ .task-chapter-field { transition: opacity .15s ease; }
1645
+ .task-chapter-field.is-disabled { opacity: .48; }
1646
+ .task-chapter-field select:disabled { cursor: not-allowed; }
1636
1647
  .model-temperature-hint { margin: 0; color: var(--accent-dark); font-size: 10px; line-height: 1.55; }
1637
1648
  .item-list-rows { display: grid; gap: 7px; }
1638
1649
  .item-list-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 7px; }
@@ -1658,6 +1669,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
1658
1669
  .character-card h3 { margin-bottom: 5px; }
1659
1670
  .character-identity { margin: 0 0 6px !important; color: var(--ink) !important; font-weight: 600; line-height: 1.55 !important; }
1660
1671
  .character-aliases { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-width: 0; }
1672
+ .character-aliases b { margin-right: 2px; color: var(--muted); font-size: 9px; font-weight: 500; }
1661
1673
  .character-aliases .pill { margin: 0; border: 1px solid var(--line); background: var(--surface); }
1662
1674
  .character-code { display: flex; align-items: center; gap: 7px; margin-top: 10px; color: var(--muted); font-size: 10px; }
1663
1675
  .character-species { display: grid; grid-template-columns: max-content minmax(0, 1fr); align-items: center; gap: 6px; margin-top: 8px; color: var(--muted); font-size: 10px; }
@@ -2013,8 +2025,13 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
2013
2025
  }
2014
2026
 
2015
2027
  @media (max-width: 640px) {
2016
- .setting-editor-heading { gap: 6px; }
2017
- .setting-editor-header-fields { align-items: flex-start; flex-direction: column; }
2028
+ #onboarding-dialog, #onboarding-menu-button { display: none !important; }
2029
+ .topbar .work-meta { display: none; }
2030
+ .top-actions .save-state { display: none; }
2031
+ .presence-button #presence-count { display: none; }
2032
+ .account-button > span:last-child { display: none; }
2033
+ .book-shelf { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px 14px; }
2034
+ .book-info > span, .book-access-badge { display: none; }
2018
2035
  .ai-history-dialog-body { min-height: min(360px, 62vh); padding: 12px 14px 18px; }
2019
2036
  .account-settings-body { padding: 16px; }
2020
2037
  .account-settings-section { padding: 15px; }
@@ -2060,6 +2077,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
2060
2077
  .brand-block strong { font-size: 14px; }
2061
2078
  .brand-block small { display: none; }
2062
2079
  .mobile-module-tab { position: fixed; z-index: 32; top: 132px; right: 0; display: grid; width: 34px; height: 42px; padding: 0; place-items: center; border: 1px solid var(--line); border-right: 0; border-radius: 5px 0 0 5px; background: var(--surface-soft); color: var(--accent-dark); font-size: 10px; writing-mode: vertical-rl; }
2080
+ .app-shell.shelf-mode .mobile-module-tab { display: none; }
2063
2081
  .mobile-panel-backdrop { position: fixed; z-index: 30; inset: 0; width: 100%; height: 100%; padding: 0; border: 0; background: rgba(20, 17, 14, .28); }
2064
2082
  .app-shell:not(.shelf-mode):not(.left-panel-collapsed) .mobile-panel-backdrop { display: block; }
2065
2083
  .work-meta {
@@ -2243,7 +2261,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
2243
2261
  .editor-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
2244
2262
  .editor-actions .chapter-stats { display: none; }
2245
2263
  .editor-actions > button { width: auto; }
2246
- .book-shelf { grid-template-columns: minmax(0, 1fr); max-width: 280px; }
2264
+ .book-shelf { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 12px; max-width: none; }
2247
2265
  .book-add-card { min-height: 220px; }
2248
2266
  .onboarding-popover { width: calc(100vw - 20px); }
2249
2267
  }
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const APP_VERSION = "0.4.5";
1
+ export const APP_VERSION = "0.4.7";
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@musnows/scriverse",
3
- "version": "0.4.6",
3
+ "version": "0.4.7",
4
4
  "description": "Command-line client and local server for the Scriverse long-form fiction workspace",
5
5
  "license": "MIT",
6
6
  "author": "musnows",