@musnows/scriverse 0.7.0 → 0.7.2

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.
@@ -1915,12 +1915,24 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
1915
1915
  .timeline-track-tab[data-track-color-index="7"],
1916
1916
  .timeline-track-panel[data-track-color-index="7"] { --timeline-track-color: var(--timeline-track-palette-7); }
1917
1917
  .relationship-map-card { margin-bottom: 28px; overflow: hidden; border: 1px solid var(--line); border-radius: 6px; background: var(--surface-soft); }
1918
- .relationship-map-toolbar { display: flex; align-items: center; gap: 18px; padding: 13px 16px; border-bottom: 1px solid var(--line); }
1919
- .relationship-map-toolbar > div:first-child { display: grid; gap: 3px; }
1918
+ .relationship-map-toolbar { position: relative; z-index: 10; display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; padding: 13px 16px; overflow: visible; border-bottom: 1px solid var(--line); }
1919
+ .relationship-map-toolbar > div:first-child { display: grid; flex: 0 0 auto; gap: 3px; }
1920
1920
  .relationship-map-toolbar strong { font-size: 13px; }
1921
1921
  .relationship-map-toolbar small { color: var(--muted); font-size: 9px; }
1922
- .relationship-map-toolbar .relationship-map-actions { display: flex; gap: 7px; margin-left: auto; }
1922
+ .relationship-map-toolbar .relationship-map-actions { display: flex; gap: 7px; margin-left: 0; }
1923
1923
  .relationship-map-actions button { white-space: nowrap; }
