@musnows/scriverse 0.4.6 → 0.4.9
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/dist/ai.js +33 -15
- package/dist/ai.js.map +1 -1
- package/dist/app.js +5 -2
- package/dist/app.js.map +1 -1
- package/dist/database.js +30 -1
- package/dist/database.js.map +1 -1
- package/dist/pagination.js +3 -2
- package/dist/pagination.js.map +1 -1
- package/dist/public/app.js +474 -43
- package/dist/public/avatar-crop.d.ts +57 -0
- package/dist/public/avatar-crop.js +201 -0
- package/dist/public/index.html +38 -6
- package/dist/public/styles.css +113 -9
- package/dist/store.js +5 -2
- package/dist/store.js.map +1 -1
- package/dist/user-auth.js +12 -2
- package/dist/user-auth.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export type CropRect = {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
size: number;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type DisplayRect = {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
scale: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type CropHandle = "nw" | "ne" | "sw" | "se";
|
|
16
|
+
|
|
17
|
+
export const AVATAR_CROP_OUTPUT_SIZE: number;
|
|
18
|
+
export const AVATAR_CROP_MIN_SIZE: number;
|
|
19
|
+
|
|
20
|
+
export function defaultCropRect(imageWidth: number, imageHeight: number): CropRect;
|
|
21
|
+
export function clampCropRect(
|
|
22
|
+
rect: Partial<CropRect> | null | undefined,
|
|
23
|
+
imageWidth: number,
|
|
24
|
+
imageHeight: number,
|
|
25
|
+
minSize?: number
|
|
26
|
+
): CropRect;
|
|
27
|
+
export function moveCropRect(
|
|
28
|
+
rect: Partial<CropRect> | null | undefined,
|
|
29
|
+
deltaX: number,
|
|
30
|
+
deltaY: number,
|
|
31
|
+
imageWidth: number,
|
|
32
|
+
imageHeight: number
|
|
33
|
+
): CropRect;
|
|
34
|
+
export function resizeCropRect(
|
|
35
|
+
rect: Partial<CropRect> | null | undefined,
|
|
36
|
+
handle: CropHandle | string,
|
|
37
|
+
pointerX: number,
|
|
38
|
+
pointerY: number,
|
|
39
|
+
imageWidth: number,
|
|
40
|
+
imageHeight: number
|
|
41
|
+
): CropRect;
|
|
42
|
+
export function containImageRect(
|
|
43
|
+
imageWidth: number,
|
|
44
|
+
imageHeight: number,
|
|
45
|
+
viewportWidth: number,
|
|
46
|
+
viewportHeight: number
|
|
47
|
+
): DisplayRect;
|
|
48
|
+
export function mapDisplayPointToImage(
|
|
49
|
+
point: { x: number; y: number },
|
|
50
|
+
displayRect: DisplayRect
|
|
51
|
+
): { x: number; y: number };
|
|
52
|
+
export function mapImageRectToDisplay(cropRect: CropRect, displayRect: DisplayRect): {
|
|
53
|
+
x: number;
|
|
54
|
+
y: number;
|
|
55
|
+
size: number;
|
|
56
|
+
};
|
|
57
|
+
export function cropOutputSize(cropSize: number, maxSize?: number): number;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/** 头像裁剪输出边长上限(像素) */
|
|
2
|
+
export const AVATAR_CROP_OUTPUT_SIZE = 512;
|
|
3
|
+
|
|
4
|
+
/** 选区最小边长(原图像素) */
|
|
5
|
+
export const AVATAR_CROP_MIN_SIZE = 32;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {{ x: number, y: number, size: number }} CropRect
|
|
9
|
+
* @typedef {{ x: number, y: number, width: number, height: number, scale: number }} DisplayRect
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
function finitePositive(value) {
|
|
13
|
+
return Number.isFinite(value) && value > 0 ? value : 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 在图片内取居中最大正方形作为默认选区。
|
|
18
|
+
* @param {number} imageWidth
|
|
19
|
+
* @param {number} imageHeight
|
|
20
|
+
* @returns {CropRect}
|
|
21
|
+
*/
|
|
22
|
+
export function defaultCropRect(imageWidth, imageHeight) {
|
|
23
|
+
const width = Math.max(0, Math.floor(finitePositive(imageWidth)));
|
|
24
|
+
const height = Math.max(0, Math.floor(finitePositive(imageHeight)));
|
|
25
|
+
const size = Math.min(width, height);
|
|
26
|
+
if (size < 1) return { x: 0, y: 0, size: 0 };
|
|
27
|
+
return {
|
|
28
|
+
x: Math.floor((width - size) / 2),
|
|
29
|
+
y: Math.floor((height - size) / 2),
|
|
30
|
+
size
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 将正方形选区钳制在图片范围内。
|
|
36
|
+
* @param {Partial<CropRect>} rect
|
|
37
|
+
* @param {number} imageWidth
|
|
38
|
+
* @param {number} imageHeight
|
|
39
|
+
* @param {number} [minSize]
|
|
40
|
+
* @returns {CropRect}
|
|
41
|
+
*/
|
|
42
|
+
export function clampCropRect(rect, imageWidth, imageHeight, minSize = AVATAR_CROP_MIN_SIZE) {
|
|
43
|
+
const width = Math.max(0, Math.floor(finitePositive(imageWidth)));
|
|
44
|
+
const height = Math.max(0, Math.floor(finitePositive(imageHeight)));
|
|
45
|
+
if (width < 1 || height < 1) return { x: 0, y: 0, size: 0 };
|
|
46
|
+
|
|
47
|
+
const maxSize = Math.min(width, height);
|
|
48
|
+
const floorMin = Math.max(1, Math.min(Math.floor(finitePositive(minSize)) || 1, maxSize));
|
|
49
|
+
let size = Math.floor(finitePositive(rect?.size));
|
|
50
|
+
if (size < floorMin) size = floorMin;
|
|
51
|
+
if (size > maxSize) size = maxSize;
|
|
52
|
+
|
|
53
|
+
let x = Math.floor(Number(rect?.x) || 0);
|
|
54
|
+
let y = Math.floor(Number(rect?.y) || 0);
|
|
55
|
+
if (x < 0) x = 0;
|
|
56
|
+
if (y < 0) y = 0;
|
|
57
|
+
if (x > width - size) x = width - size;
|
|
58
|
+
if (y > height - size) y = height - size;
|
|
59
|
+
return { x, y, size };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 平移选区。
|
|
64
|
+
* @param {CropRect} rect
|
|
65
|
+
* @param {number} deltaX
|
|
66
|
+
* @param {number} deltaY
|
|
67
|
+
* @param {number} imageWidth
|
|
68
|
+
* @param {number} imageHeight
|
|
69
|
+
* @returns {CropRect}
|
|
70
|
+
*/
|
|
71
|
+
export function moveCropRect(rect, deltaX, deltaY, imageWidth, imageHeight) {
|
|
72
|
+
return clampCropRect({
|
|
73
|
+
x: (Number(rect?.x) || 0) + (Number(deltaX) || 0),
|
|
74
|
+
y: (Number(rect?.y) || 0) + (Number(deltaY) || 0),
|
|
75
|
+
size: rect?.size
|
|
76
|
+
}, imageWidth, imageHeight);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 从四角手柄调整正方形选区,对边角保持固定。
|
|
81
|
+
* @param {CropRect} rect
|
|
82
|
+
* @param {"nw"|"ne"|"sw"|"se"} handle
|
|
83
|
+
* @param {number} pointerX 原图像素坐标
|
|
84
|
+
* @param {number} pointerY 原图像素坐标
|
|
85
|
+
* @param {number} imageWidth
|
|
86
|
+
* @param {number} imageHeight
|
|
87
|
+
* @returns {CropRect}
|
|
88
|
+
*/
|
|
89
|
+
export function resizeCropRect(rect, handle, pointerX, pointerY, imageWidth, imageHeight) {
|
|
90
|
+
const current = clampCropRect(rect, imageWidth, imageHeight);
|
|
91
|
+
const left = current.x;
|
|
92
|
+
const top = current.y;
|
|
93
|
+
const right = current.x + current.size;
|
|
94
|
+
const bottom = current.y + current.size;
|
|
95
|
+
const px = Number(pointerX) || 0;
|
|
96
|
+
const py = Number(pointerY) || 0;
|
|
97
|
+
|
|
98
|
+
let nextLeft = left;
|
|
99
|
+
let nextTop = top;
|
|
100
|
+
let nextRight = right;
|
|
101
|
+
let nextBottom = bottom;
|
|
102
|
+
|
|
103
|
+
if (handle === "nw") {
|
|
104
|
+
nextLeft = px;
|
|
105
|
+
nextTop = py;
|
|
106
|
+
} else if (handle === "ne") {
|
|
107
|
+
nextRight = px;
|
|
108
|
+
nextTop = py;
|
|
109
|
+
} else if (handle === "sw") {
|
|
110
|
+
nextLeft = px;
|
|
111
|
+
nextBottom = py;
|
|
112
|
+
} else {
|
|
113
|
+
nextRight = px;
|
|
114
|
+
nextBottom = py;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const widthSpan = Math.abs(nextRight - nextLeft);
|
|
118
|
+
const heightSpan = Math.abs(nextBottom - nextTop);
|
|
119
|
+
const size = Math.max(widthSpan, heightSpan);
|
|
120
|
+
|
|
121
|
+
if (handle === "nw") {
|
|
122
|
+
return clampCropRect({ x: right - size, y: bottom - size, size }, imageWidth, imageHeight);
|
|
123
|
+
}
|
|
124
|
+
if (handle === "ne") {
|
|
125
|
+
return clampCropRect({ x: left, y: bottom - size, size }, imageWidth, imageHeight);
|
|
126
|
+
}
|
|
127
|
+
if (handle === "sw") {
|
|
128
|
+
return clampCropRect({ x: right - size, y: top, size }, imageWidth, imageHeight);
|
|
129
|
+
}
|
|
130
|
+
return clampCropRect({ x: left, y: top, size }, imageWidth, imageHeight);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 计算 object-fit: contain 时图片在视口中的显示矩形。
|
|
135
|
+
* @param {number} imageWidth
|
|
136
|
+
* @param {number} imageHeight
|
|
137
|
+
* @param {number} viewportWidth
|
|
138
|
+
* @param {number} viewportHeight
|
|
139
|
+
* @returns {DisplayRect}
|
|
140
|
+
*/
|
|
141
|
+
export function containImageRect(imageWidth, imageHeight, viewportWidth, viewportHeight) {
|
|
142
|
+
const width = finitePositive(imageWidth);
|
|
143
|
+
const height = finitePositive(imageHeight);
|
|
144
|
+
const viewW = finitePositive(viewportWidth);
|
|
145
|
+
const viewH = finitePositive(viewportHeight);
|
|
146
|
+
if (!width || !height || !viewW || !viewH) {
|
|
147
|
+
return { x: 0, y: 0, width: 0, height: 0, scale: 0 };
|
|
148
|
+
}
|
|
149
|
+
const scale = Math.min(viewW / width, viewH / height);
|
|
150
|
+
const displayWidth = width * scale;
|
|
151
|
+
const displayHeight = height * scale;
|
|
152
|
+
return {
|
|
153
|
+
x: (viewW - displayWidth) / 2,
|
|
154
|
+
y: (viewH - displayHeight) / 2,
|
|
155
|
+
width: displayWidth,
|
|
156
|
+
height: displayHeight,
|
|
157
|
+
scale
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 视口坐标映射到原图像素坐标。
|
|
163
|
+
* @param {{ x: number, y: number }} point
|
|
164
|
+
* @param {DisplayRect} displayRect
|
|
165
|
+
* @returns {{ x: number, y: number }}
|
|
166
|
+
*/
|
|
167
|
+
export function mapDisplayPointToImage(point, displayRect) {
|
|
168
|
+
const scale = finitePositive(displayRect?.scale);
|
|
169
|
+
if (!scale) return { x: 0, y: 0 };
|
|
170
|
+
return {
|
|
171
|
+
x: ((Number(point?.x) || 0) - (Number(displayRect.x) || 0)) / scale,
|
|
172
|
+
y: ((Number(point?.y) || 0) - (Number(displayRect.y) || 0)) / scale
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 原图选区映射到视口显示矩形。
|
|
178
|
+
* @param {CropRect} cropRect
|
|
179
|
+
* @param {DisplayRect} displayRect
|
|
180
|
+
* @returns {{ x: number, y: number, size: number }}
|
|
181
|
+
*/
|
|
182
|
+
export function mapImageRectToDisplay(cropRect, displayRect) {
|
|
183
|
+
const scale = finitePositive(displayRect?.scale);
|
|
184
|
+
return {
|
|
185
|
+
x: (Number(displayRect?.x) || 0) + (Number(cropRect?.x) || 0) * scale,
|
|
186
|
+
y: (Number(displayRect?.y) || 0) + (Number(cropRect?.y) || 0) * scale,
|
|
187
|
+
size: (Number(cropRect?.size) || 0) * scale
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 裁剪输出边长:不超过原选区,也不超过上限。
|
|
193
|
+
* @param {number} cropSize
|
|
194
|
+
* @param {number} [maxSize]
|
|
195
|
+
* @returns {number}
|
|
196
|
+
*/
|
|
197
|
+
export function cropOutputSize(cropSize, maxSize = AVATAR_CROP_OUTPUT_SIZE) {
|
|
198
|
+
const size = Math.max(1, Math.floor(finitePositive(cropSize)) || 1);
|
|
199
|
+
const limit = Math.max(1, Math.floor(finitePositive(maxSize)) || AVATAR_CROP_OUTPUT_SIZE);
|
|
200
|
+
return Math.min(size, limit);
|
|
201
|
+
}
|
package/dist/public/index.html
CHANGED
|
@@ -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-
|
|
13
|
+
<link rel="stylesheet" href="/styles.css?v=20260725-module-header-scale">
|
|
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>密码<
|
|
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>
|
|
@@ -123,10 +123,10 @@
|
|
|
123
123
|
<button type="button" data-module="organizations"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>组织</button>
|
|
124
124
|
<button type="button" data-module="timeline"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>时间轴</button>
|
|
125
125
|
<button type="button" data-module="relationships"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.59 13.51 6.83 3.98"/><path d="m15.41 6.51-6.82 3.98"/></svg>关系</button>
|
|
126
|
-
<button type="button" data-module="
|
|
126
|
+
<button type="button" data-module="races"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="4" r="2"/><circle cx="18" cy="8" r="2"/><circle cx="20" cy="16" r="2"/><path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z"/></svg>种族</button>
|
|
127
127
|
<button class="ai-analysis-entry" type="button" data-module="tasks"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/><path d="M20 3v4"/><path d="M22 5h-4"/><path d="M4 17v2"/><path d="M5 18H3"/></svg>AI 分析</button>
|
|
128
128
|
<button id="module-more-button" type="button" aria-expanded="false"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg><span class="nav-label">更多</span></button>
|
|
129
|
-
<button class="module-nav-secondary hidden" type="button" data-module="
|
|
129
|
+
<button class="module-nav-secondary hidden" type="button" data-module="outlines"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m3 17 2 2 4-4"/><path d="m3 7 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/></svg><span class="nav-label">大纲/伏笔</span></button>
|
|
130
130
|
<button class="module-nav-secondary hidden" type="button" data-module="reviews"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="m9 14 2 2 4-4"/></svg>审核</button>
|
|
131
131
|
<button class="module-nav-secondary hidden" type="button" data-module="ai-settings"><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><rect width="16" height="16" x="4" y="4" rx="2"/><rect width="6" height="6" x="9" y="9" rx="1"/><path d="M15 2v2"/><path d="M15 20v2"/><path d="M2 15h2"/><path d="M2 9h2"/><path d="M20 15h2"/><path d="M20 9h2"/><path d="M9 2v2"/><path d="M9 20v2"/></svg>AI 设置</button>
|
|
132
132
|
<button class="module-nav-secondary hidden" type="button" data-work-settings><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.09a2 2 0 0 1 1 1.74v.5a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.38a2 2 0 0 0-.73-2.73l-.15-.09a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2Z"/><circle cx="12" cy="12" r="3"/></svg>作品设置</button>
|
|
@@ -202,6 +202,7 @@
|
|
|
202
202
|
<span id="chapter-stats" class="chapter-stats">0 字 · v0</span>
|
|
203
203
|
<button id="insight-button" class="ghost-button" type="button">章节概览</button>
|
|
204
204
|
<button id="versions-button" class="ghost-button" type="button">版本</button>
|
|
205
|
+
<button id="chapter-edit-button" class="primary-button hidden" type="button">编辑</button>
|
|
205
206
|
<button id="tidy-blank-lines-button" class="ghost-button" type="button">整理空行</button>
|
|
206
207
|
<button id="save-button" class="primary-button" type="button">保存正文</button>
|
|
207
208
|
</div>
|
|
@@ -590,7 +591,7 @@
|
|
|
590
591
|
<header class="account-settings-section-header"><h3 id="profile-settings-title">个人资料</h3><p>用于作品协作和历史记录中的作者标识。</p></header>
|
|
591
592
|
<div class="avatar-settings">
|
|
592
593
|
<div id="profile-avatar-preview" class="user-avatar profile-avatar-preview" role="img" aria-label="当前头像"><span class="user-avatar-fallback">作</span></div>
|
|
593
|
-
<div class="avatar-settings-copy"><strong>头像图片</strong><small>支持 PNG、JPEG、WebP,文件不超过 5 MB
|
|
594
|
+
<div class="avatar-settings-copy"><strong>头像图片</strong><small>支持 PNG、JPEG、WebP,文件不超过 5 MB。选择后可框选正方形选区再裁剪上传。</small></div>
|
|
594
595
|
<div class="avatar-settings-actions"><button id="avatar-upload-button" class="ghost-button" type="button">上传头像</button><button id="avatar-remove-button" class="ghost-button hidden" type="button">移除头像</button></div>
|
|
595
596
|
</div>
|
|
596
597
|
<form id="profile-form" class="account-settings-form"><label>显示名称<input id="profile-display-name" name="displayName" maxlength="80" required></label><button class="primary-button" type="submit">保存名称</button></form>
|
|
@@ -613,6 +614,37 @@
|
|
|
613
614
|
</div>
|
|
614
615
|
</dialog>
|
|
615
616
|
|
|
617
|
+
<dialog id="avatar-crop-dialog" class="dialog avatar-crop-dialog" aria-labelledby="avatar-crop-dialog-title" aria-describedby="avatar-crop-dialog-description">
|
|
618
|
+
<div class="dialog-header">
|
|
619
|
+
<div>
|
|
620
|
+
<span class="eyebrow">个人账户</span>
|
|
621
|
+
<h2 id="avatar-crop-dialog-title">裁剪头像</h2>
|
|
622
|
+
<p id="avatar-crop-dialog-description" class="dialog-header-meta">拖动选区或四角手柄调整范围,圆形预览即最终显示效果。</p>
|
|
623
|
+
</div>
|
|
624
|
+
<button id="avatar-crop-close" class="dialog-close" aria-label="关闭裁剪" type="button">×</button>
|
|
625
|
+
</div>
|
|
626
|
+
<div class="avatar-crop-body">
|
|
627
|
+
<div id="avatar-crop-stage" class="avatar-crop-stage" tabindex="0" role="img" aria-label="头像裁剪选区">
|
|
628
|
+
<img id="avatar-crop-image" class="avatar-crop-image" alt="">
|
|
629
|
+
<div id="avatar-crop-shade" class="avatar-crop-shade" aria-hidden="true"></div>
|
|
630
|
+
<div id="avatar-crop-selection" class="avatar-crop-selection" hidden>
|
|
631
|
+
<button type="button" class="avatar-crop-handle" data-avatar-crop-handle="nw" aria-label="调整左上角"></button>
|
|
632
|
+
<button type="button" class="avatar-crop-handle" data-avatar-crop-handle="ne" aria-label="调整右上角"></button>
|
|
633
|
+
<button type="button" class="avatar-crop-handle" data-avatar-crop-handle="sw" aria-label="调整左下角"></button>
|
|
634
|
+
<button type="button" class="avatar-crop-handle" data-avatar-crop-handle="se" aria-label="调整右下角"></button>
|
|
635
|
+
</div>
|
|
636
|
+
</div>
|
|
637
|
+
<aside class="avatar-crop-side">
|
|
638
|
+
<div id="avatar-crop-preview" class="user-avatar avatar-crop-preview" role="img" aria-label="裁剪预览"></div>
|
|
639
|
+
<p>预览</p>
|
|
640
|
+
</aside>
|
|
641
|
+
</div>
|
|
642
|
+
<div class="dialog-actions">
|
|
643
|
+
<button id="avatar-crop-cancel" class="ghost-button" type="button">取消</button>
|
|
644
|
+
<button id="avatar-crop-confirm" class="primary-button" type="button">确认裁剪</button>
|
|
645
|
+
</div>
|
|
646
|
+
</dialog>
|
|
647
|
+
|
|
616
648
|
<dialog id="members-dialog" class="dialog wide-dialog" aria-labelledby="members-dialog-title">
|
|
617
649
|
<div class="dialog-header"><div><span id="members-dialog-eyebrow" class="eyebrow">作品权限</span><h2 id="members-dialog-title">成员模块权限</h2></div><button id="members-dialog-close" class="dialog-close" aria-label="关闭" type="button">×</button></div>
|
|
618
650
|
<div class="access-dialog-body">
|
|
@@ -699,6 +731,6 @@
|
|
|
699
731
|
<div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
|
|
700
732
|
<script id="vditorIconScript" src="/vendor/vditor/dist/js/icons/ant.js?v=3.11.2"></script>
|
|
701
733
|
<script src="/vendor/vditor/dist/index.min.js?v=3.11.2"></script>
|
|
702
|
-
<script type="module" src="/app.js?v=20260725-
|
|
734
|
+
<script type="module" src="/app.js?v=20260725-module-header-scale"></script>
|
|
703
735
|
</body>
|
|
704
736
|
</html>
|
package/dist/public/styles.css
CHANGED
|
@@ -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; }
|
|
@@ -618,17 +619,23 @@ body.is-panel-resizing { cursor: col-resize; user-select: none; }
|
|
|
618
619
|
.app-shell.prose-hidden-mode:not(.shelf-mode) .panel-heading,
|
|
619
620
|
.app-shell.prose-hidden-mode:not(.shelf-mode) #novel-tree { display: none !important; }
|
|
620
621
|
.app-shell.prose-read-only-mode:not(.shelf-mode) #tidy-blank-lines-button,
|
|
621
|
-
.app-shell.prose-read-only-mode:not(.shelf-mode) #save-button
|
|
622
|
+
.app-shell.prose-read-only-mode:not(.shelf-mode) #save-button,
|
|
623
|
+
.app-shell.prose-read-only-mode:not(.shelf-mode) #chapter-edit-button { display: none !important; }
|
|
622
624
|
.app-shell.prose-read-only-mode:not(.shelf-mode) .left-primary-actions { justify-content: flex-end; }
|
|
623
625
|
.app-shell.prose-read-only-mode:not(.shelf-mode) #chapter-title[readonly],
|
|
624
626
|
.app-shell.prose-read-only-mode:not(.shelf-mode) #chapter-content[readonly] { cursor: default; }
|
|
625
627
|
.app-shell.view-only-mode:not(.shelf-mode) #module-create-button,
|
|
626
628
|
.app-shell.view-only-mode:not(.shelf-mode) [data-work-settings],
|
|
627
629
|
.app-shell.view-only-mode:not(.shelf-mode) #tidy-blank-lines-button,
|
|
628
|
-
.app-shell.view-only-mode:not(.shelf-mode) #save-button
|
|
630
|
+
.app-shell.view-only-mode:not(.shelf-mode) #save-button,
|
|
631
|
+
.app-shell.view-only-mode:not(.shelf-mode) #chapter-edit-button { display: none !important; }
|
|
629
632
|
.app-shell.view-only-mode:not(.shelf-mode) .left-primary-actions { justify-content: flex-end; }
|
|
630
633
|
.app-shell.view-only-mode:not(.shelf-mode) #chapter-title[readonly],
|
|
631
634
|
.app-shell.view-only-mode:not(.shelf-mode) #chapter-content[readonly] { cursor: default; }
|
|
635
|
+
.editor-view.is-read-only #tidy-blank-lines-button,
|
|
636
|
+
.editor-view.is-read-only #save-button { display: none !important; }
|
|
637
|
+
.editor-view.is-read-only #chapter-title[readonly],
|
|
638
|
+
.editor-view.is-read-only #chapter-content[readonly] { cursor: default; }
|
|
632
639
|
body.work-viewer-mode [data-setting-status],
|
|
633
640
|
body.work-viewer-mode [data-edit-setting],
|
|
634
641
|
body.work-viewer-mode [data-edit-character],
|
|
@@ -746,6 +753,74 @@ body.work-viewer-mode .character-editor-actions { display: none !important; }
|
|
|
746
753
|
.avatar-settings-actions { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
747
754
|
.avatar-settings-actions button { min-height: 32px; padding: 6px 11px; font-size: 11px; }
|
|
748
755
|
.avatar-settings-actions button:disabled { cursor: wait; opacity: .5; }
|
|
756
|
+
.avatar-crop-dialog { width: min(720px, 94vw); }
|
|
757
|
+
.avatar-crop-body { display: grid; grid-template-columns: minmax(0, 1fr) 96px; gap: 16px; align-items: start; padding: 18px 24px 8px; }
|
|
758
|
+
.avatar-crop-stage {
|
|
759
|
+
position: relative;
|
|
760
|
+
display: grid;
|
|
761
|
+
place-items: center;
|
|
762
|
+
width: 100%;
|
|
763
|
+
aspect-ratio: 1;
|
|
764
|
+
max-height: min(52vh, 480px);
|
|
765
|
+
overflow: hidden;
|
|
766
|
+
border: 1px solid var(--line);
|
|
767
|
+
border-radius: 6px;
|
|
768
|
+
background:
|
|
769
|
+
linear-gradient(45deg, color-mix(in srgb, var(--line) 55%, transparent) 25%, transparent 25%) 0 0 / 16px 16px,
|
|
770
|
+
linear-gradient(-45deg, color-mix(in srgb, var(--line) 55%, transparent) 25%, transparent 25%) 0 8px / 16px 16px,
|
|
771
|
+
var(--surface-soft);
|
|
772
|
+
touch-action: none;
|
|
773
|
+
user-select: none;
|
|
774
|
+
}
|
|
775
|
+
.avatar-crop-image {
|
|
776
|
+
position: absolute;
|
|
777
|
+
inset: 0;
|
|
778
|
+
width: 100%;
|
|
779
|
+
height: 100%;
|
|
780
|
+
object-fit: contain;
|
|
781
|
+
pointer-events: none;
|
|
782
|
+
}
|
|
783
|
+
.avatar-crop-shade {
|
|
784
|
+
position: absolute;
|
|
785
|
+
inset: 0;
|
|
786
|
+
pointer-events: none;
|
|
787
|
+
background: color-mix(in srgb, var(--ink) 42%, transparent);
|
|
788
|
+
-webkit-mask-image: linear-gradient(#000, #000), linear-gradient(#000, #000);
|
|
789
|
+
mask-image: linear-gradient(#000, #000), linear-gradient(#000, #000);
|
|
790
|
+
-webkit-mask-repeat: no-repeat;
|
|
791
|
+
mask-repeat: no-repeat;
|
|
792
|
+
-webkit-mask-composite: xor;
|
|
793
|
+
mask-composite: exclude;
|
|
794
|
+
}
|
|
795
|
+
.avatar-crop-selection {
|
|
796
|
+
position: absolute;
|
|
797
|
+
box-sizing: border-box;
|
|
798
|
+
border: 2px solid color-mix(in srgb, white 88%, var(--accent));
|
|
799
|
+
border-radius: 2px;
|
|
800
|
+
box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink) 35%, transparent);
|
|
801
|
+
cursor: grab;
|
|
802
|
+
touch-action: none;
|
|
803
|
+
}
|
|
804
|
+
.avatar-crop-selection.is-dragging { cursor: grabbing; }
|
|
805
|
+
.avatar-crop-selection[hidden] { display: none; }
|
|
806
|
+
.avatar-crop-handle {
|
|
807
|
+
position: absolute;
|
|
808
|
+
width: 12px;
|
|
809
|
+
height: 12px;
|
|
810
|
+
padding: 0;
|
|
811
|
+
border: 2px solid color-mix(in srgb, white 90%, var(--accent));
|
|
812
|
+
border-radius: 2px;
|
|
813
|
+
background: var(--accent);
|
|
814
|
+
box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink) 28%, transparent);
|
|
815
|
+
}
|
|
816
|
+
.avatar-crop-handle[data-avatar-crop-handle="nw"] { top: -7px; left: -7px; cursor: nwse-resize; }
|
|
817
|
+
.avatar-crop-handle[data-avatar-crop-handle="ne"] { top: -7px; right: -7px; cursor: nesw-resize; }
|
|
818
|
+
.avatar-crop-handle[data-avatar-crop-handle="sw"] { bottom: -7px; left: -7px; cursor: nesw-resize; }
|
|
819
|
+
.avatar-crop-handle[data-avatar-crop-handle="se"] { bottom: -7px; right: -7px; cursor: nwse-resize; }
|
|
820
|
+
.avatar-crop-side { display: grid; justify-items: center; gap: 8px; padding-top: 8px; }
|
|
821
|
+
.avatar-crop-side p { margin: 0; color: var(--muted); font-size: 10px; }
|
|
822
|
+
.avatar-crop-preview { width: 76px; height: 76px; border: 2px solid color-mix(in srgb, var(--line) 72%, transparent); background: var(--surface); }
|
|
823
|
+
.avatar-crop-preview canvas { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: 50%; }
|
|
749
824
|
.account-settings-form { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 12px; margin-top: 16px; }
|
|
750
825
|
.account-settings-form label { display: grid; min-width: 0; gap: 6px; color: var(--muted); font-size: 11px; }
|
|
751
826
|
.account-settings-form input { width: 100%; min-height: 38px; padding: 9px 10px; background: var(--surface); font-size: 12px; }
|
|
@@ -877,7 +952,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
877
952
|
.chapter-add-button { flex: none; }
|
|
878
953
|
.volume-chapters { display: block; }
|
|
879
954
|
.volume-node.is-collapsed .volume-chapters { display: none; }
|
|
880
|
-
|
|
955
|
+
.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
956
|
.chapter-node:hover, .chapter-node.active { background: var(--row-active); }
|
|
882
957
|
.chapter-node.active { color: var(--accent-dark); font-weight: 700; }
|
|
883
958
|
.chapter-node > span:first-child { min-width: 0; overflow-wrap: anywhere; line-height: 1.45; white-space: normal; }
|
|
@@ -944,7 +1019,8 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
944
1019
|
.module-header-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
|
|
945
1020
|
.module-header-actions > [data-module-header-action] { order: 1; }
|
|
946
1021
|
.module-header-actions > #module-create-button { order: 2; min-height: 36px; }
|
|
947
|
-
.module-
|
|
1022
|
+
.module-count-badge { display: inline-grid; min-width: 28px; height: 28px; padding: 0 7px; place-items: center; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); color: var(--accent-dark); font-family: var(--font-latin), monospace; font-size: 11px; font-weight: 650; }
|
|
1023
|
+
.module-header h1 { margin: 0 0 6px; font-weight: 500; font-size: 24px; line-height: 1.25; }
|
|
948
1024
|
.module-header p { margin: 0; color: var(--muted); font-size: 13px; }
|
|
949
1025
|
.module-content { padding: 24px 0 80px; }
|
|
950
1026
|
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 13px; }
|
|
@@ -962,6 +1038,8 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
962
1038
|
.module-filter-toggle svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.7; }
|
|
963
1039
|
.module-filter-toggle + .module-layout-toolbar { order: 2; }
|
|
964
1040
|
.module-filter-toggle ~ #module-create-button { order: 3; }
|
|
1041
|
+
.race-tree-expand-toolbar ~ .module-layout-toolbar { order: 2; }
|
|
1042
|
+
.race-tree-expand-toolbar ~ #module-create-button { order: 3; }
|
|
965
1043
|
.settings-layout-hint { color: var(--muted); font-size: 10px; }
|
|
966
1044
|
.settings-layout-toggle, .module-layout-toggle { display: inline-flex; border: 1px solid var(--line); border-radius: 4px; overflow: hidden; background: var(--surface); }
|
|
967
1045
|
.settings-layout-toggle button { min-height: 34px; padding: 0 12px; border: 0; border-right: 1px solid var(--line); background: transparent; color: var(--muted); font-size: 11px; }
|
|
@@ -982,6 +1060,13 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
982
1060
|
.setting-row .card-actions, .module-row .card-actions { margin-top: 0; flex-wrap: wrap; justify-content: flex-end; }
|
|
983
1061
|
.module-row .card-actions > .record-card-edit { position: static; }
|
|
984
1062
|
.character-card { cursor: pointer; }
|
|
1063
|
+
.character-row { grid-template-columns: minmax(140px, .28fr) minmax(0, 1fr) auto; }
|
|
1064
|
+
.character-card-heading { display: flex; min-width: 0; align-items: center; gap: 7px; }
|
|
1065
|
+
.character-card-heading h3 { min-width: 0; }
|
|
1066
|
+
.record-card.has-card-edit .character-card-heading { padding-right: 34px; }
|
|
1067
|
+
.record-card.has-card-edit .character-card-heading h3 { padding-right: 0; }
|
|
1068
|
+
.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; }
|
|
1069
|
+
.character-lock-badge svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.8; }
|
|
985
1070
|
.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
1071
|
.character-filter-dropdown { position: relative; min-width: 0; }
|
|
987
1072
|
.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; }
|
|
@@ -1399,6 +1484,12 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1399
1484
|
.book-summary-context-percent-field { width: min(100%, 280px); }
|
|
1400
1485
|
.context-compact-threshold-field { width: min(100%, 280px); }
|
|
1401
1486
|
.book-summary-context-percent-field input, .context-compact-threshold-field input { width: 64px; min-height: 32px; padding: 5px 8px; font-size: 13px; font-family: var(--font-latin), monospace; }
|
|
1487
|
+
.config-inline-save { display: flex; align-items: flex-end; gap: 10px; margin: 14px 0 4px; flex-wrap: wrap; }
|
|
1488
|
+
.config-inline-save .book-summary-context-percent-field,
|
|
1489
|
+
.config-inline-save .context-compact-threshold-field { display: grid; gap: 6px; width: auto; margin: 0; color: var(--muted); font-size: 10px; }
|
|
1490
|
+
.config-section .config-save-button { min-height: 32px; padding: 5px 11px; font-size: 11px; }
|
|
1491
|
+
.config-section .card-actions { margin-top: 12px; }
|
|
1492
|
+
.config-section .card-actions .config-save-button { border: 1px solid var(--line); background: transparent; color: var(--ink); }
|
|
1402
1493
|
.field-label textarea {
|
|
1403
1494
|
width: 100%;
|
|
1404
1495
|
min-height: 140px;
|
|
@@ -1633,6 +1724,9 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1633
1724
|
.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
1725
|
.form-field { display: grid; gap: 6px; color: var(--muted); font-size: 11px; }
|
|
1635
1726
|
.analysis-type-description { margin: 0; color: var(--muted); font-size: 10px; line-height: 1.55; }
|
|
1727
|
+
.task-chapter-field { transition: opacity .15s ease; }
|
|
1728
|
+
.task-chapter-field.is-disabled { opacity: .48; }
|
|
1729
|
+
.task-chapter-field select:disabled { cursor: not-allowed; }
|
|
1636
1730
|
.model-temperature-hint { margin: 0; color: var(--accent-dark); font-size: 10px; line-height: 1.55; }
|
|
1637
1731
|
.item-list-rows { display: grid; gap: 7px; }
|
|
1638
1732
|
.item-list-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 7px; }
|
|
@@ -1658,6 +1752,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1658
1752
|
.character-card h3 { margin-bottom: 5px; }
|
|
1659
1753
|
.character-identity { margin: 0 0 6px !important; color: var(--ink) !important; font-weight: 600; line-height: 1.55 !important; }
|
|
1660
1754
|
.character-aliases { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-width: 0; }
|
|
1755
|
+
.character-aliases b { margin-right: 2px; color: var(--muted); font-size: 9px; font-weight: 500; }
|
|
1661
1756
|
.character-aliases .pill { margin: 0; border: 1px solid var(--line); background: var(--surface); }
|
|
1662
1757
|
.character-code { display: flex; align-items: center; gap: 7px; margin-top: 10px; color: var(--muted); font-size: 10px; }
|
|
1663
1758
|
.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,14 +2108,22 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2013
2108
|
}
|
|
2014
2109
|
|
|
2015
2110
|
@media (max-width: 640px) {
|
|
2016
|
-
|
|
2017
|
-
.
|
|
2111
|
+
#onboarding-dialog, #onboarding-menu-button { display: none !important; }
|
|
2112
|
+
.topbar .work-meta { display: none; }
|
|
2113
|
+
.top-actions .save-state { display: none; }
|
|
2114
|
+
.presence-button #presence-count { display: none; }
|
|
2115
|
+
.account-button > span:last-child { display: none; }
|
|
2116
|
+
.book-shelf { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px 14px; }
|
|
2117
|
+
.book-info > span, .book-access-badge { display: none; }
|
|
2018
2118
|
.ai-history-dialog-body { min-height: min(360px, 62vh); padding: 12px 14px 18px; }
|
|
2019
2119
|
.account-settings-body { padding: 16px; }
|
|
2020
2120
|
.account-settings-section { padding: 15px; }
|
|
2021
2121
|
.account-settings-form, .password-settings-form, .api-key-settings, .api-key-result { grid-template-columns: minmax(0, 1fr); }
|
|
2022
2122
|
.avatar-settings { grid-template-columns: 64px minmax(0, 1fr); }
|
|
2023
2123
|
.profile-avatar-preview { width: 64px; height: 64px; }
|
|
2124
|
+
.avatar-crop-body { grid-template-columns: minmax(0, 1fr); gap: 12px; padding: 14px 16px 4px; }
|
|
2125
|
+
.avatar-crop-side { grid-template-columns: auto 1fr; align-items: center; justify-items: start; gap: 10px; padding-top: 0; }
|
|
2126
|
+
.avatar-crop-preview { width: 64px; height: 64px; }
|
|
2024
2127
|
.account-settings-form .primary-button { width: 100%; }
|
|
2025
2128
|
}
|
|
2026
2129
|
|
|
@@ -2060,6 +2163,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2060
2163
|
.brand-block strong { font-size: 14px; }
|
|
2061
2164
|
.brand-block small { display: none; }
|
|
2062
2165
|
.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; }
|
|
2166
|
+
.app-shell.shelf-mode .mobile-module-tab { display: none; }
|
|
2063
2167
|
.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
2168
|
.app-shell:not(.shelf-mode):not(.left-panel-collapsed) .mobile-panel-backdrop { display: block; }
|
|
2065
2169
|
.work-meta {
|
|
@@ -2183,7 +2287,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2183
2287
|
|
|
2184
2288
|
.module-view { padding: 26px var(--mobile-gutter) 46px; }
|
|
2185
2289
|
.module-header { padding-bottom: 18px; }
|
|
2186
|
-
.module-header h1 { font-size:
|
|
2290
|
+
.module-header h1 { font-size: 22px; }
|
|
2187
2291
|
.module-header-actions { justify-content: stretch; }
|
|
2188
2292
|
.module-header-actions > * { flex: 1 1 auto; }
|
|
2189
2293
|
.module-content { padding: 18px 0 56px; }
|
|
@@ -2243,7 +2347,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2243
2347
|
.editor-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
2244
2348
|
.editor-actions .chapter-stats { display: none; }
|
|
2245
2349
|
.editor-actions > button { width: auto; }
|
|
2246
|
-
.book-shelf { grid-template-columns: minmax(0, 1fr); max-width:
|
|
2350
|
+
.book-shelf { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 12px; max-width: none; }
|
|
2247
2351
|
.book-add-card { min-height: 220px; }
|
|
2248
2352
|
.onboarding-popover { width: calc(100vw - 20px); }
|
|
2249
2353
|
}
|
package/dist/store.js
CHANGED
|
@@ -608,7 +608,9 @@ export class Store {
|
|
|
608
608
|
autoRunBatchLimit: Math.min(200, Math.max(1, Number(row?.auto_run_batch_limit ?? 20) || 20)),
|
|
609
609
|
bookSummaryContextPercent: Math.min(90, Math.max(1, Number(row?.book_summary_context_percent ?? 50) || 50)),
|
|
610
610
|
contextCompactThreshold: Math.min(90, Math.max(50, Number(row?.context_compact_threshold ?? 85) || 85)),
|
|
611
|
-
agentTools: json(String(row?.agent_tools_json ?? '["story_index","read_chapters","
|
|
611
|
+
agentTools: json(String(row?.agent_tools_json ?? '["story_index","read_chapters","search_story_entities","grep","read_character_sections"]'), ["story_index", "read_chapters", "search_story_entities", "grep", "read_character_sections"])
|
|
612
|
+
.map((tool) => tool === "query_story_knowledge" ? "search_story_entities" : tool)
|
|
613
|
+
.filter((tool, index, tools) => tools.indexOf(tool) === index),
|
|
612
614
|
updatedAt: String(row?.updated_at ?? "")
|
|
613
615
|
};
|
|
614
616
|
}
|
|
@@ -2282,8 +2284,9 @@ export class Store {
|
|
|
2282
2284
|
listCharactersPage(workId, pagination, includeProfileSections = false, includeMerged = false, includeRaceMarkdown = true) {
|
|
2283
2285
|
this.getWork(workId);
|
|
2284
2286
|
const page = paginationSql(pagination);
|
|
2287
|
+
const count = this.db.get(`SELECT COUNT(*) AS count FROM characters WHERE work_id = ?${includeMerged ? "" : " AND merged_into_character_id IS NULL"}`, workId);
|
|
2285
2288
|
const rows = this.db.all(`SELECT * FROM characters WHERE work_id = ?${includeMerged ? "" : " AND merged_into_character_id IS NULL"} ORDER BY name${page.sql}`, workId, ...page.params);
|
|
2286
|
-
return paginated(rows.map((row) => this.mapCharacter(row, includeProfileSections, includeRaceMarkdown)), pagination);
|
|
2289
|
+
return paginated(rows.map((row) => this.mapCharacter(row, includeProfileSections, includeRaceMarkdown)), pagination, Number(count?.count ?? 0));
|
|
2287
2290
|
}
|
|
2288
2291
|
mapCharacterProfileSection(row) {
|
|
2289
2292
|
return {
|