@musnows/scriverse 0.9.0 → 0.9.1
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/app.js +22 -0
- package/dist/app.js.map +1 -1
- package/dist/database.js +77 -2
- package/dist/database.js.map +1 -1
- package/dist/public/app.js +206 -16
- package/dist/public/index.html +5 -2
- package/dist/public/styles.css +38 -4
- package/dist/store.js +220 -50
- package/dist/store.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/public/app.js
CHANGED
|
@@ -3445,15 +3445,18 @@ async function createNewAiConversation(taskType = "chat") {
|
|
|
3445
3445
|
function favoriteCharactersFirst(characters) {
|
|
3446
3446
|
const items = Array.isArray(characters) ? characters : [];
|
|
3447
3447
|
return [
|
|
3448
|
-
...items.filter((character) => character.isFavorite === true),
|
|
3449
|
-
...items.filter((character) => character.isFavorite !== true)
|
|
3448
|
+
...items.filter((character) => character.isPinned === true && character.isFavorite === true),
|
|
3449
|
+
...items.filter((character) => character.isPinned === true && character.isFavorite !== true),
|
|
3450
|
+
...items.filter((character) => character.isPinned !== true && character.isFavorite === true),
|
|
3451
|
+
...items.filter((character) => character.isPinned !== true && character.isFavorite !== true)
|
|
3450
3452
|
];
|
|
3451
3453
|
}
|
|
3452
3454
|
|
|
3453
3455
|
function roleplayCharacterOptionLabel(character) {
|
|
3454
|
-
const
|
|
3456
|
+
const pinLabel = character?.isPinned === true ? "[置顶]" : "";
|
|
3457
|
+
const favoriteLabel = character?.isFavorite === true ? "[收藏]" : "";
|
|
3455
3458
|
const deathLabel = character?.isDead ? "(已死亡)" : "";
|
|
3456
|
-
return `${favoriteLabel}${String(character?.name ?? "")}${deathLabel}`;
|
|
3459
|
+
return `${pinLabel}${favoriteLabel}${pinLabel || favoriteLabel ? " " : ""}${String(character?.name ?? "")}${deathLabel}`;
|
|
3457
3460
|
}
|
|
3458
3461
|
|
|
3459
3462
|
function renderAiRoleplayCharacterSelect() {
|
|
@@ -4109,6 +4112,31 @@ function aiPromptTextBeforeCursor() {
|
|
|
4109
4112
|
return promptTextFromNode(fragment).replace(/\n$/u, "");
|
|
4110
4113
|
}
|
|
4111
4114
|
|
|
4115
|
+
function aiPromptTextFromRange(range, prompt) {
|
|
4116
|
+
const beforeCursor = range.cloneRange();
|
|
4117
|
+
beforeCursor.selectNodeContents(prompt);
|
|
4118
|
+
beforeCursor.setEnd(range.endContainer, range.endOffset);
|
|
4119
|
+
const fragment = document.createElement("div");
|
|
4120
|
+
fragment.append(beforeCursor.cloneContents());
|
|
4121
|
+
return promptTextFromNode(fragment).replace(/\n$/u, "");
|
|
4122
|
+
}
|
|
4123
|
+
|
|
4124
|
+
function aiPromptTextBoundary(prompt, offset) {
|
|
4125
|
+
const walker = document.createTreeWalker(prompt, NodeFilter.SHOW_TEXT);
|
|
4126
|
+
let remaining = Math.max(0, Number(offset) || 0);
|
|
4127
|
+
let lastTextNode = null;
|
|
4128
|
+
while (walker.nextNode()) {
|
|
4129
|
+
const textNode = walker.currentNode;
|
|
4130
|
+
if (textNode.parentElement?.closest("[data-ai-reference-key]")) continue;
|
|
4131
|
+
lastTextNode = textNode;
|
|
4132
|
+
const length = textNode.textContent?.length ?? 0;
|
|
4133
|
+
if (remaining <= length) return { node: textNode, offset: remaining };
|
|
4134
|
+
remaining -= length;
|
|
4135
|
+
}
|
|
4136
|
+
if (lastTextNode) return { node: lastTextNode, offset: lastTextNode.textContent?.length ?? 0 };
|
|
4137
|
+
return { node: prompt, offset: prompt.childNodes.length };
|
|
4138
|
+
}
|
|
4139
|
+
|
|
4112
4140
|
function hideAiMentionMenu() {
|
|
4113
4141
|
aiMentionMatch = null;
|
|
4114
4142
|
aiMentionRange = null;
|
|
@@ -4197,12 +4225,14 @@ function selectAiMention(button) {
|
|
|
4197
4225
|
id: button.dataset.aiReferenceId,
|
|
4198
4226
|
name: button.dataset.aiReferenceName
|
|
4199
4227
|
};
|
|
4200
|
-
const
|
|
4201
|
-
const
|
|
4202
|
-
const localText = textNode.nodeType === Node.TEXT_NODE ? textNode.textContent?.slice(0, range.startOffset) ?? "" : "";
|
|
4203
|
-
const localMention = findAiMention(localText);
|
|
4228
|
+
const cursorText = aiPromptTextFromRange(aiMentionRange, prompt);
|
|
4229
|
+
const localMention = findAiMention(cursorText);
|
|
4204
4230
|
if (!localMention) return hideAiMentionMenu();
|
|
4205
|
-
range.
|
|
4231
|
+
const range = document.createRange();
|
|
4232
|
+
const startBoundary = aiPromptTextBoundary(prompt, localMention.start);
|
|
4233
|
+
const endBoundary = aiPromptTextBoundary(prompt, cursorText.length);
|
|
4234
|
+
range.setStart(startBoundary.node, startBoundary.offset);
|
|
4235
|
+
range.setEnd(endBoundary.node, endBoundary.offset);
|
|
4206
4236
|
range.deleteContents();
|
|
4207
4237
|
const spacer = document.createTextNode(" ");
|
|
4208
4238
|
range.insertNode(spacer);
|
|
@@ -8687,6 +8717,18 @@ function characterFavoriteIconMarkup() {
|
|
|
8687
8717
|
return '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m12 3 2.8 5.7 6.2.9-4.5 4.4 1.1 6.2-5.6-2.9-5.6 2.9 1.1-6.2L3 9.6l6.2-.9L12 3Z"></path></svg>';
|
|
8688
8718
|
}
|
|
8689
8719
|
|
|
8720
|
+
function pushPinIconMarkup() {
|
|
8721
|
+
return '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M16 9V4h1V2H7v2h1v5c0 1.66-1.34 3-3 3v2h5.97v6l1.03 1 1.03-1v-6H20v-2c-2.21 0-4-1.79-4-4Z"></path></svg>';
|
|
8722
|
+
}
|
|
8723
|
+
|
|
8724
|
+
function characterPinButton(item) {
|
|
8725
|
+
const isPinned = item.isPinned === true;
|
|
8726
|
+
const canPin = canEditModule("characters");
|
|
8727
|
+
const action = isPinned ? "取消置顶" : "置顶";
|
|
8728
|
+
const title = canPin ? action : "当前账户没有角色模块写入权限";
|
|
8729
|
+
return `<button class="character-pin-button${isPinned ? " is-pinned" : ""}" type="button" data-character-pin="${esc(item.id)}" aria-label="${action}角色“${esc(item.name)}”" aria-pressed="${isPinned}" title="${title}" ${canPin ? "" : "disabled"}>${pushPinIconMarkup()}</button>`;
|
|
8730
|
+
}
|
|
8731
|
+
|
|
8690
8732
|
function characterFavoriteButton(item) {
|
|
8691
8733
|
const isFavorite = item.isFavorite === true;
|
|
8692
8734
|
const canFavorite = canEditModule("characters");
|
|
@@ -8713,6 +8755,17 @@ function recordFavoriteButton(type, item, { cardControl = false } = {}) {
|
|
|
8713
8755
|
return `<button class="record-favorite-button${cardControl ? " is-card-control" : ""}${isFavorite ? " is-favorite" : ""}" type="button" data-record-favorite="${esc(item.id)}" data-record-favorite-type="${esc(type)}" aria-label="${action}${config.label}“${esc(name)}”" aria-pressed="${isFavorite}" title="${title}" ${canFavorite ? "" : "disabled"}>${characterFavoriteIconMarkup()}</button>`;
|
|
8714
8756
|
}
|
|
8715
8757
|
|
|
8758
|
+
function recordPinButton(type, item, { cardControl = false } = {}) {
|
|
8759
|
+
const config = recordFavoriteConfigs[type];
|
|
8760
|
+
if (!config) return "";
|
|
8761
|
+
const isPinned = item.isPinned === true;
|
|
8762
|
+
const canPin = canEditModule(config.module);
|
|
8763
|
+
const action = isPinned ? "取消置顶" : "置顶";
|
|
8764
|
+
const title = canPin ? action : `当前账户没有${config.label}模块写入权限`;
|
|
8765
|
+
const name = String(item[config.nameField] ?? "");
|
|
8766
|
+
return `<button class="record-pin-button${cardControl ? " is-card-control" : ""}${isPinned ? " is-pinned" : ""}" type="button" data-record-pin="${esc(item.id)}" data-record-pin-type="${esc(type)}" aria-label="${action}${config.label}“${esc(name)}”" aria-pressed="${isPinned}" title="${title}" ${canPin ? "" : "disabled"}>${pushPinIconMarkup()}</button>`;
|
|
8767
|
+
}
|
|
8768
|
+
|
|
8716
8769
|
async function toggleRecordFavorite(type, item, render) {
|
|
8717
8770
|
const config = recordFavoriteConfigs[type];
|
|
8718
8771
|
if (!config) return;
|
|
@@ -8729,6 +8782,22 @@ async function toggleRecordFavorite(type, item, render) {
|
|
|
8729
8782
|
toast(updated.isFavorite ? `已收藏${config.label}“${name}”` : `已取消收藏${config.label}“${name}”`);
|
|
8730
8783
|
}
|
|
8731
8784
|
|
|
8785
|
+
async function toggleRecordPin(type, item, render) {
|
|
8786
|
+
const config = recordFavoriteConfigs[type];
|
|
8787
|
+
if (!config) return;
|
|
8788
|
+
const updated = await api(`/api/${config.resource}/${encodeURIComponent(item.id)}/pin`, {
|
|
8789
|
+
method: "PATCH",
|
|
8790
|
+
body: { isPinned: item.isPinned !== true }
|
|
8791
|
+
});
|
|
8792
|
+
await render();
|
|
8793
|
+
const pinButton = [...$("#module-content").querySelectorAll("[data-record-pin]")].find((button) => (
|
|
8794
|
+
button.dataset.recordPinType === type && button.dataset.recordPin === String(updated.id)
|
|
8795
|
+
));
|
|
8796
|
+
pinButton?.focus({ preventScroll: true });
|
|
8797
|
+
const name = String(updated[config.nameField] ?? "");
|
|
8798
|
+
toast(updated.isPinned ? `已置顶${config.label}“${name}”` : `已取消置顶${config.label}“${name}”`);
|
|
8799
|
+
}
|
|
8800
|
+
|
|
8732
8801
|
function bindRecordFavoriteButtons(type, items, render) {
|
|
8733
8802
|
const config = recordFavoriteConfigs[type];
|
|
8734
8803
|
if (!config) return;
|
|
@@ -8745,6 +8814,22 @@ function bindRecordFavoriteButtons(type, items, render) {
|
|
|
8745
8814
|
}));
|
|
8746
8815
|
}
|
|
8747
8816
|
|
|
8817
|
+
function bindRecordPinButtons(type, items, render) {
|
|
8818
|
+
const config = recordFavoriteConfigs[type];
|
|
8819
|
+
if (!config) return;
|
|
8820
|
+
$("#module-content").querySelectorAll(`[data-record-pin-type="${type}"]`).forEach((button) => button.addEventListener("click", async () => {
|
|
8821
|
+
const item = items.find((candidate) => candidate.id === button.dataset.recordPin);
|
|
8822
|
+
if (!item || !canEditModule(config.module)) return;
|
|
8823
|
+
button.disabled = true;
|
|
8824
|
+
try {
|
|
8825
|
+
await toggleRecordPin(type, item, render);
|
|
8826
|
+
} catch (error) {
|
|
8827
|
+
button.disabled = false;
|
|
8828
|
+
toast(`${config.label}置顶状态更新失败:${error.message}`, "error");
|
|
8829
|
+
}
|
|
8830
|
+
}));
|
|
8831
|
+
}
|
|
8832
|
+
|
|
8748
8833
|
function syncEntityDetailFavoriteButton(button, type, item) {
|
|
8749
8834
|
const config = recordFavoriteConfigs[type];
|
|
8750
8835
|
const visible = Boolean(config && item);
|
|
@@ -8769,6 +8854,30 @@ function syncEntityDetailFavoriteButton(button, type, item) {
|
|
|
8769
8854
|
button.disabled = !canFavorite;
|
|
8770
8855
|
}
|
|
8771
8856
|
|
|
8857
|
+
function syncEntityDetailPinButton(button, type, item) {
|
|
8858
|
+
const config = recordFavoriteConfigs[type];
|
|
8859
|
+
const visible = Boolean(config && item);
|
|
8860
|
+
button.classList.toggle("hidden", !visible);
|
|
8861
|
+
button.innerHTML = pushPinIconMarkup();
|
|
8862
|
+
if (!visible) {
|
|
8863
|
+
button.disabled = true;
|
|
8864
|
+
button.classList.remove("is-pinned");
|
|
8865
|
+
button.setAttribute("aria-pressed", "false");
|
|
8866
|
+
button.removeAttribute("aria-label");
|
|
8867
|
+
button.removeAttribute("title");
|
|
8868
|
+
return;
|
|
8869
|
+
}
|
|
8870
|
+
const isPinned = item.isPinned === true;
|
|
8871
|
+
const canPin = canEditModule(config.module);
|
|
8872
|
+
const action = isPinned ? "取消置顶" : "置顶";
|
|
8873
|
+
const name = String(item[config.nameField] ?? "");
|
|
8874
|
+
button.classList.toggle("is-pinned", isPinned);
|
|
8875
|
+
button.setAttribute("aria-pressed", String(isPinned));
|
|
8876
|
+
button.setAttribute("aria-label", `${action}${config.label}“${name}”`);
|
|
8877
|
+
button.title = canPin ? action : `当前账户没有${config.label}模块写入权限`;
|
|
8878
|
+
button.disabled = !canPin;
|
|
8879
|
+
}
|
|
8880
|
+
|
|
8772
8881
|
function bindEntityDetailFavoriteButton(button, type, getItem, onUpdated) {
|
|
8773
8882
|
syncEntityDetailFavoriteButton(button, type, getItem());
|
|
8774
8883
|
button.onclick = async () => {
|
|
@@ -8793,6 +8902,30 @@ function bindEntityDetailFavoriteButton(button, type, getItem, onUpdated) {
|
|
|
8793
8902
|
};
|
|
8794
8903
|
}
|
|
8795
8904
|
|
|
8905
|
+
function bindEntityDetailPinButton(button, type, getItem, onUpdated) {
|
|
8906
|
+
syncEntityDetailPinButton(button, type, getItem());
|
|
8907
|
+
button.onclick = async () => {
|
|
8908
|
+
const config = recordFavoriteConfigs[type];
|
|
8909
|
+
const item = getItem();
|
|
8910
|
+
if (!config || !item || !canEditModule(config.module)) return;
|
|
8911
|
+
button.disabled = true;
|
|
8912
|
+
try {
|
|
8913
|
+
const updated = await api(`/api/${config.resource}/${encodeURIComponent(item.id)}/pin`, {
|
|
8914
|
+
method: "PATCH",
|
|
8915
|
+
body: { isPinned: item.isPinned !== true }
|
|
8916
|
+
});
|
|
8917
|
+
onUpdated(updated);
|
|
8918
|
+
syncEntityDetailPinButton(button, type, getItem() ?? updated);
|
|
8919
|
+
button.focus({ preventScroll: true });
|
|
8920
|
+
const name = String(updated[config.nameField] ?? "");
|
|
8921
|
+
toast(updated.isPinned ? `已置顶${config.label}“${name}”` : `已取消置顶${config.label}“${name}”`);
|
|
8922
|
+
} catch (error) {
|
|
8923
|
+
syncEntityDetailPinButton(button, type, getItem());
|
|
8924
|
+
toast(`${config.label}置顶状态更新失败:${error instanceof Error ? error.message : "未知错误"}`, "error");
|
|
8925
|
+
}
|
|
8926
|
+
};
|
|
8927
|
+
}
|
|
8928
|
+
|
|
8796
8929
|
function recordCardEditButton(attribute, id, label) {
|
|
8797
8930
|
return `<button class="record-card-edit" type="button" data-${attribute}="${esc(id)}" aria-label="编辑${esc(label)}" title="编辑">${pencilIconMarkup()}</button>`;
|
|
8798
8931
|
}
|
|
@@ -9131,7 +9264,7 @@ function settingRecordActions(item) {
|
|
|
9131
9264
|
const recordAction = canEditModule("settings")
|
|
9132
9265
|
? recordCardEditButton("edit-setting", item.id, `设定“${item.title}”`)
|
|
9133
9266
|
: recordHistoryButton("setting", item.id, item.title);
|
|
9134
|
-
return `${recordFavoriteButton("setting", item)}${recordAction}`;
|
|
9267
|
+
return `${recordPinButton("setting", item)}${recordFavoriteButton("setting", item)}${recordAction}`;
|
|
9135
9268
|
}
|
|
9136
9269
|
|
|
9137
9270
|
function draftTypeLabel(draftType) {
|
|
@@ -9245,7 +9378,11 @@ function openDraftDialog(item = null, { readOnly = false } = {}) {
|
|
|
9245
9378
|
}
|
|
9246
9379
|
if (item && viewOnly) {
|
|
9247
9380
|
const headerActions = $("#dialog-context-actions");
|
|
9248
|
-
headerActions.innerHTML = `<button id="draft-dialog-favorite" class="entity-detail-favorite-button" type="button" aria-pressed="false"></button>${canEditModule("drafts") ? '<button id="draft-dialog-edit" class="ghost-button" type="button">编辑想法</button>' : ""}`;
|
|
9381
|
+
headerActions.innerHTML = `<button id="draft-dialog-pin" class="entity-detail-pin-button hidden" type="button" aria-pressed="false"></button><button id="draft-dialog-favorite" class="entity-detail-favorite-button" type="button" aria-pressed="false"></button>${canEditModule("drafts") ? '<button id="draft-dialog-edit" class="ghost-button" type="button">编辑想法</button>' : ""}`;
|
|
9382
|
+
bindEntityDetailPinButton($("#draft-dialog-pin"), "draft", () => draftDialogItem, (updated) => {
|
|
9383
|
+
draftDialogItem = updated;
|
|
9384
|
+
void renderDrafts(moduleListPages.drafts).catch((error) => toast(`想法列表刷新失败:${error instanceof Error ? error.message : "未知错误"}`, "error"));
|
|
9385
|
+
});
|
|
9249
9386
|
bindEntityDetailFavoriteButton($("#draft-dialog-favorite"), "draft", () => draftDialogItem, (updated) => {
|
|
9250
9387
|
draftDialogItem = updated;
|
|
9251
9388
|
void renderDrafts(moduleListPages.drafts).catch((error) => toast(`想法列表刷新失败:${error instanceof Error ? error.message : "未知错误"}`, "error"));
|
|
@@ -9288,7 +9425,7 @@ async function renderDrafts(page = moduleListPages.drafts) {
|
|
|
9288
9425
|
<details class="character-filter-dropdown"><summary><span>按绑定位置筛选</span><strong>${selectedBindingKeys.size ? `已选 ${selectedBindingKeys.size} 项` : "全部位置"}</strong></summary><div id="draft-binding-filter" class="character-filter-options">${bindingOptions.map(([value, label]) => `<label class="character-filter-option"><input type="checkbox" value="${esc(value)}" ${selectedBindingKeys.has(value) ? "checked" : ""}><span>${esc(label)}</span></label>`).join("")}</div></details>
|
|
9289
9426
|
<div class="character-filter-toolbar-actions">${hasDraftFilters ? `<span class="character-filter-result-count" aria-live="polite">筛选后剩余 ${drafts.length} 条想法</span>` : ""}<button id="clear-draft-filters" class="ghost-button" type="button" ${hasDraftFilters ? "" : "disabled"}>重置筛选</button></div>
|
|
9290
9427
|
</section>`;
|
|
9291
|
-
const actions = (item) => `${recordFavoriteButton("draft", item)}${canEditModule("drafts")
|
|
9428
|
+
const actions = (item) => `${recordPinButton("draft", item)}${recordFavoriteButton("draft", item)}${canEditModule("drafts")
|
|
9292
9429
|
? `${recordCardEditButton("edit-draft", item.id, `想法“${item.title}”`)}${recordHistoryButton("draft", item.id, item.title)}`
|
|
9293
9430
|
: recordHistoryButton("draft", item.id, item.title)}`;
|
|
9294
9431
|
const cards = `<div class="card-grid">${pageResult.items.map((item) => `
|
|
@@ -9350,6 +9487,10 @@ async function renderDrafts(page = moduleListPages.drafts) {
|
|
|
9350
9487
|
moduleListPages.drafts = 1;
|
|
9351
9488
|
await renderDrafts(1);
|
|
9352
9489
|
});
|
|
9490
|
+
bindRecordPinButtons("draft", pageResult.items, async () => {
|
|
9491
|
+
moduleListPages.drafts = 1;
|
|
9492
|
+
await renderDrafts(1);
|
|
9493
|
+
});
|
|
9353
9494
|
bindEntityHistoryButtons(() => renderDrafts(pageResult.page));
|
|
9354
9495
|
}
|
|
9355
9496
|
|
|
@@ -9417,6 +9558,10 @@ async function renderSettings(page = moduleListPages.settings) {
|
|
|
9417
9558
|
moduleListPages.settings = 1;
|
|
9418
9559
|
await renderSettings(1);
|
|
9419
9560
|
});
|
|
9561
|
+
bindRecordPinButtons("setting", pageResult.items, async () => {
|
|
9562
|
+
moduleListPages.settings = 1;
|
|
9563
|
+
await renderSettings(1);
|
|
9564
|
+
});
|
|
9420
9565
|
bindEntityHistoryButtons(async () => { await renderSettings(pageResult.page); await loadAiReferences(); });
|
|
9421
9566
|
};
|
|
9422
9567
|
|
|
@@ -9468,6 +9613,21 @@ async function toggleCharacterFavorite(item) {
|
|
|
9468
9613
|
toast(updated.isFavorite ? `已收藏角色“${updated.name}”` : `已取消收藏角色“${updated.name}”`);
|
|
9469
9614
|
}
|
|
9470
9615
|
|
|
9616
|
+
async function toggleCharacterPin(item) {
|
|
9617
|
+
const updated = await api(`/api/characters/${encodeURIComponent(item.id)}/pin`, {
|
|
9618
|
+
method: "PATCH",
|
|
9619
|
+
body: { isPinned: item.isPinned !== true }
|
|
9620
|
+
});
|
|
9621
|
+
if (loadedAiReferencesWorkId === state.work?.id) {
|
|
9622
|
+
state.characters = upsertEntityCollection(state.characters, updated);
|
|
9623
|
+
renderAiRoleplayCharacterSelect();
|
|
9624
|
+
}
|
|
9625
|
+
characterListPage = 1;
|
|
9626
|
+
await renderCharacters(1);
|
|
9627
|
+
$("#module-content").querySelector(`[data-character-pin="${CSS.escape(String(updated.id))}"]`)?.focus({ preventScroll: true });
|
|
9628
|
+
toast(updated.isPinned ? `已置顶角色“${updated.name}”` : `已取消置顶角色“${updated.name}”`);
|
|
9629
|
+
}
|
|
9630
|
+
|
|
9471
9631
|
async function renderCharacters(page = characterListPage) {
|
|
9472
9632
|
const hasCharacterFilters = characterFilters.raceIds.length > 0
|
|
9473
9633
|
|| characterFilters.organizationIds.length > 0
|
|
@@ -9490,14 +9650,14 @@ async function renderCharacters(page = characterListPage) {
|
|
|
9490
9650
|
[state.races, state.organizations] = [races, organizations];
|
|
9491
9651
|
mountModuleCount(characterPage.total);
|
|
9492
9652
|
const layout = readModuleLayout();
|
|
9493
|
-
const characterActions = (item) => `${characterFavoriteButton(item)}${recordCardEditButton("edit-character", item.id, `角色“${item.name}”`)}`;
|
|
9653
|
+
const characterActions = (item) => `${characterPinButton(item)}${characterFavoriteButton(item)}${recordCardEditButton("edit-character", item.id, `角色“${item.name}”`)}`;
|
|
9494
9654
|
const characterLockBadge = (item) => item.lockedFields.length
|
|
9495
9655
|
? `<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>`
|
|
9496
9656
|
: "";
|
|
9497
9657
|
const characterCards = () => `<div class="card-grid">${pageCharacters.map((item) => {
|
|
9498
9658
|
const details = normalizeCharacterDetails(item.attributes?.details);
|
|
9499
9659
|
return `
|
|
9500
|
-
<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)}">${characterFavoriteButton(item)}${recordCardEditButton("edit-character", item.id, `角色“${item.name}”`)}
|
|
9660
|
+
<article class="record-card character-card preview-record-card has-card-edit has-pin-control" data-open-character="${esc(item.id)}" role="button" tabindex="0" aria-label="查看角色 ${esc(item.name)}">${characterPinButton(item)}${characterFavoriteButton(item)}${recordCardEditButton("edit-character", item.id, `角色“${item.name}”`)}
|
|
9501
9661
|
<div class="character-card-heading">${characterAvatarHtml(item)}<h3>${esc(item.name)}</h3>${entityLifecycleBadge(item.isDead, "已死亡")}${characterLockBadge(item)}</div>
|
|
9502
9662
|
${item.attributes?.identity ? `<p class="character-identity">${esc(item.attributes.identity)}</p>` : ""}
|
|
9503
9663
|
<div class="character-gender"><b>性别</b><span class="pill">${esc(characterGenderLabel(item.gender))}</span></div>
|
|
@@ -9574,6 +9734,17 @@ async function renderCharacters(page = characterListPage) {
|
|
|
9574
9734
|
toast(`角色收藏状态更新失败:${error.message}`, "error");
|
|
9575
9735
|
}
|
|
9576
9736
|
}));
|
|
9737
|
+
$("#module-content").querySelectorAll("[data-character-pin]").forEach((button) => button.addEventListener("click", async () => {
|
|
9738
|
+
const item = pageCharacters.find((candidate) => candidate.id === button.dataset.characterPin);
|
|
9739
|
+
if (!item || !canEditModule("characters")) return;
|
|
9740
|
+
button.disabled = true;
|
|
9741
|
+
try {
|
|
9742
|
+
await toggleCharacterPin(item);
|
|
9743
|
+
} catch (error) {
|
|
9744
|
+
button.disabled = false;
|
|
9745
|
+
toast(`角色置顶状态更新失败:${error.message}`, "error");
|
|
9746
|
+
}
|
|
9747
|
+
}));
|
|
9577
9748
|
const readSelectedValues = (selector) => [...$(selector).querySelectorAll('input[type="checkbox"]:checked')].map((input) => input.value);
|
|
9578
9749
|
$("#character-race-filter").addEventListener("change", async () => {
|
|
9579
9750
|
characterFiltersPanelOpen = true;
|
|
@@ -9728,14 +9899,14 @@ async function renderOrganizations(page = moduleListPages.organizations) {
|
|
|
9728
9899
|
moduleListPages.organizations = pageResult.page;
|
|
9729
9900
|
const layout = readModuleLayout();
|
|
9730
9901
|
const canEditOrganizations = canEditModule("organizations");
|
|
9731
|
-
const organizationActions = (item, { cardControl = false } = {}) => `${recordFavoriteButton("organization", item, { cardControl })}${canEditOrganizations
|
|
9902
|
+
const organizationActions = (item, { cardControl = false } = {}) => `${recordPinButton("organization", item, { cardControl })}${recordFavoriteButton("organization", item, { cardControl })}${canEditOrganizations
|
|
9732
9903
|
? recordCardEditButton("edit-organization", item.id, `组织“${item.name}”`)
|
|
9733
9904
|
: recordHistoryButton("organization", item.id, item.name)}`;
|
|
9734
9905
|
const organizationCardActions = (item) => canEditOrganizations
|
|
9735
9906
|
? organizationActions(item, { cardControl: true })
|
|
9736
9907
|
: `<div class="card-actions">${organizationActions(item)}</div>`;
|
|
9737
9908
|
const organizationCards = () => `<div class="card-grid organization-grid">${pageResult.items.map((item) => `
|
|
9738
|
-
<article class="record-card organization-card preview-record-card${canEditOrganizations ? " has-card-edit has-favorite-control" : ""}" data-open-organization="${esc(item.id)}" role="button" tabindex="0" aria-label="查看组织 ${esc(item.name)}"><small>${item.memberIds.length} 位成员 · ${(item.settingsCount ?? item.settings?.length ?? 0) ? "已填写组织设定" : "暂无组织设定"}</small>
|
|
9909
|
+
<article class="record-card organization-card preview-record-card${canEditOrganizations ? " has-card-edit has-favorite-control has-pin-control" : ""}" data-open-organization="${esc(item.id)}" role="button" tabindex="0" aria-label="查看组织 ${esc(item.name)}"><small>${item.memberIds.length} 位成员 · ${(item.settingsCount ?? item.settings?.length ?? 0) ? "已填写组织设定" : "暂无组织设定"}</small>
|
|
9739
9910
|
<h3>${esc(item.name)}${entityLifecycleBadge(item.isDissolved, "已解散")}</h3><p>${esc(item.description || "尚未填写组织简介")}</p>
|
|
9740
9911
|
<div class="organization-settings">${item.settingsCount ? `<span class="pill">${item.settingsCount} 条组织设定,打开查看详情</span>` : '<span class="pill">暂无组织设定</span>'}</div>
|
|
9741
9912
|
<p class="organization-members">成员:${item.members.length ? item.members.map((member) => esc(member.name)).join("、") : "暂无绑定角色"}</p>
|
|
@@ -9764,6 +9935,10 @@ async function renderOrganizations(page = moduleListPages.organizations) {
|
|
|
9764
9935
|
moduleListPages.organizations = 1;
|
|
9765
9936
|
await renderOrganizations(1);
|
|
9766
9937
|
});
|
|
9938
|
+
bindRecordPinButtons("organization", pageResult.items, async () => {
|
|
9939
|
+
moduleListPages.organizations = 1;
|
|
9940
|
+
await renderOrganizations(1);
|
|
9941
|
+
});
|
|
9767
9942
|
bindEntityHistoryButtons(async () => { await renderOrganizations(pageResult.page); await loadAiReferences(); });
|
|
9768
9943
|
}
|
|
9769
9944
|
|
|
@@ -13476,6 +13651,10 @@ function openSettingEditor(item = null, { readOnly = false } = {}) {
|
|
|
13476
13651
|
const editButton = $("#setting-editor-edit");
|
|
13477
13652
|
editButton.classList.toggle("hidden", !readOnly || !canEditModule("settings"));
|
|
13478
13653
|
editButton.onclick = () => openSettingEditor(settingEditorItem);
|
|
13654
|
+
bindEntityDetailPinButton($("#setting-editor-pin"), "setting", () => viewOnly ? settingEditorItem : null, (updated) => {
|
|
13655
|
+
settingEditorItem = updated;
|
|
13656
|
+
state.settings = upsertEntityCollection(state.settings, updated);
|
|
13657
|
+
});
|
|
13479
13658
|
bindEntityDetailFavoriteButton($("#setting-editor-favorite"), "setting", () => viewOnly ? settingEditorItem : null, (updated) => {
|
|
13480
13659
|
settingEditorItem = updated;
|
|
13481
13660
|
state.settings = upsertEntityCollection(state.settings, updated);
|
|
@@ -14712,6 +14891,13 @@ async function openCharacterEditor(item = null, { readOnly = false } = {}) {
|
|
|
14712
14891
|
const editButton = $("#character-editor-edit");
|
|
14713
14892
|
editButton.classList.toggle("hidden", !readOnly || !canEditModule("characters"));
|
|
14714
14893
|
editButton.onclick = () => void openCharacterEditor(characterEditorItem);
|
|
14894
|
+
bindEntityDetailPinButton($("#character-editor-pin"), "character", () => viewOnly ? characterEditorItem : null, (updated) => {
|
|
14895
|
+
characterEditorItem = updated;
|
|
14896
|
+
if (loadedAiReferencesWorkId === state.work?.id) {
|
|
14897
|
+
state.characters = upsertEntityCollection(state.characters, updated);
|
|
14898
|
+
renderAiRoleplayCharacterSelect();
|
|
14899
|
+
}
|
|
14900
|
+
});
|
|
14715
14901
|
bindEntityDetailFavoriteButton($("#character-editor-favorite"), "character", () => viewOnly ? characterEditorItem : null, (updated) => {
|
|
14716
14902
|
characterEditorItem = updated;
|
|
14717
14903
|
if (loadedAiReferencesWorkId === state.work?.id) {
|
|
@@ -14905,6 +15091,10 @@ async function openKnowledgeEditor(kind, item, { readOnly = false } = {}) {
|
|
|
14905
15091
|
editButton.textContent = `编辑${label}`;
|
|
14906
15092
|
editButton.classList.toggle("hidden", !readOnly || !canEditModule(module));
|
|
14907
15093
|
editButton.onclick = () => void openKnowledgeEditor(kind, knowledgeEditorItem);
|
|
15094
|
+
bindEntityDetailPinButton($("#knowledge-editor-pin"), "organization", () => !isRace && viewOnly ? knowledgeEditorItem : null, (updated) => {
|
|
15095
|
+
knowledgeEditorItem = updated;
|
|
15096
|
+
state.organizations = upsertEntityCollection(state.organizations, updated);
|
|
15097
|
+
});
|
|
14908
15098
|
bindEntityDetailFavoriteButton($("#knowledge-editor-favorite"), "organization", () => !isRace && viewOnly ? knowledgeEditorItem : null, (updated) => {
|
|
14909
15099
|
knowledgeEditorItem = updated;
|
|
14910
15100
|
state.organizations = upsertEntityCollection(state.organizations, updated);
|
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=20260816-task-scope-volume-collapse-v2&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v3&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=galaxy-compact-controls-v2&feature=galaxy-motion-mode-v2&feature=chapter-search-replace-v3&feature=task-auto-run-ring-center-v3&feature=character-relationship-delete-v1&feature=ai-assistant-workspace-v2&feature=mobile-module-tab-position-v1&feature=volume-detail-icon-v1&feature=editor-actions-flow-v1&feature=reader-controls-subpanel-v1&feature=reader-focus-ring-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v2&feature=ai-composer-square-controls-v2&feature=annotation-line-counts-v1&feature=line-number-gutter-fill-v1&feature=ai-relationship-roleplay-v1&feature=ai-model-picker-v1&feature=markdown-word-count-five-digit-v2&feature=ai-stream-character-count-stable-v1&feature=annotation-marker-offset-v1&feature=mobile-ai-entry-hidden-v1&feature=phone-client-entry-v1&feature=ai-stream-idle-timeout-v1&feature=ai-user-message-width-v2&feature=ai-chat-image-attachments-v9&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=toast-click-dismiss-v3&feature=character-avatar-v6&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-token-quota-positive-v5&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-stream-connection-seconds-v1&feature=ai-token-usage-estimated-price-v1&feature=record-favorites-v1&feature=entity-detail-favorites-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v1&feature=admin-account-identity-v2&feature=task-detail-failure-orange-v1">
|
|
13
|
+
<link rel="stylesheet" href="/styles.css?v=20260816-task-scope-volume-collapse-v2&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v3&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=galaxy-compact-controls-v2&feature=galaxy-motion-mode-v2&feature=chapter-search-replace-v3&feature=task-auto-run-ring-center-v3&feature=character-relationship-delete-v1&feature=ai-assistant-workspace-v2&feature=mobile-module-tab-position-v1&feature=volume-detail-icon-v1&feature=editor-actions-flow-v1&feature=reader-controls-subpanel-v1&feature=reader-focus-ring-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v2&feature=ai-composer-square-controls-v2&feature=annotation-line-counts-v1&feature=line-number-gutter-fill-v1&feature=ai-relationship-roleplay-v1&feature=ai-model-picker-v1&feature=markdown-word-count-five-digit-v2&feature=ai-stream-character-count-stable-v1&feature=annotation-marker-offset-v1&feature=mobile-ai-entry-hidden-v1&feature=phone-client-entry-v1&feature=ai-stream-idle-timeout-v1&feature=ai-user-message-width-v2&feature=ai-chat-image-attachments-v9&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=toast-click-dismiss-v3&feature=character-avatar-v6&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-token-quota-positive-v5&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-stream-connection-seconds-v1&feature=ai-token-usage-estimated-price-v1&feature=record-favorites-v1&feature=entity-detail-favorites-v1&feature=entity-pin-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v1&feature=admin-account-identity-v2&feature=task-detail-failure-orange-v1&feature=character-card-header-alignment-v3&feature=character-card-title-fit-v2">
|
|
14
14
|
</head>
|
|
15
15
|
<body class="auth-pending">
|
|
16
16
|
<section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
|
|
@@ -658,6 +658,7 @@
|
|
|
658
658
|
</div>
|
|
659
659
|
</div>
|
|
660
660
|
<div class="entity-editor-mode-actions">
|
|
661
|
+
<button id="setting-editor-pin" class="entity-detail-pin-button hidden" type="button" aria-pressed="false"></button>
|
|
661
662
|
<button id="setting-editor-favorite" class="entity-detail-favorite-button hidden" type="button" aria-pressed="false"></button>
|
|
662
663
|
<button id="setting-editor-edit" class="ghost-button hidden" type="button">编辑设定</button>
|
|
663
664
|
<button id="setting-editor-confirm" class="ghost-button hidden" type="button">确认候选</button>
|
|
@@ -685,6 +686,7 @@
|
|
|
685
686
|
<div class="character-editor-title-row"><h1 id="character-editor-title">新建角色</h1><span id="character-editor-version" class="character-version-badge">新档案</span><span id="character-editor-readonly-badge" class="entity-editor-readonly-badge hidden">只读模式</span></div>
|
|
686
687
|
</div>
|
|
687
688
|
<div class="character-editor-header-actions">
|
|
689
|
+
<button id="character-editor-pin" class="entity-detail-pin-button hidden" type="button" aria-pressed="false"></button>
|
|
688
690
|
<button id="character-editor-favorite" class="entity-detail-favorite-button hidden" type="button" aria-pressed="false"></button>
|
|
689
691
|
<button id="character-editor-edit" class="primary-button hidden" type="button">编辑角色</button>
|
|
690
692
|
<button id="character-history-button" class="ghost-button" type="button" aria-controls="character-history-panel" aria-expanded="false">版本历史</button>
|
|
@@ -721,6 +723,7 @@
|
|
|
721
723
|
</div>
|
|
722
724
|
<div class="character-editor-header-actions">
|
|
723
725
|
<span id="knowledge-editor-header-note" class="knowledge-editor-header-note">独立资料页</span>
|
|
726
|
+
<button id="knowledge-editor-pin" class="entity-detail-pin-button hidden" type="button" aria-pressed="false"></button>
|
|
724
727
|
<button id="knowledge-editor-favorite" class="entity-detail-favorite-button hidden" type="button" aria-pressed="false"></button>
|
|
725
728
|
<button id="knowledge-editor-edit" class="primary-button hidden" type="button">编辑档案</button>
|
|
726
729
|
<button id="knowledge-editor-history" class="ghost-button hidden" type="button">版本历史</button>
|
|
@@ -1258,6 +1261,6 @@
|
|
|
1258
1261
|
<div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
|
|
1259
1262
|
<script id="vditorIconScript" src="/vendor/vditor/dist/js/icons/ant.js?v=3.11.2"></script>
|
|
1260
1263
|
<script src="/vendor/vditor/dist/index.min.js?v=3.11.2"></script>
|
|
1261
|
-
<script type="module" src="/app.js?v=20260816-extended-thinking-effort-v1&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v2&feature=analysis-task-queue-refresh-v1&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=ai-session-id-copy-v2&feature=galaxy-motion-mode-v3&feature=galaxy-edge-label-threshold-v1&feature=calculate-time-tool-v2&feature=analysis-task-expired-toast-v1&feature=global-replace-volume-v1&feature=chapter-search-replace-v1&feature=chapter-save-toast-v1&feature=character-relationship-delete-v1&feature=character-relationship-group-v1&feature=analysis-task-stability-delay-v1&feature=ai-assistant-workspace-v2&feature=volume-detail-icon-v1&feature=volume-story-order-v1&feature=reader-manual-chapter-navigation-v1&feature=ai-message-reference-badges-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v3&feature=annotation-permissions-v1&feature=annotation-line-counts-v1&feature=ai-relationship-roleplay-v1&feature=ai-roleplay-user-character-visibility-v1&feature=ai-roleplay-story-recall-v1&feature=ai-model-picker-v1&feature=ai-fork-model-unlock-v1&feature=context-percent-format-v1&feature=annotation-precise-locate-v1&feature=markdown-word-count-stable-v1&feature=ai-stream-character-count-stable-v2&feature=ai-process-empty-intermediate-v1&feature=ai-feed-scroll-follow-v1&feature=ai-message-retry-v1&feature=ai-stream-idle-timeout-v2&feature=ai-config-delete-v1&feature=ai-provider-protocol-options-v1&feature=ai-provider-thinking-type-v1&feature=ai-chat-image-attachments-v8&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=ai-image-conversation-model-lock-v1&feature=toast-click-dismiss-v2&feature=system-restart-dialog-delay-v1&feature=character-avatar-v6&feature=character-death-position-v1&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-usage-pricing-label-v1&feature=ai-usage-token-breakdown-v1&feature=ai-usage-pricing-cache-v2&feature=ai-usage-pricing-manual-refresh-v1&feature=ai-monthly-token-quota-v1&feature=ai-provider-token-quota-v1&feature=ai-token-quota-positive-v6&feature=ai-model-thinking-label-v1&feature=ai-model-picker-focus-v1&feature=ai-provider-model-import-v1&feature=ai-assistant-brain-icon-v1&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-token-usage-raw-input-v1&feature=ai-stream-connection-seconds-v1&feature=phone-client-entry-v1&feature=ai-usage-pricing-cache-v1&feature=ai-model-thinking-label-v3&feature=ai-roleplay-speaker-label-v1&feature=toast-modal-host-v1&feature=api-key-copy-existing-v1&feature=character-favorite-v1&feature=record-favorites-v1&feature=ai-token-usage-estimated-price-v1&feature=entity-detail-favorites-v1&feature=roleplay-favorite-label-v1&feature=ai-roleplay-knowledge-tools-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v2&feature=admin-account-identity-v2&feature=ai-provider-analysis-timeout-v1&feature=task-detail-failure-orange-v1"></script>
|
|
1264
|
+
<script type="module" src="/app.js?v=20260816-extended-thinking-effort-v1&feature=ai-tool-call-copy-feedback-v2&feature=ai-send-control-v2&feature=analysis-task-queue-refresh-v1&feature=character-gender-v1&feature=character-filter-state-v1&feature=relationship-canvas-scale-v1&feature=relationship-table-scroll-v1&feature=ai-session-id-copy-v2&feature=galaxy-motion-mode-v3&feature=galaxy-edge-label-threshold-v1&feature=calculate-time-tool-v2&feature=analysis-task-expired-toast-v1&feature=global-replace-volume-v1&feature=chapter-search-replace-v1&feature=chapter-save-toast-v1&feature=character-relationship-delete-v1&feature=character-relationship-group-v1&feature=analysis-task-stability-delay-v1&feature=ai-assistant-workspace-v2&feature=volume-detail-icon-v1&feature=volume-story-order-v1&feature=reader-manual-chapter-navigation-v1&feature=ai-message-reference-badges-v1&feature=ai-roleplay-message-reference-v1&feature=ai-message-actions-v1&feature=assistant-responsive-navigation-v3&feature=annotation-permissions-v1&feature=annotation-line-counts-v1&feature=ai-relationship-roleplay-v1&feature=ai-roleplay-user-character-visibility-v1&feature=ai-roleplay-story-recall-v1&feature=ai-model-picker-v1&feature=ai-fork-model-unlock-v1&feature=context-percent-format-v1&feature=annotation-precise-locate-v1&feature=markdown-word-count-stable-v1&feature=ai-stream-character-count-stable-v2&feature=ai-process-empty-intermediate-v1&feature=ai-feed-scroll-follow-v1&feature=ai-message-retry-v1&feature=ai-stream-idle-timeout-v2&feature=ai-config-delete-v1&feature=ai-provider-protocol-options-v1&feature=ai-provider-thinking-type-v1&feature=ai-chat-image-attachments-v8&feature=ai-message-image-preview-v1&feature=ai-thinking-scroll-v2&feature=ai-image-conversation-model-lock-v1&feature=toast-click-dismiss-v2&feature=system-restart-dialog-delay-v1&feature=character-avatar-v6&feature=character-death-position-v1&feature=admin-ai-conversations-v1&feature=ai-usage-pricing-v1&feature=ai-usage-pricing-badge-v2&feature=ai-usage-pricing-label-v1&feature=ai-usage-token-breakdown-v1&feature=ai-usage-pricing-cache-v2&feature=ai-usage-pricing-manual-refresh-v1&feature=ai-monthly-token-quota-v1&feature=ai-provider-token-quota-v1&feature=ai-token-quota-positive-v6&feature=ai-model-thinking-label-v1&feature=ai-model-picker-focus-v1&feature=ai-provider-model-import-v1&feature=ai-assistant-brain-icon-v1&feature=ai-token-usage-details-v1&feature=ai-token-usage-details-centered-v1&feature=ai-token-usage-raw-input-v1&feature=ai-stream-connection-seconds-v1&feature=phone-client-entry-v1&feature=ai-usage-pricing-cache-v1&feature=ai-model-thinking-label-v3&feature=ai-roleplay-speaker-label-v1&feature=toast-modal-host-v1&feature=api-key-copy-existing-v1&feature=character-favorite-v1&feature=record-favorites-v1&feature=ai-token-usage-estimated-price-v1&feature=entity-detail-favorites-v1&feature=entity-pin-v1&feature=entity-pin-icon-v1&feature=roleplay-favorite-label-v1&feature=ai-roleplay-knowledge-tools-v1&feature=character-persona-summary-v1&feature=ai-roleplay-scene-turn-v2&feature=admin-account-identity-v2&feature=ai-provider-analysis-timeout-v1&feature=task-detail-failure-orange-v1"></script>
|
|
1262
1265
|
</body>
|
|
1263
1266
|
</html>
|
package/dist/public/styles.css
CHANGED
|
@@ -1517,9 +1517,16 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1517
1517
|
.record-card { position: relative; border: 1px solid var(--line); background: var(--surface-soft); padding: 18px; min-height: 130px; border-radius: 4px; }
|
|
1518
1518
|
.record-card.has-card-edit h3 { padding-right: 34px; }
|
|
1519
1519
|
.record-card.has-favorite-control h3 { padding-right: 70px; }
|
|
1520
|
+
.record-card.has-pin-control h3 { padding-right: 106px; }
|
|
1520
1521
|
.record-card-edit { position: absolute; top: 12px; right: 12px; display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: transparent; color: var(--muted); }
|
|
1521
1522
|
.record-card-edit:hover, .record-card-edit:focus-visible { border-color: var(--accent); color: var(--accent-dark); outline: none; }
|
|
1522
1523
|
.record-card-edit svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.7; }
|
|
1524
|
+
.character-pin-button { position: absolute; top: 12px; right: 84px; display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: transparent; color: var(--muted); }
|
|
1525
|
+
.character-pin-button:hover, .character-pin-button:focus-visible { border-color: var(--accent); color: var(--accent-dark); outline: none; }
|
|
1526
|
+
.character-pin-button.is-pinned { border-color: color-mix(in srgb, var(--accent) 58%, var(--line)); background: color-mix(in srgb, var(--accent) 10%, var(--surface)); color: var(--accent-dark); }
|
|
1527
|
+
.character-pin-button:disabled { cursor: not-allowed; opacity: .5; }
|
|
1528
|
+
.character-pin-button svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.6; }
|
|
1529
|
+
.character-pin-button.is-pinned svg { fill: currentColor; }
|
|
1523
1530
|
.character-favorite-button { position: absolute; top: 12px; right: 48px; display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: transparent; color: var(--muted); }
|
|
1524
1531
|
.character-favorite-button:hover, .character-favorite-button:focus-visible { border-color: var(--accent); color: var(--accent-dark); outline: none; }
|
|
1525
1532
|
.character-favorite-button.is-favorite { border-color: color-mix(in srgb, var(--accent) 58%, var(--line)); background: color-mix(in srgb, var(--accent) 10%, var(--surface)); color: var(--accent-dark); }
|
|
@@ -1533,12 +1540,25 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1533
1540
|
.record-favorite-button:disabled { cursor: not-allowed; opacity: .5; }
|
|
1534
1541
|
.record-favorite-button svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.6; }
|
|
1535
1542
|
.record-favorite-button.is-favorite svg { fill: currentColor; }
|
|
1543
|
+
.record-pin-button { display: inline-grid; flex: 0 0 auto; place-items: center; width: 28px; height: 28px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: transparent; color: var(--muted); }
|
|
1544
|
+
.record-pin-button.is-card-control { position: absolute; top: 12px; right: 84px; }
|
|
1545
|
+
.record-pin-button:hover, .record-pin-button:focus-visible { border-color: var(--accent); color: var(--accent-dark); outline: none; }
|
|
1546
|
+
.record-pin-button.is-pinned { border-color: color-mix(in srgb, var(--accent) 58%, var(--line)); background: color-mix(in srgb, var(--accent) 10%, var(--surface)); color: var(--accent-dark); }
|
|
1547
|
+
.record-pin-button:disabled { cursor: not-allowed; opacity: .5; }
|
|
1548
|
+
.record-pin-button svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.6; }
|
|
1549
|
+
.record-pin-button.is-pinned svg { fill: currentColor; }
|
|
1536
1550
|
.entity-detail-favorite-button { display: inline-grid; flex: 0 0 36px; place-items: center; width: 36px; min-width: 36px; height: 36px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: transparent; color: var(--muted); }
|
|
1537
1551
|
.entity-detail-favorite-button:hover, .entity-detail-favorite-button:focus-visible { border-color: var(--accent); color: var(--accent-dark); outline: none; }
|
|
1538
1552
|
.entity-detail-favorite-button.is-favorite { border-color: color-mix(in srgb, var(--accent) 58%, var(--line)); background: color-mix(in srgb, var(--accent) 10%, var(--surface)); color: var(--accent-dark); }
|
|
1539
1553
|
.entity-detail-favorite-button:disabled { cursor: not-allowed; opacity: .5; }
|
|
1540
1554
|
.entity-detail-favorite-button svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.6; }
|
|
1541
1555
|
.entity-detail-favorite-button.is-favorite svg { fill: currentColor; }
|
|
1556
|
+
.entity-detail-pin-button { display: inline-grid; flex: 0 0 36px; place-items: center; width: 36px; min-width: 36px; height: 36px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: transparent; color: var(--muted); }
|
|
1557
|
+
.entity-detail-pin-button:hover, .entity-detail-pin-button:focus-visible { border-color: var(--accent); color: var(--accent-dark); outline: none; }
|
|
1558
|
+
.entity-detail-pin-button.is-pinned { border-color: color-mix(in srgb, var(--accent) 58%, var(--line)); background: color-mix(in srgb, var(--accent) 10%, var(--surface)); color: var(--accent-dark); }
|
|
1559
|
+
.entity-detail-pin-button:disabled { cursor: not-allowed; opacity: .5; }
|
|
1560
|
+
.entity-detail-pin-button svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.6; }
|
|
1561
|
+
.entity-detail-pin-button.is-pinned svg { fill: currentColor; }
|
|
1542
1562
|
.record-card h3 { font-size: 16px; margin: 5px 0 10px; font-weight: 600; }
|
|
1543
1563
|
.record-card p { color: var(--muted); font-size: 12px; line-height: 1.55; margin: 0; white-space: pre-wrap; }
|
|
1544
1564
|
.record-card small { color: var(--accent); font-size: 9px; letter-spacing: .1em; text-transform: uppercase; }
|
|
@@ -1569,15 +1589,28 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
1569
1589
|
.module-row-span { grid-column: 1 / -1; }
|
|
1570
1590
|
.setting-row .card-actions, .module-row .card-actions { margin-top: 0; flex-wrap: wrap; justify-content: flex-end; }
|
|
1571
1591
|
.module-row .card-actions > .record-card-edit { position: static; }
|
|
1572
|
-
.character-card { cursor: pointer; }
|
|
1592
|
+
.character-card { cursor: pointer; container: character-card / inline-size; }
|
|
1573
1593
|
.character-row { grid-template-columns: minmax(140px, .28fr) minmax(0, 1fr) auto; }
|
|
1574
1594
|
.character-card-heading { display: flex; min-width: 0; align-items: center; gap: 7px; }
|
|
1575
1595
|
.character-card-heading .character-avatar { width: 32px; height: 32px; font-size: 12px; }
|
|
1576
|
-
.character-card-heading h3 { min-width: 0; }
|
|
1596
|
+
.character-card-heading h3 { flex: 1 1 0; min-width: 0; overflow-wrap: anywhere; }
|
|
1577
1597
|
.record-card.has-card-edit .character-card-heading { padding-right: 70px; }
|
|
1598
|
+
.record-card.has-card-edit.has-pin-control .character-card-heading { padding-right: 106px; }
|
|
1578
1599
|
.record-card.has-card-edit .character-card-heading h3 { padding-right: 0; }
|
|
1600
|
+
.character-card.has-card-edit > .character-favorite-button, .character-card.has-card-edit > .record-card-edit { top: 21px; }
|
|
1579
1601
|
.module-row .character-favorite-button { position: static; }
|
|
1602
|
+
.module-row .character-pin-button { position: static; }
|
|
1580
1603
|
.module-row .record-favorite-button { position: static; }
|
|
1604
|
+
.module-row .record-pin-button { position: static; }
|
|
1605
|
+
@container character-card (max-width: 320px) {
|
|
1606
|
+
.record-card.has-card-edit.has-pin-control .character-card-heading { padding-top: 42px; padding-right: 0; }
|
|
1607
|
+
}
|
|
1608
|
+
@media (max-width: 560px) {
|
|
1609
|
+
.character-card-heading h3 { font-size: 15px; }
|
|
1610
|
+
}
|
|
1611
|
+
@media (max-width: 420px) {
|
|
1612
|
+
.character-card-heading h3 { font-size: 14px; }
|
|
1613
|
+
}
|
|
1581
1614
|
.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; }
|
|
1582
1615
|
.entity-lifecycle-badge { display: inline-flex; align-items: center; min-height: 20px; margin-left: 7px; padding: 2px 7px; border: 1px solid color-mix(in srgb, var(--accent) 58%, var(--line)); border-radius: 10px; background: color-mix(in srgb, var(--accent) 13%, var(--surface)); color: var(--accent-dark); font-size: 9px; font-weight: 650; line-height: 1; vertical-align: middle; white-space: nowrap; }
|
|
1583
1616
|
.character-lock-badge svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.8; }
|
|
@@ -4066,6 +4099,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
4066
4099
|
.module-content { padding: 18px 0 56px; }
|
|
4067
4100
|
.card-grid, .provider-card-grid { grid-template-columns: minmax(0, 1fr); }
|
|
4068
4101
|
.record-card { padding: 15px; }
|
|
4102
|
+
.character-card.has-card-edit > .character-favorite-button, .character-card.has-card-edit > .record-card-edit { top: 12px; }
|
|
4069
4103
|
.card-actions, .task-row-actions, .module-header-actions { gap: 8px; }
|
|
4070
4104
|
.card-actions button, .task-row-actions button { flex: 1 1 auto; min-height: 38px; }
|
|
4071
4105
|
.setting-row, .module-row { padding: 13px; }
|
|
@@ -4152,8 +4186,8 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
4152
4186
|
.entity-editor-header, .character-editor-header { grid-template-columns: auto minmax(0, 1fr); gap: 8px; padding: 12px var(--mobile-gutter); }
|
|
4153
4187
|
.entity-editor-mode-actions, .character-editor-header-actions { grid-column: 1 / -1; justify-content: stretch; flex-wrap: wrap; }
|
|
4154
4188
|
.entity-editor-mode-actions > button, .character-editor-header-actions > button { flex: 1 1 auto; }
|
|
4155
|
-
.entity-editor-mode-actions > .entity-detail-favorite-button, .character-editor-header-actions > .entity-detail-favorite-button { flex: 0 0 40px; width: 40px; min-width: 40px; height: 40px; }
|
|
4156
|
-
.dialog-context-actions .entity-detail-favorite-button { flex-basis: 40px; width: 40px; min-width: 40px; height: 40px; }
|
|
4189
|
+
.entity-editor-mode-actions > .entity-detail-favorite-button, .entity-editor-mode-actions > .entity-detail-pin-button, .character-editor-header-actions > .entity-detail-favorite-button, .character-editor-header-actions > .entity-detail-pin-button { flex: 0 0 40px; width: 40px; min-width: 40px; height: 40px; }
|
|
4190
|
+
.dialog-context-actions .entity-detail-favorite-button, .dialog-context-actions .entity-detail-pin-button { flex-basis: 40px; width: 40px; min-width: 40px; height: 40px; }
|
|
4157
4191
|
.setting-editor-header { min-height: 0; }
|
|
4158
4192
|
.setting-editor-content { padding: 12px; }
|
|
4159
4193
|
.setting-markdown-field { height: 68dvh; }
|