1924
+ .relationship-node-search { position: relative; display: flex; flex: 0 1 240px; align-items: center; width: min(240px, 28vw); min-width: 180px; margin-left: auto; color: var(--relationship-toolbar-muted); }
1925
+ .relationship-node-search input { width: 100%; min-width: 0; min-height: 34px; padding: 7px 30px 7px 31px; border: 1px solid var(--relationship-control-border); border-radius: 4px; background: var(--relationship-card-surface); color: var(--relationship-toolbar-ink); font-family: inherit; font-size: 11px; line-height: 1.35; }
1926
+ .relationship-node-search input::placeholder { color: var(--relationship-toolbar-muted); }
1927
+ .relationship-node-search input:focus { border-color: var(--relationship-control-hover-border); outline: 2px solid color-mix(in srgb, var(--relationship-focus-strong) 22%, transparent); outline-offset: 0; }
1928
+ .relationship-node-search-icon { position: absolute; z-index: 1; left: 10px; width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-width: 1.8; pointer-events: none; }
1929
+ .relationship-node-search:focus-within .relationship-node-search-icon { color: var(--relationship-focus-strong); }
1930
+ .relationship-node-search-results { position: absolute; z-index: 30; top: calc(100% + 6px); right: 0; left: 0; display: grid; max-height: min(280px, 44vh); padding: 5px; overflow-y: auto; border: 1px solid var(--relationship-control-border); border-radius: 5px; background: var(--relationship-detail-surface); box-shadow: 0 12px 28px rgba(0,0,0,.2); }
1931
+ .relationship-node-search-results button { display: grid; width: 100%; min-height: 0; gap: 3px; padding: 8px 9px; border: 0; border-radius: 3px; background: transparent; color: var(--relationship-detail-ink); text-align: left; }
1932
+ .relationship-node-search-results button:hover, .relationship-node-search-results button:focus-visible, .relationship-node-search-results button.is-active { background: var(--relationship-control-hover-surface); color: var(--relationship-control-hover-ink); outline: none; }
1933
+ .relationship-node-search-results button strong { overflow: hidden; font-size: 11px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
1934
+ .relationship-node-search-results button small { overflow: hidden; color: var(--relationship-detail-muted); font-size: 9px; font-weight: 400; text-overflow: ellipsis; white-space: nowrap; }
1935
+ .relationship-node-search-empty { margin: 0; padding: 10px 9px; color: var(--relationship-detail-muted); font-size: 10px; text-align: center; }
1924
1936
  .relationship-galaxy-button { display: inline-flex; align-items: center; gap: 6px; }
1925
1937
  .relationship-galaxy-icon { width: 14px; height: 14px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 1.55; stroke-linecap: round; stroke-linejoin: round; }
1926
1938
  .relationship-map-legend { display: flex; flex-wrap: wrap; gap: 10px; }
@@ -1953,11 +1965,11 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
1953
1965
  .relationship-empty-note { margin-top: -12px; padding: 12px 14px; border: 1px dashed var(--line); color: var(--muted); font-size: 11px; }
1954
1966
  .relationship-map-dialog { position: relative; width: min(1400px, 96vw); max-width: none; height: min(900px, 92vh); max-height: none; padding: 0; border: 1px solid var(--line); border-radius: 7px; background: var(--panel); box-shadow: var(--shadow); }
1955
1967
  .relationship-map-dialog::backdrop { background: rgba(34,30,25,.52); backdrop-filter: blur(3px); }
1956
- .relationship-map-floating-close { position: absolute; z-index: 6; top: 25px; right: 25px; width: 34px; height: 34px; border: 1px solid var(--line); border-radius: 50%; background: var(--surface-strong); color: var(--ink); font-size: 20px; }
1968
+ .relationship-map-floating-close { position: absolute; z-index: 12; top: 25px; right: 25px; width: 34px; height: 34px; border: 1px solid var(--line); border-radius: 50%; background: var(--surface-strong); color: var(--ink); font-size: 20px; }
1957
1969
  .relationship-map-expanded-host { height: 100%; padding: 18px; }
1958
- .relationship-map-expanded-host .relationship-map-card { height: 100%; margin: 0; }
1970
+ .relationship-map-expanded-host .relationship-map-card { display: flex; height: 100%; margin: 0; flex-direction: column; }
1959
1971
  .relationship-map-expanded-host .relationship-map-toolbar { padding-right: 72px; }
1960
- .relationship-map-expanded-host .relationship-mindmap { height: calc(100% - 67px); min-height: 0; }
1972
+ .relationship-map-expanded-host .relationship-mindmap { flex: 1 1 auto; height: auto; min-height: 0; }
1961
1973
  .relationship-network-card { border-color: var(--relationship-card-border); background: var(--relationship-card-surface); box-shadow: none; }
1962
1974
  .relationship-network-card .relationship-map-toolbar { min-height: 58px; border-color: var(--relationship-card-border); background: var(--relationship-toolbar-surface); color: var(--relationship-toolbar-ink); }
1963
1975
  .relationship-network-card .relationship-map-toolbar small { color: var(--relationship-toolbar-muted); }
@@ -2116,11 +2128,22 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
2116
2128
  .relationship-network-help { left: 16px; bottom: 13px; font-size: 9px; }
2117
2129
  .relationship-network .mind-edge-detail { left: 50%; right: auto; bottom: 38px; width: min(680px, calc(100% - 40px)); transform: translateX(-50%); border-color: var(--relationship-detail-border); background: var(--relationship-detail-surface); color: var(--relationship-detail-ink); box-shadow: none; }
2118
2130
  .relationship-network .mind-edge-detail span { color: var(--relationship-detail-muted); }
2119
- .relationship-map-expanded-host .relationship-network { height: calc(100% - 63px); min-height: 0; }
2131
+ .relationship-map-expanded-host .relationship-network { height: auto; min-height: 0; }
2120
2132
  .relationship-map-dialog { background: var(--relationship-card-surface); border-color: var(--relationship-card-border); }
2121
2133
  .relationship-map-expanded-host { background: var(--relationship-card-surface); }
2122
2134
  @keyframes relationship-node-settle { from { transform: translate(-50%, -50%) scale(1.36); } to { transform: translate(-50%, -50%) scale(1.32); } }
2123
2135
  @media (prefers-reduced-motion: reduce) { .relationship-network .mind-node, .relationship-network-stage { transition-duration: .01ms; animation-duration: .01ms; } }
2136
+ @media (max-width: 700px) {
2137
+ .relationship-map-toolbar { align-items: stretch; gap: 10px; padding: 12px; }
2138
+ .relationship-map-toolbar > div:first-child { flex: 1 1 auto; justify-content: center; }
2139
+ .relationship-node-search:not(.is-galaxy) { order: 2; flex: 1 0 100%; width: 100%; max-width: none; min-width: 0; margin-left: 0; }
2140
+ .relationship-map-toolbar .relationship-map-actions { order: 3; width: 100%; overflow-x: auto; }
2141
+ .relationship-map-actions button { flex: 1 0 auto; }
2142
+ .relationship-network { min-height: 460px; }
2143
+ .relationship-map-expanded-host { padding: 8px; }
2144
+ .relationship-map-expanded-host .relationship-map-toolbar { padding-right: 58px; }
2145
+ .relationship-map-floating-close { top: 16px; right: 16px; width: 36px; height: 36px; }
2146
+ }
2124
2147
 
2125
2148
  .outline-summary { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-bottom: 32px; }
2126
2149
  .outline-summary article { display: grid; padding: 17px; border: 1px solid var(--line); background: var(--surface-soft); }
@@ -2169,8 +2192,9 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
2169
2192
  .galaxy-dialog::backdrop { background: #05070d; }.galaxy-shell { position: relative; width: 100%; height: 100%; overflow: hidden; touch-action: none; background: #05070d; cursor: grab; contain: strict; }
2170
2193
  .galaxy-shell.is-rotating-camera { cursor: grabbing; }.galaxy-background, .galaxy-graph, .galaxy-node-layer { position: absolute; inset: 0; width: 100%; height: 100%; }.galaxy-background { opacity: 1; transition: opacity .2s ease; }.galaxy-background.hidden-stars { opacity: .96; }
2171
2194
  .galaxy-graph { z-index: 2; }.galaxy-node-layer { z-index: 3; pointer-events: none; }
2172
- .galaxy-node { --node-size: 12px; position: absolute; left: 0; top: 0; display: grid; grid-template-rows: var(--node-size) auto; justify-items: center; align-items: start; gap: 5px; padding: 8px; border: 0; background: transparent; color: #edf7ff; cursor: grab; touch-action: none; pointer-events: auto; transform-origin: center; opacity: var(--depth-opacity, 1); will-change: transform, opacity; }
2173
- .galaxy-node i { position: relative; isolation: isolate; width: var(--node-size); height: var(--node-size); border-radius: 50%; background: radial-gradient(circle at 32% 27%, var(--node-core, #fff) 0 7%, color-mix(in srgb, var(--node-color) 62%, #fff) 18%, var(--node-color) 52%, var(--node-rim, #07101f) 100%); box-shadow: inset -3px -4px 7px color-mix(in srgb, var(--node-rim, #07101f) 74%, transparent), 0 0 4px rgba(255,255,255,.82), 0 0 calc(7px + var(--node-glow) * 11px) var(--node-color), 0 0 calc(15px + var(--node-glow) * 25px) var(--node-atmosphere, color-mix(in srgb, var(--node-color) 72%, transparent)); filter: brightness(var(--node-brightness)); transition: filter .18s ease, box-shadow .18s ease; }
2195
+ .galaxy-node { --node-size: 12px; position: absolute; left: 0; top: 0; display: grid; grid-template-rows: var(--node-size) auto; justify-items: center; align-items: start; gap: 5px; padding: 8px; border: 0; background: transparent; color: #edf7ff; cursor: grab; touch-action: none; pointer-events: auto; transform-origin: center; opacity: var(--depth-opacity, 1); }
2196
+ .galaxy-node.is-dragging, .galaxy-node.is-selected, .galaxy-node.is-edge-endpoint { will-change: transform, opacity; }
2197
+ .galaxy-node i { position: relative; isolation: isolate; width: var(--node-size); height: var(--node-size); border-radius: 50%; background: radial-gradient(circle at 32% 27%, var(--node-core, #fff) 0 7%, color-mix(in srgb, var(--node-color) 62%, #fff) 18%, var(--node-color) 52%, var(--node-rim, #07101f) 100%); box-shadow: inset -3px -4px 7px color-mix(in srgb, var(--node-rim, #07101f) 74%, transparent), 0 0 4px rgba(255,255,255,.82), 0 0 calc(7px + var(--node-glow) * 11px) var(--node-color), 0 0 calc(15px + var(--node-glow) * 25px) var(--node-atmosphere, color-mix(in srgb, var(--node-color) 72%, transparent)); filter: brightness(var(--node-brightness)); transform: scale(var(--node-marker-scale, 1)); transform-origin: center; transition: filter .18s ease, box-shadow .18s ease; }
2174
2198
  .galaxy-node i::before, .galaxy-node i::after { content: ""; position: absolute; pointer-events: none; }
2175
2199
  .galaxy-node i::before { z-index: -1; inset: -34%; border-radius: 50%; background: radial-gradient(circle, var(--node-atmosphere) 0, transparent 68%); opacity: .64; }
2176
2200
  .galaxy-node[data-celestial-type="star"] i { background: radial-gradient(circle at 38% 34%, #fff 0 10%, var(--node-core) 22%, var(--node-color) 53%, var(--node-rim) 100%); box-shadow: 0 0 5px #fff, 0 0 calc(10px + var(--node-glow) * 15px) var(--node-color), 0 0 calc(24px + var(--node-glow) * 34px) var(--node-atmosphere); }
@@ -2182,15 +2206,30 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
2182
2206
  .galaxy-node[data-celestial-type="volcanic"] i { background: radial-gradient(circle at 66% 61%, #ffd37a 0 3%, #ff7438 5%, transparent 9%), radial-gradient(circle at 37% 72%, #ff9b48 0 3%, transparent 8%), radial-gradient(circle at 30% 25%, var(--node-core) 0 5%, var(--node-color) 38%, var(--node-rim) 100%); }
2183
2207
  .galaxy-node[data-celestial-type="rocky"] i { background: radial-gradient(circle at 67% 31%, color-mix(in srgb, var(--node-rim) 74%, transparent) 0 8%, transparent 10%), radial-gradient(circle at 34% 68%, color-mix(in srgb, var(--node-rim) 58%, transparent) 0 11%, transparent 13%), radial-gradient(circle at 30% 26%, var(--node-core) 0 5%, var(--node-color) 45%, var(--node-rim) 100%); }
2184
2208
  .galaxy-node[data-celestial-type="dwarf"] i { box-shadow: inset -2px -3px 5px var(--node-rim), 0 0 3px rgba(255,255,255,.65), 0 0 calc(5px + var(--node-glow) * 8px) var(--node-atmosphere); }
2185
- .galaxy-node span { opacity: 0; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 3px 6px; border-radius: 4px; background: rgba(5,7,13,.68); font-size: 10px; text-align: center; text-shadow: 0 1px 3px #000; transition: opacity .14s ease; }
2209
+ .galaxy-node span { opacity: 0; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 3px 6px; border-radius: 4px; background: rgba(5,7,13,.68); font-size: 10px; text-align: center; text-shadow: 0 1px 3px #000; transform: translate3d(0, var(--node-label-offset, 0px), 0); transition: opacity .14s ease; }
2186
2210
  .galaxy-node.show-label span, .galaxy-node:hover span, .galaxy-node:focus-visible span, .galaxy-node.is-selected span, .galaxy-node.is-related span, .galaxy-node.is-edge-endpoint span { opacity: 1; }.galaxy-node.is-selected i { background: #fff4bd; box-shadow: 0 0 5px #fff, 0 0 18px #ffc86b, 0 0 42px rgba(255,173,66,.82); }.galaxy-node.is-related i { background: #d8f4ff; box-shadow: 0 0 4px #fff, 0 0 15px #438cff, 0 0 28px rgba(67,140,255,.56); }.galaxy-node.is-edge-endpoint i { background: #fff; box-shadow: 0 0 6px #fff, 0 0 22px #6fcaff, 0 0 48px rgba(67,140,255,.92); }.galaxy-node.is-dimmed { opacity: .16; }.galaxy-node.is-selected, .galaxy-node.is-related, .galaxy-node.is-edge-endpoint { z-index: 2; }
2187
2211
  .galaxy-node.is-dragging { cursor: grabbing; z-index: 6; }
2188
2212
  .galaxy-close { position: fixed; z-index: 8; top: 22px; right: 25px; width: 42px; height: 42px; border: 1px solid rgba(255,255,255,.22); border-radius: 50%; background: rgba(5,7,13,.62); color: #fff; font-size: 25px; }
2189
2213
  .galaxy-stats { position: fixed; z-index: 7; left: 22px; bottom: 20px; color: rgba(235,245,255,.82); font-size: 11px; }
2214
+ .relationship-node-search.is-galaxy { position: fixed; z-index: 9; top: 22px; left: 50%; width: min(320px, calc(100vw - 180px)); max-width: none; min-width: 0; margin: 0; color: rgba(220,235,250,.72); transform: translateX(-50%); }
2215
+ .relationship-node-search.is-galaxy input { min-height: 42px; border-color: rgba(255,255,255,.18); border-radius: 7px; background: rgba(5,7,13,.74); color: #eef6ff; box-shadow: 0 8px 24px rgba(0,0,0,.26); backdrop-filter: blur(14px); font-size: 12px; }
2216
+ .relationship-node-search.is-galaxy input::placeholder { color: rgba(220,235,250,.58); }
2217
+ .relationship-node-search.is-galaxy input:focus { border-color: rgba(132,197,255,.66); outline-color: rgba(97,184,255,.24); }
2218
+ .relationship-node-search.is-galaxy .relationship-node-search-results { border-color: rgba(255,255,255,.16); background: rgba(5,7,13,.9); box-shadow: 0 14px 34px rgba(0,0,0,.42); backdrop-filter: blur(16px); }
2219
+ .relationship-node-search.is-galaxy .relationship-node-search-results button { color: #e8f3ff; }
2220
+ .relationship-node-search.is-galaxy .relationship-node-search-results button:hover, .relationship-node-search.is-galaxy .relationship-node-search-results button:focus-visible, .relationship-node-search.is-galaxy .relationship-node-search-results button.is-active { background: rgba(97,184,255,.14); color: #fff; }
2221
+ .relationship-node-search.is-galaxy .relationship-node-search-results button small, .relationship-node-search.is-galaxy .relationship-node-search-empty { color: rgba(220,235,250,.64); }
2190
2222
  .galaxy-controls { position: fixed; z-index: 7; right: 23px; top: 50%; display: grid; width: 104px; gap: 7px; transform: translateY(-50%); padding: 10px; border: 1px solid rgba(255,255,255,.12); border-radius: 8px; background: rgba(5,7,13,.66); backdrop-filter: blur(12px); }
2191
2223
  .galaxy-controls button { border: 1px solid rgba(255,255,255,.14); border-radius: 5px; padding: 7px; background: rgba(255,255,255,.05); color: #dcecff; font-size: 9px; }
2192
2224
  .galaxy-legend { position: fixed; z-index: 7; right: 22px; bottom: 18px; display: grid; grid-template-columns: 1fr 1fr; gap: 7px 12px; margin: 0; padding: 11px; list-style: none; border: 1px solid rgba(255,255,255,.12); border-radius: 7px; background: rgba(5,7,13,.66); color: rgba(235,245,255,.8); font-size: 9px; }.galaxy-legend li { display: flex; align-items: center; gap: 5px; }
2193
2225
  .galaxy-detail { position: fixed; z-index: 7; left: 22px; top: 22px; width: min(300px, 30vw); max-height: 55vh; overflow: auto; padding: 15px; border: 1px solid rgba(255,255,255,.14); border-radius: 8px; background: rgba(5,7,13,.74); backdrop-filter: blur(14px); }.galaxy-detail strong, .galaxy-detail small { display: block; }.galaxy-detail strong { font-size: 18px; }.galaxy-detail small, .galaxy-detail p { color: rgba(220,235,250,.65); font-size: 9px; }.galaxy-detail ul { display: grid; gap: 7px; padding: 0; list-style: none; font-size: 9px; }.galaxy-detail li { display: flex; align-items: center; gap: 6px; }
2226
+ .galaxy-shell .relationship-node-search, .galaxy-shell .relationship-node-search * { cursor: auto; }
2227
+ .galaxy-shell .relationship-node-search input { cursor: text; }
2228
+ @media (max-width: 700px) {
2229
+ .relationship-node-search.is-galaxy { top: 14px; right: 68px; left: 14px; width: auto; transform: none; }
2230
+ .galaxy-close { top: 14px; right: 14px; }
2231
+ .galaxy-detail { top: 72px; right: 14px; left: 14px; width: auto; max-height: 42vh; }
2232
+ }
2194
2233
  .sr-only { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
2195
2234
 
2196
2235
  .ai-heading { display: flex; align-items: center; gap: 8px; min-width: 0; padding-bottom: 9px; border-bottom: 1px solid var(--line); }
package/dist/store.js CHANGED
@@ -6,7 +6,7 @@ import { AppError, notFound } from "./errors.js";
6
6
  import { accountReference, logger } from "./logger.js";
7
7
  import { paginated, paginationSql } from "./pagination.js";
8
8
  import { currentRequestActor } from "./request-context.js";
9
- import { classifyWorkModulePermissions, emptyWorkModulePermissions, fullWorkModulePermissions, storedWorkModulePermissions } from "./work-permissions.js";
9
+ import { canWriteWorkModule, classifyWorkModulePermissions, emptyWorkModulePermissions, fullWorkModulePermissions, storedWorkModulePermissions } from "./work-permissions.js";
10
10
  import { countWords, documentShortSearchTerms, id, json, normalizeDocumentSearchText, normalizeParagraphSpacing, now, splitDocumentParagraphs } from "./utils.js";
11
11
  import { buildWritingCalendar, writingDateKey } from "./writing-progress-time.js";
12
12
  import { resolveMaxAgentToolCallLimit } from "./ai-tool-results.js";
@@ -37,6 +37,7 @@ export function normalizeWorkAgentTools(value) {
37
37
  }
38
38
  return WORK_AGENT_TOOL_IDS.filter((toolId) => enabled.has(toolId));
39
39
  }
40
+ const galaxyFrameRates = [24, 30, 60, 90, 120, 144, 165, 240];
40
41
  const defaultPlatformPageSizes = {
41
42
  drafts: 30,
42
43
  settings: 30,
@@ -76,6 +77,10 @@ function platformPageSizes(value) {
76
77
  fileVersions: pageSize("fileVersions")
77
78
  };
78
79
  }
80
+ function galaxyFrameRate(value) {
81
+ const candidate = Number(value);
82
+ return galaxyFrameRates.includes(candidate) ? candidate : 30;
83
+ }
79
84
  function settingsFromInput(settingsMarkdown, settings, fallback = []) {
80
85
  if (settingsMarkdown !== undefined)
81
86
  return settingsMarkdown.trim() ? [settingsMarkdown] : [];
@@ -704,6 +709,7 @@ export class Store {
704
709
  return {
705
710
  toastPosition: String(row?.toast_position) === "top-right" ? "top-right" : "bottom-right",
706
711
  pageSizes: platformPageSizes(row?.page_sizes_json),
712
+ galaxyFrameRate: galaxyFrameRate(row?.galaxy_frame_rate),
707
713
  updatedAt: String(row?.updated_at ?? "")
708
714
  };
709
715
  }
@@ -713,13 +719,16 @@ export class Store {
713
719
  const currentPageSizes = platformPageSizes(current.pageSizes);
714
720
  const pageSizes = platformPageSizes(JSON.stringify({ ...currentPageSizes, ...input.pageSizes }));
715
721
  const toastPosition = input.toastPosition ?? (current.toastPosition === "top-right" ? "top-right" : "bottom-right");
722
+ const frameRate = input.galaxyFrameRate ?? galaxyFrameRate(current.galaxyFrameRate);
716
723
  this.db.transaction(() => {
717
- this.db.run(`INSERT INTO platform_ui_settings (id, toast_position, page_sizes_json, updated_at) VALUES (1, ?, ?, ?)
724
+ this.db.run(`INSERT INTO platform_ui_settings (id, toast_position, page_sizes_json, galaxy_frame_rate, updated_at) VALUES (1, ?, ?, ?, ?)
718
725
  ON CONFLICT(id) DO UPDATE SET toast_position = excluded.toast_position,
719
- page_sizes_json = excluded.page_sizes_json, updated_at = excluded.updated_at`, toastPosition, JSON.stringify(pageSizes), timestamp);
726
+ page_sizes_json = excluded.page_sizes_json, galaxy_frame_rate = excluded.galaxy_frame_rate,
727
+ updated_at = excluded.updated_at`, toastPosition, JSON.stringify(pageSizes), frameRate, timestamp);
720
728
  this.audit(PLATFORM_AI_WORK_ID, "platform.ui-settings.updated", "platform-ui-settings", "platform-ui-settings", {
721
729
  toastPosition,
722
- pageSizes
730
+ pageSizes,
731
+ galaxyFrameRate: frameRate
723
732
  });
724
733
  });
725
734
  return this.getPlatformUiSettings();
@@ -1495,12 +1504,26 @@ export class Store {
1495
1504
  if (!["prose", "settings", "prose-and-settings"].includes(input.scope)) {
1496
1505
  throw new AppError(400, "REPLACE_SCOPE_INVALID", "替换范围无效");
1497
1506
  }
1507
+ const work = this.getWork(workId);
1508
+ const permissions = work.modulePermissions;
1509
+ const requestedProse = input.scope === "prose" || input.scope === "prose-and-settings";
1510
+ const requestedSettings = input.scope === "settings" || input.scope === "prose-and-settings";
1511
+ const includeProse = requestedProse && canWriteWorkModule(permissions, "prose");
1512
+ const includeSettings = requestedSettings && canWriteWorkModule(permissions, "settings");
1513
+ const processedModules = [];
1514
+ const skippedModules = [];
1515
+ if (includeProse)
1516
+ processedModules.push("prose");
1517
+ if (includeSettings)
1518
+ processedModules.push("settings");
1519
+ if (requestedProse && !includeProse)
1520
+ skippedModules.push("prose");
1521
+ if (requestedSettings && !includeSettings)
1522
+ skippedModules.push("settings");
1498
1523
  const operationId = id("globalReplace");
1499
1524
  let chapterCount = 0;
1500
1525
  let settingCount = 0;
1501
1526
  let totalMatches = 0;
1502
- const includeProse = input.scope === "prose" || input.scope === "prose-and-settings";
1503
- const includeSettings = input.scope === "settings" || input.scope === "prose-and-settings";
1504
1527
  const replaceLiteral = (value) => {
1505
1528
  let matches = 0;
1506
1529
  let offset = 0;
@@ -1516,7 +1539,6 @@ export class Store {
1516
1539
  matches
1517
1540
  };
1518
1541
  };
1519
- this.getWork(workId);
1520
1542
  this.db.transaction(() => {
1521
1543
  if (includeProse) {
1522
1544
  const chapters = this.db.all("SELECT id, content FROM chapters WHERE work_id = ? AND deleted_at IS NULL ORDER BY sort_order, created_at", workId);
@@ -1550,7 +1572,9 @@ export class Store {
1550
1572
  scope: input.scope,
1551
1573
  chapterCount,
1552
1574
  settingCount,
1553
- totalMatches
1575
+ totalMatches,
1576
+ processedModules,
1577
+ skippedModules
1554
1578
  });
1555
1579
  }
1556
1580
  });
@@ -1560,6 +1584,8 @@ export class Store {
1560
1584
  chapterCount,
1561
1585
  settingCount,
1562
1586
  totalMatches,
1587
+ processedModules,
1588
+ skippedModules,
1563
1589
  work: this.getWorkDirectory(workId)
1564
1590
  };
1565
1591
  }