@musnows/scriverse 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-conversation-export.js +3 -1
- package/dist/ai-conversation-export.js.map +1 -1
- package/dist/ai.js +50 -4
- package/dist/ai.js.map +1 -1
- package/dist/app.js +9 -4
- package/dist/app.js.map +1 -1
- package/dist/database.js +26 -2
- package/dist/database.js.map +1 -1
- package/dist/public/app.js +165 -19
- package/dist/public/index.html +10 -5
- package/dist/public/relationship-graph.js +11 -3
- package/dist/public/styles.css +14 -4
- package/dist/store.js +40 -6
- package/dist/store.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/public/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildRelationshipGraph, createGalaxyRenderer, normalizeGalaxyFrameRate, normalizeGalaxyMotionMode, renderRelationshipMindMap } from "/relationship-graph.js?v=20260817-relationship-canvas-scale-v1&feature=galaxy-motion-mode-v3";
|
|
1
|
+
import { buildRelationshipGraph, createGalaxyRenderer, normalizeGalaxyFrameRate, normalizeGalaxyMotionMode, renderRelationshipMindMap } from "/relationship-graph.js?v=20260817-relationship-canvas-scale-v1&feature=galaxy-motion-mode-v3&feature=galaxy-edge-label-threshold-v1";
|
|
2
2
|
import { collapseExcessBlankLines, formatDateTime, normalizeParagraphSpacing } from "/text-formatting.js?v=20260713-saved-at-seconds";
|
|
3
3
|
import { renderMarkdown } from "/markdown.js?v=20260731-no-external-images-v1";
|
|
4
4
|
import { findAiMention, listAiMentionOptions, mergeAiReferenceScope, userMessageMentionNames } from "/ai-mentions.js?v=20260811-user-message-mentions-v1";
|
|
@@ -178,6 +178,7 @@ const state = {
|
|
|
178
178
|
aiConversationModelId: null,
|
|
179
179
|
aiConversations: [],
|
|
180
180
|
aiRoleplayCharacter: null,
|
|
181
|
+
aiRoleplayUserCharacter: null,
|
|
181
182
|
aiLastMessageAt: null,
|
|
182
183
|
settings: [],
|
|
183
184
|
dirty: false,
|
|
@@ -2040,6 +2041,7 @@ function createAiChatTabState(input = {}) {
|
|
|
2040
2041
|
taskType: input.taskType ?? "chat",
|
|
2041
2042
|
contextScope: input.contextScope ?? { type: "none" },
|
|
2042
2043
|
roleplayCharacter: input.roleplayCharacter ?? null,
|
|
2044
|
+
roleplayUserCharacter: input.roleplayUserCharacter ?? null,
|
|
2043
2045
|
citations: input.citations ?? [],
|
|
2044
2046
|
references: input.references ?? [],
|
|
2045
2047
|
composer: input.composer ?? { text: "", citations: [], references: [] },
|
|
@@ -2067,6 +2069,7 @@ function persistActiveAiChatTab() {
|
|
|
2067
2069
|
tab.taskType = state.aiTaskType;
|
|
2068
2070
|
tab.contextScope = { ...state.aiContextScope };
|
|
2069
2071
|
tab.roleplayCharacter = state.aiRoleplayCharacter ? { ...state.aiRoleplayCharacter } : null;
|
|
2072
|
+
tab.roleplayUserCharacter = state.aiRoleplayUserCharacter ? { ...state.aiRoleplayUserCharacter } : null;
|
|
2070
2073
|
setAiChatTabComposerSnapshot(tab, captureAiPromptComposer());
|
|
2071
2074
|
tab.contextUsage = latestAiContextUsage;
|
|
2072
2075
|
tab.contextWarning = !$("#ai-context-warning").classList.contains("hidden");
|
|
@@ -2099,9 +2102,10 @@ function applyAiChatTabState(tab) {
|
|
|
2099
2102
|
$("#ai-conversation-title").textContent = tab.title || "新对话";
|
|
2100
2103
|
applyAiConversationTaskType(tab.taskType);
|
|
2101
2104
|
applyAiConversationContextScope(tab.contextScope);
|
|
2102
|
-
applyAiRoleplayCharacter(tab.roleplayCharacter);
|
|
2105
|
+
applyAiRoleplayCharacter(tab.roleplayCharacter, tab.roleplayUserCharacter);
|
|
2103
2106
|
const selectedModelId = tab.modelId ?? tab.selectedModelId;
|
|
2104
2107
|
if (selectedModelId && state.models.some((model) => model.id === selectedModelId)) $("#ai-model").value = selectedModelId;
|
|
2108
|
+
syncAiModelPicker();
|
|
2105
2109
|
setAiPromptText(tab.composer.text);
|
|
2106
2110
|
renderAiCitations();
|
|
2107
2111
|
renderAiReferences();
|
|
@@ -2340,7 +2344,7 @@ function resetAiChatTabs() {
|
|
|
2340
2344
|
panels.replaceChildren();
|
|
2341
2345
|
aiChatTabManager.reset();
|
|
2342
2346
|
const tab = createAiChatTabState();
|
|
2343
|
-
resetAiFeed(tab.feed, tab.roleplayCharacter);
|
|
2347
|
+
resetAiFeed(tab.feed, tab.roleplayCharacter, tab.roleplayUserCharacter);
|
|
2344
2348
|
activateAiChatTab(tab.id, { persistCurrent: false, force: true });
|
|
2345
2349
|
}
|
|
2346
2350
|
|
|
@@ -2385,13 +2389,18 @@ function updateMessageCreatedAt(message, createdAt) {
|
|
|
2385
2389
|
}
|
|
2386
2390
|
}
|
|
2387
2391
|
|
|
2388
|
-
function resetAiFeed(
|
|
2392
|
+
function resetAiFeed(
|
|
2393
|
+
feed = $("#ai-feed"),
|
|
2394
|
+
roleplayCharacter = state.aiRoleplayCharacter,
|
|
2395
|
+
roleplayUserCharacter = state.aiRoleplayUserCharacter
|
|
2396
|
+
) {
|
|
2389
2397
|
const tab = aiChatTabManager.get(feed?.dataset.aiTabId);
|
|
2390
2398
|
if (tab) tab.lastMessageAt = null;
|
|
2391
2399
|
if (isActiveAiChatTab(tab)) state.aiLastMessageAt = null;
|
|
2392
2400
|
const roleplayName = roleplayCharacter?.name;
|
|
2401
|
+
const roleplayUserName = roleplayUserCharacter?.name;
|
|
2393
2402
|
feed.innerHTML = roleplayName
|
|
2394
|
-
? `<div class="assistant-message"><span class="message-heading"><span>${esc(roleplayName)}</span></span><div class="message-body"><p>正在扮演 ${esc(roleplayName)}
|
|
2403
|
+
? `<div class="assistant-message"><span class="message-heading"><span>${esc(roleplayName)}</span></span><div class="message-body"><p>正在扮演 ${esc(roleplayName)}。${roleplayUserName ? `你将以 ${esc(roleplayUserName)} 的身份与我互动。` : "我只能通过角色卡和与自己有关的记忆回答。"}</p></div></div>`
|
|
2395
2404
|
: '<div class="assistant-message"><span class="message-heading"><span>助手</span></span><div class="message-body"><p>选择章节和模型后,可以问答、续写或校对。所有引用都基于已保存正文。</p></div></div>';
|
|
2396
2405
|
}
|
|
2397
2406
|
|
|
@@ -2400,6 +2409,10 @@ function aiAssistantLabel(suffix = "", roleplayCharacter = state.aiRoleplayChara
|
|
|
2400
2409
|
return suffix ? `${name} · ${suffix}` : name;
|
|
2401
2410
|
}
|
|
2402
2411
|
|
|
2412
|
+
function aiUserLabel(roleplayUserCharacter = state.aiRoleplayUserCharacter) {
|
|
2413
|
+
return roleplayUserCharacter?.name || "作者";
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2403
2416
|
function createAiContextCompactionDivider({ kind = "conversation", ariaLabel = "当前上下文已压缩", title = "" } = {}) {
|
|
2404
2417
|
const divider = document.createElement("div");
|
|
2405
2418
|
divider.className = "ai-context-compaction-divider";
|
|
@@ -2901,6 +2914,7 @@ function aiConversationHistoryMeta(conversation) {
|
|
|
2901
2914
|
aiConversationContextScopeLabel(conversation.contextScope)
|
|
2902
2915
|
];
|
|
2903
2916
|
if (conversation.roleplayCharacter?.name) parts.push(`扮演 ${conversation.roleplayCharacter.name}`);
|
|
2917
|
+
if (conversation.roleplayUserCharacter?.name) parts.push(`用户扮演 ${conversation.roleplayUserCharacter.name}`);
|
|
2904
2918
|
parts.push(formatDateTime(conversation.updatedAt));
|
|
2905
2919
|
return parts.join(" · ");
|
|
2906
2920
|
}
|
|
@@ -3117,12 +3131,13 @@ function applyConversationToAiChatTab(tab, conversation) {
|
|
|
3117
3131
|
tab.taskType = conversation.taskType ?? "chat";
|
|
3118
3132
|
tab.contextScope = conversation.contextScope ?? { type: "none" };
|
|
3119
3133
|
tab.roleplayCharacter = conversation.roleplayCharacter ?? null;
|
|
3134
|
+
tab.roleplayUserCharacter = conversation.roleplayUserCharacter ?? null;
|
|
3120
3135
|
tab.citations = [];
|
|
3121
3136
|
tab.references = [];
|
|
3122
3137
|
tab.composer = { text: "", citations: [], references: [] };
|
|
3123
3138
|
tab.contextUsage = null;
|
|
3124
3139
|
tab.contextWarning = conversation.contextWarningPending === true;
|
|
3125
|
-
resetAiFeed(tab.feed, tab.roleplayCharacter);
|
|
3140
|
+
resetAiFeed(tab.feed, tab.roleplayCharacter, tab.roleplayUserCharacter);
|
|
3126
3141
|
for (const message of conversation.messages ?? []) {
|
|
3127
3142
|
appendMessage(message.role, message.content, message.citations, message.createdAt, message.metadata, message.id, { tab });
|
|
3128
3143
|
}
|
|
@@ -3197,10 +3212,65 @@ function renderAiRoleplayCharacterSelect() {
|
|
|
3197
3212
|
? aiConversationOptionLockedMessage
|
|
3198
3213
|
: "为当前对话选择角色卡;角色扮演时 Agent 只能查询与该角色自身有关的记忆"
|
|
3199
3214
|
: "当前账户没有角色模块读取权限";
|
|
3215
|
+
renderAiRoleplayUserCharacterSelect();
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
function renderAiRoleplayUserCharacterSelect() {
|
|
3219
|
+
const select = $("#ai-roleplay-user-character");
|
|
3220
|
+
const selectedId = String(state.aiRoleplayUserCharacter?.id ?? "");
|
|
3221
|
+
const aiCharacterId = String(state.aiRoleplayCharacter?.id ?? "");
|
|
3222
|
+
const availableCharacters = state.characters.filter((character) => (
|
|
3223
|
+
!character.mergedIntoCharacterId && String(character.id) !== aiCharacterId
|
|
3224
|
+
));
|
|
3225
|
+
const options = [{ id: "", name: "选择我的角色(可选)" }, ...availableCharacters.map((character) => ({
|
|
3226
|
+
id: String(character.id),
|
|
3227
|
+
name: `${String(character.name)}${character.isDead ? "(已死亡)" : ""}`
|
|
3228
|
+
}))];
|
|
3229
|
+
if (selectedId && !options.some((option) => option.id === selectedId)) {
|
|
3230
|
+
options.push({ id: selectedId, name: String(state.aiRoleplayUserCharacter.name) });
|
|
3231
|
+
}
|
|
3232
|
+
select.replaceChildren(...options.map((option) => {
|
|
3233
|
+
const element = document.createElement("option");
|
|
3234
|
+
element.value = option.id;
|
|
3235
|
+
element.textContent = option.name;
|
|
3236
|
+
return element;
|
|
3237
|
+
}));
|
|
3238
|
+
select.value = selectedId;
|
|
3239
|
+
const canSelectCharacter = Boolean(state.work)
|
|
3240
|
+
&& canReadModule("characters")
|
|
3241
|
+
&& canWritePermissionModule(state.work, "ai-chat");
|
|
3242
|
+
select.disabled = aiInteractionBusy() || !canSelectCharacter || !state.aiRoleplayCharacter;
|
|
3243
|
+
select.title = !canSelectCharacter
|
|
3244
|
+
? "当前账户没有角色模块读取权限"
|
|
3245
|
+
: !state.aiRoleplayCharacter
|
|
3246
|
+
? "请先选择 AI 扮演的角色"
|
|
3247
|
+
: state.aiPromptSent
|
|
3248
|
+
? aiConversationOptionLockedMessage
|
|
3249
|
+
: "选择后,AI 会将每条用户消息视为该角色的发言或行动";
|
|
3200
3250
|
}
|
|
3201
3251
|
|
|
3202
3252
|
const aiConversationOptionLockedMessage = "会话选项在会话开始后不支持修改,若需要修改,请新建会话";
|
|
3203
3253
|
|
|
3254
|
+
function selectedAiModelLabel() {
|
|
3255
|
+
return $("#ai-model").selectedOptions[0]?.textContent?.trim() || "尚未选择模型";
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
function syncAiModelPicker() {
|
|
3259
|
+
const select = $("#ai-model");
|
|
3260
|
+
const button = $("#ai-model-picker");
|
|
3261
|
+
const interactionBusy = aiInteractionBusy();
|
|
3262
|
+
const selectedLabel = selectedAiModelLabel();
|
|
3263
|
+
const label = state.aiPromptSent
|
|
3264
|
+
? `当前模型:${selectedLabel}。${aiConversationOptionLockedMessage}`
|
|
3265
|
+
: `选择实际使用模型:${selectedLabel}`;
|
|
3266
|
+
select.disabled = interactionBusy;
|
|
3267
|
+
select.title = state.aiPromptSent ? aiConversationOptionLockedMessage : "";
|
|
3268
|
+
button.disabled = interactionBusy;
|
|
3269
|
+
button.title = label;
|
|
3270
|
+
button.setAttribute("aria-label", label);
|
|
3271
|
+
if (interactionBusy) setAiModelPickerVisible(false);
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3204
3274
|
function notifyAiConversationOptionLocked(select) {
|
|
3205
3275
|
if (!state.aiPromptSent) return false;
|
|
3206
3276
|
const now = Date.now();
|
|
@@ -3224,19 +3294,21 @@ function blockLockedAiConversationOptionKeydown(event) {
|
|
|
3224
3294
|
|
|
3225
3295
|
function syncAiTaskOptions() {
|
|
3226
3296
|
const roleplaySelected = $("#ai-task").value === "roleplay";
|
|
3297
|
+
const relationshipRoleplaySelectable = roleplaySelected && Boolean(state.aiRoleplayCharacter);
|
|
3227
3298
|
const interactionBusy = aiInteractionBusy();
|
|
3228
3299
|
$("#ai-scope").classList.toggle("hidden", roleplaySelected);
|
|
3229
3300
|
$("#ai-scope").setAttribute("aria-hidden", String(roleplaySelected));
|
|
3230
3301
|
$("#ai-roleplay-character").classList.toggle("hidden", !roleplaySelected);
|
|
3231
3302
|
$("#ai-roleplay-character").setAttribute("aria-hidden", String(!roleplaySelected));
|
|
3303
|
+
$("#ai-roleplay-user-character").classList.toggle("hidden", !relationshipRoleplaySelectable);
|
|
3304
|
+
$("#ai-roleplay-user-character").setAttribute("aria-hidden", String(!relationshipRoleplaySelectable));
|
|
3232
3305
|
$("#ai-task").disabled = interactionBusy;
|
|
3233
3306
|
$("#ai-task").title = state.aiPromptSent ? aiConversationOptionLockedMessage : "";
|
|
3234
3307
|
$("#ai-scope").disabled = interactionBusy || roleplaySelected;
|
|
3235
3308
|
$("#ai-scope").title = state.aiPromptSent
|
|
3236
3309
|
? aiConversationOptionLockedMessage
|
|
3237
3310
|
: roleplaySelected ? "角色扮演模式只使用角色自身的记忆" : "";
|
|
3238
|
-
|
|
3239
|
-
$("#ai-model").title = state.aiPromptSent ? aiConversationOptionLockedMessage : "";
|
|
3311
|
+
syncAiModelPicker();
|
|
3240
3312
|
}
|
|
3241
3313
|
|
|
3242
3314
|
function applyAiConversationTaskType(taskType) {
|
|
@@ -3262,15 +3334,19 @@ function applyAiConversationContextScope(scope) {
|
|
|
3262
3334
|
syncAiTaskOptions();
|
|
3263
3335
|
}
|
|
3264
3336
|
|
|
3265
|
-
function applyAiRoleplayCharacter(character) {
|
|
3337
|
+
function applyAiRoleplayCharacter(character, userCharacter = null) {
|
|
3266
3338
|
state.aiRoleplayCharacter = character?.id ? character : null;
|
|
3339
|
+
state.aiRoleplayUserCharacter = state.aiRoleplayCharacter && userCharacter?.id ? userCharacter : null;
|
|
3267
3340
|
const tab = activeAiChatTab();
|
|
3268
3341
|
if (tab) tab.roleplayCharacter = state.aiRoleplayCharacter ? { ...state.aiRoleplayCharacter } : null;
|
|
3342
|
+
if (tab) tab.roleplayUserCharacter = state.aiRoleplayUserCharacter ? { ...state.aiRoleplayUserCharacter } : null;
|
|
3269
3343
|
const active = Boolean(state.aiRoleplayCharacter);
|
|
3270
3344
|
if (active) $("#ai-scope").value = "none";
|
|
3271
3345
|
$(".ai-panel").classList.toggle("is-roleplaying", active);
|
|
3272
3346
|
$("#ai-prompt").dataset.placeholder = active
|
|
3273
|
-
?
|
|
3347
|
+
? state.aiRoleplayUserCharacter
|
|
3348
|
+
? `以 ${String(state.aiRoleplayUserCharacter.name)} 的身份与 ${String(state.aiRoleplayCharacter.name)} 对话……`
|
|
3349
|
+
: `与 ${String(state.aiRoleplayCharacter.name)} 角色开始对话……`
|
|
3274
3350
|
: "告诉 AI 你想讨论或修改什么……";
|
|
3275
3351
|
renderAiRoleplayCharacterSelect();
|
|
3276
3352
|
syncAiTaskOptions();
|
|
@@ -3281,22 +3357,27 @@ function refreshAiMessageRoleLabels() {
|
|
|
3281
3357
|
$("#ai-feed").querySelectorAll(".assistant-message .message-heading > span").forEach((label) => {
|
|
3282
3358
|
label.textContent = aiAssistantLabel();
|
|
3283
3359
|
});
|
|
3360
|
+
$("#ai-feed").querySelectorAll(".user-message .message-heading > span").forEach((label) => {
|
|
3361
|
+
label.textContent = aiUserLabel();
|
|
3362
|
+
});
|
|
3284
3363
|
}
|
|
3285
3364
|
|
|
3286
|
-
async function updateAiRoleplayCharacter(characterId) {
|
|
3365
|
+
async function updateAiRoleplayCharacter(characterId, userCharacterId = null) {
|
|
3287
3366
|
const conversationId = await ensureAiConversation();
|
|
3288
3367
|
const conversation = await api(`/api/ai-conversations/${conversationId}/roleplay`, {
|
|
3289
3368
|
method: "PATCH",
|
|
3290
|
-
body: { characterId: characterId || null }
|
|
3369
|
+
body: { characterId: characterId || null, userCharacterId: userCharacterId || null }
|
|
3291
3370
|
});
|
|
3292
3371
|
upsertAiConversationSummary(conversation);
|
|
3293
3372
|
applyAiConversationTaskType(conversation.taskType);
|
|
3294
|
-
applyAiRoleplayCharacter(conversation.roleplayCharacter);
|
|
3373
|
+
applyAiRoleplayCharacter(conversation.roleplayCharacter, conversation.roleplayUserCharacter);
|
|
3295
3374
|
resetAiContextMeter();
|
|
3296
3375
|
if ($("#ai-feed").querySelector("[data-message-id]")) refreshAiMessageRoleLabels();
|
|
3297
3376
|
else resetAiFeed();
|
|
3298
3377
|
toast(conversation.roleplayCharacter
|
|
3299
|
-
?
|
|
3378
|
+
? conversation.roleplayUserCharacter
|
|
3379
|
+
? `已进入 ${conversation.roleplayCharacter.name} 与 ${conversation.roleplayUserCharacter.name} 的关系扮演模式`
|
|
3380
|
+
: `已进入 ${conversation.roleplayCharacter.name} 的角色扮演模式`
|
|
3300
3381
|
: "已退出角色扮演模式");
|
|
3301
3382
|
}
|
|
3302
3383
|
|
|
@@ -3314,7 +3395,7 @@ async function persistAiConversationTaskType(taskType) {
|
|
|
3314
3395
|
});
|
|
3315
3396
|
upsertAiConversationSummary(conversation);
|
|
3316
3397
|
applyAiConversationTaskType(conversation.taskType);
|
|
3317
|
-
applyAiRoleplayCharacter(conversation.roleplayCharacter);
|
|
3398
|
+
applyAiRoleplayCharacter(conversation.roleplayCharacter, conversation.roleplayUserCharacter);
|
|
3318
3399
|
return conversation;
|
|
3319
3400
|
}
|
|
3320
3401
|
|
|
@@ -3354,9 +3435,10 @@ async function prepareAiRequestConversation(requestHolder, taskType, scope) {
|
|
|
3354
3435
|
upsertAiConversationSummary(taskConversation);
|
|
3355
3436
|
tab.taskType = taskConversation.taskType;
|
|
3356
3437
|
tab.roleplayCharacter = taskConversation.roleplayCharacter ?? null;
|
|
3438
|
+
tab.roleplayUserCharacter = taskConversation.roleplayUserCharacter ?? null;
|
|
3357
3439
|
if (isActiveAiChatTab(tab)) {
|
|
3358
3440
|
applyAiConversationTaskType(tab.taskType);
|
|
3359
|
-
applyAiRoleplayCharacter(tab.roleplayCharacter);
|
|
3441
|
+
applyAiRoleplayCharacter(tab.roleplayCharacter, tab.roleplayUserCharacter);
|
|
3360
3442
|
}
|
|
3361
3443
|
|
|
3362
3444
|
const scopedConversation = await api(`/api/ai-conversations/${encodeURIComponent(request.conversationId)}/context-scope`, {
|
|
@@ -6554,6 +6636,8 @@ function resetWorkScopedUiCaches() {
|
|
|
6554
6636
|
state.aiConversations = [];
|
|
6555
6637
|
resetAiChatTabs();
|
|
6556
6638
|
$("#ai-model").innerHTML = '<option value="">使用创作助手时加载模型</option>';
|
|
6639
|
+
syncAiModelPicker();
|
|
6640
|
+
setAiModelPickerVisible(false);
|
|
6557
6641
|
renderAiConversationHistory();
|
|
6558
6642
|
}
|
|
6559
6643
|
|
|
@@ -11395,12 +11479,16 @@ async function ensureAiModelsLoaded() {
|
|
|
11395
11479
|
if (aiModelsLoadPromise && aiModelsLoadWorkId === workId) return aiModelsLoadPromise;
|
|
11396
11480
|
const select = $("#ai-model");
|
|
11397
11481
|
select.innerHTML = '<option value="">正在加载模型……</option>';
|
|
11482
|
+
syncAiModelPicker();
|
|
11398
11483
|
aiModelsLoadWorkId = workId;
|
|
11399
11484
|
aiModelsLoadPromise = loadModels();
|
|
11400
11485
|
try {
|
|
11401
11486
|
await aiModelsLoadPromise;
|
|
11402
11487
|
} catch (error) {
|
|
11403
|
-
if (state.work?.id === workId)
|
|
11488
|
+
if (state.work?.id === workId) {
|
|
11489
|
+
select.innerHTML = '<option value="">模型加载失败,点击重试</option>';
|
|
11490
|
+
syncAiModelPicker();
|
|
11491
|
+
}
|
|
11404
11492
|
throw error;
|
|
11405
11493
|
} finally {
|
|
11406
11494
|
if (aiModelsLoadWorkId === workId) {
|
|
@@ -11535,6 +11623,14 @@ function setAiContextDistributionVisible(visible) {
|
|
|
11535
11623
|
if (!visible && document.activeElement === $("#ai-context-popover-close")) meter.focus();
|
|
11536
11624
|
}
|
|
11537
11625
|
|
|
11626
|
+
function setAiModelPickerVisible(visible) {
|
|
11627
|
+
const button = $("#ai-model-picker");
|
|
11628
|
+
const popover = $("#ai-model-popover");
|
|
11629
|
+
popover.classList.toggle("hidden", !visible);
|
|
11630
|
+
button.setAttribute("aria-expanded", String(visible));
|
|
11631
|
+
if (!visible && document.activeElement === $("#ai-model")) button.focus();
|
|
11632
|
+
}
|
|
11633
|
+
|
|
11538
11634
|
function showAiContextWarning(usage = null) {
|
|
11539
11635
|
$("#ai-context-warning-title").textContent = "对话上下文过长";
|
|
11540
11636
|
$("#ai-context-warning-message").textContent = "当前对话上下文过长,是否进行压缩?不压缩可能会导致后续请求失败";
|
|
@@ -15201,7 +15297,9 @@ function appendMessage(role, text, citations = [], createdAt = null, metadata =
|
|
|
15201
15297
|
});
|
|
15202
15298
|
const heading = attachMessageHeading(
|
|
15203
15299
|
message,
|
|
15204
|
-
role === "user"
|
|
15300
|
+
role === "user"
|
|
15301
|
+
? aiUserLabel(tab?.roleplayUserCharacter)
|
|
15302
|
+
: aiAssistantLabel(isInterrupted ? aiStreamInterruptionLabel(interruptionCode) : "", tab?.roleplayCharacter),
|
|
15205
15303
|
createdAt ?? undefined,
|
|
15206
15304
|
tab
|
|
15207
15305
|
);
|
|
@@ -16863,7 +16961,7 @@ $("#ai-prompt").addEventListener("input", async () => {
|
|
|
16863
16961
|
$("#ai-prompt").addEventListener("focus", () => {
|
|
16864
16962
|
ensureAiModelsLoaded().catch((error) => toast(`模型加载失败:${error.message}`, "error"));
|
|
16865
16963
|
});
|
|
16866
|
-
for (const select of [$("#ai-task"), $("#ai-scope"), $("#ai-roleplay-character"), $("#ai-model")]) {
|
|
16964
|
+
for (const select of [$("#ai-task"), $("#ai-scope"), $("#ai-roleplay-character"), $("#ai-roleplay-user-character"), $("#ai-model")]) {
|
|
16867
16965
|
select.addEventListener("pointerdown", blockLockedAiConversationOptionInteraction);
|
|
16868
16966
|
select.addEventListener("click", blockLockedAiConversationOptionInteraction);
|
|
16869
16967
|
select.addEventListener("keydown", blockLockedAiConversationOptionKeydown);
|
|
@@ -16874,9 +16972,26 @@ $("#ai-model").addEventListener("focus", () => {
|
|
|
16874
16972
|
$("#ai-model").addEventListener("change", (event) => {
|
|
16875
16973
|
if (state.aiPromptSent) {
|
|
16876
16974
|
event.currentTarget.value = state.aiConversationModelId ?? event.currentTarget.value;
|
|
16975
|
+
syncAiModelPicker();
|
|
16877
16976
|
return toast(aiConversationOptionLockedMessage);
|
|
16878
16977
|
}
|
|
16879
16978
|
setAiContextMeter(null);
|
|
16979
|
+
syncAiModelPicker();
|
|
16980
|
+
setAiModelPickerVisible(false);
|
|
16981
|
+
});
|
|
16982
|
+
$("#ai-model-picker").addEventListener("click", async (event) => {
|
|
16983
|
+
const button = event.currentTarget;
|
|
16984
|
+
if (notifyAiConversationOptionLocked(button)) return;
|
|
16985
|
+
const willOpen = $("#ai-model-popover").classList.contains("hidden");
|
|
16986
|
+
setAiContextDistributionVisible(false);
|
|
16987
|
+
setAiModelPickerVisible(willOpen);
|
|
16988
|
+
if (!willOpen) return;
|
|
16989
|
+
try {
|
|
16990
|
+
await ensureAiModelsLoaded();
|
|
16991
|
+
} catch (error) {
|
|
16992
|
+
toast(`模型加载失败:${error.message}`, "error");
|
|
16993
|
+
}
|
|
16994
|
+
if (!$("#ai-model-popover").classList.contains("hidden") && !$("#ai-model").disabled) $("#ai-model").focus();
|
|
16880
16995
|
});
|
|
16881
16996
|
$("#ai-roleplay-character").addEventListener("focus", async () => {
|
|
16882
16997
|
try {
|
|
@@ -16903,6 +17018,31 @@ $("#ai-roleplay-character").addEventListener("change", async (event) => {
|
|
|
16903
17018
|
renderAiRoleplayCharacterSelect();
|
|
16904
17019
|
}
|
|
16905
17020
|
});
|
|
17021
|
+
$("#ai-roleplay-user-character").addEventListener("focus", async () => {
|
|
17022
|
+
try {
|
|
17023
|
+
await ensureAiReferencesLoaded();
|
|
17024
|
+
renderAiRoleplayUserCharacterSelect();
|
|
17025
|
+
} catch (error) {
|
|
17026
|
+
toast(`角色卡加载失败:${error.message}`, "error");
|
|
17027
|
+
}
|
|
17028
|
+
});
|
|
17029
|
+
$("#ai-roleplay-user-character").addEventListener("change", async (event) => {
|
|
17030
|
+
const select = event.currentTarget;
|
|
17031
|
+
if (state.aiPromptSent) {
|
|
17032
|
+
renderAiRoleplayUserCharacterSelect();
|
|
17033
|
+
return toast(aiConversationOptionLockedMessage);
|
|
17034
|
+
}
|
|
17035
|
+
const userCharacterId = select.value;
|
|
17036
|
+
select.disabled = true;
|
|
17037
|
+
try {
|
|
17038
|
+
await updateAiRoleplayCharacter($("#ai-roleplay-character").value, userCharacterId);
|
|
17039
|
+
} catch (error) {
|
|
17040
|
+
renderAiRoleplayUserCharacterSelect();
|
|
17041
|
+
toast(`关系扮演模式切换失败:${error.message}`, "error");
|
|
17042
|
+
} finally {
|
|
17043
|
+
renderAiRoleplayUserCharacterSelect();
|
|
17044
|
+
}
|
|
17045
|
+
});
|
|
16906
17046
|
$("#ai-task").addEventListener("change", async (event) => {
|
|
16907
17047
|
const select = event.currentTarget;
|
|
16908
17048
|
const previousTaskType = select.dataset.previousValue || state.aiTaskType || "chat";
|
|
@@ -17097,6 +17237,7 @@ document.addEventListener("pointerdown", (event) => {
|
|
|
17097
17237
|
if (!event.target.closest("#ai-conversation-switcher-menu") && !event.target.closest("#ai-conversation-switcher")) setAiConversationSwitcherVisible(false);
|
|
17098
17238
|
if (!event.target.closest(".prompt-composer")) hideAiMentionMenu();
|
|
17099
17239
|
if (!event.target.closest("#ai-context-meter") && !event.target.closest("#ai-context-popover")) setAiContextDistributionVisible(false);
|
|
17240
|
+
if (!event.target.closest("#ai-model-picker") && !event.target.closest("#ai-model-popover")) setAiModelPickerVisible(false);
|
|
17100
17241
|
if (!event.target.closest("#account-button") && !event.target.closest("#account-menu")) {
|
|
17101
17242
|
$("#account-menu").classList.add("hidden");
|
|
17102
17243
|
$("#account-button").setAttribute("aria-expanded", "false");
|
|
@@ -17114,6 +17255,10 @@ document.addEventListener("keydown", (event) => {
|
|
|
17114
17255
|
return;
|
|
17115
17256
|
}
|
|
17116
17257
|
if (event.key === "Escape") {
|
|
17258
|
+
if (!$("#ai-model-popover").classList.contains("hidden")) {
|
|
17259
|
+
setAiModelPickerVisible(false);
|
|
17260
|
+
return;
|
|
17261
|
+
}
|
|
17117
17262
|
if (!$("#chapter-search-panel").classList.contains("hidden")) {
|
|
17118
17263
|
closeChapterSearchPanel();
|
|
17119
17264
|
return;
|
|
@@ -17149,6 +17294,7 @@ document.addEventListener("keydown", (event) => {
|
|
|
17149
17294
|
openSearchDialog().catch((error) => toast(error.message, "error"));
|
|
17150
17295
|
}, { capture: true });
|
|
17151
17296
|
$("#ai-context-meter").addEventListener("click", () => {
|
|
17297
|
+
setAiModelPickerVisible(false);
|
|
17152
17298
|
setAiContextDistributionVisible($("#ai-context-popover").classList.contains("hidden"));
|
|
17153
17299
|
});
|
|
17154
17300
|
$("#ai-context-popover-close").addEventListener("click", () => setAiContextDistributionVisible(false));
|
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">
|
|
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">
|
|
14
14
|
</head>
|
|
15
15
|
<body class="auth-pending">
|
|
16
16
|
<section id="auth-view" class="auth-view hidden" aria-labelledby="auth-title">
|
|
@@ -379,9 +379,9 @@
|
|
|
379
379
|
<select id="ai-roleplay-character" class="ai-roleplay-character hidden" aria-label="角色扮演角色卡">
|
|
380
380
|
<option value="">选择角色卡</option>
|
|
381
381
|
</select>
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
382
|
+
<select id="ai-roleplay-user-character" class="ai-roleplay-character ai-roleplay-user-character hidden" aria-label="我扮演的角色">
|
|
383
|
+
<option value="">选择我的角色(可选)</option>
|
|
384
|
+
</select>
|
|
385
385
|
</div>
|
|
386
386
|
<div class="prompt-composer">
|
|
387
387
|
<div id="ai-prompt" class="ai-prompt" contenteditable="true" role="textbox" aria-multiline="true" aria-autocomplete="list" aria-controls="ai-mention-menu" aria-haspopup="listbox" aria-expanded="false" aria-keyshortcuts="Enter" title="Enter 发送,Shift+Enter 换行" data-placeholder="告诉 AI 你想讨论或修改什么……"></div>
|
|
@@ -392,6 +392,11 @@
|
|
|
392
392
|
<header class="ai-context-popover-header"><div><strong id="ai-context-popover-title">Token 分布</strong><small id="ai-context-popover-description">按当前模型上下文窗口计算</small></div><button id="ai-context-popover-close" class="ai-context-popover-close" type="button" aria-label="关闭 Token 分布">×</button></header>
|
|
393
393
|
<div id="ai-context-distribution" class="ai-context-distribution" role="list" aria-label="当前 Token 分布"></div>
|
|
394
394
|
</section>
|
|
395
|
+
<button id="ai-model-picker" class="ai-model-picker" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="ai-model-popover" aria-label="选择实际使用模型" title="选择实际使用模型"><svg class="ai-model-picker-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5.25a3.25 3.25 0 0 0-5.95 1.82A3.74 3.74 0 0 0 4.7 13.5a3.25 3.25 0 0 0 4.55 4.53A3.6 3.6 0 0 0 12 19.5"></path><path d="M12 5.25a3.25 3.25 0 0 1 5.95 1.82 3.74 3.74 0 0 1 1.35 6.43 3.25 3.25 0 0 1-4.55 4.53A3.6 3.6 0 0 1 12 19.5"></path><path d="M12 5.25v14.25"></path><path d="M8 9.1c1.1.1 2 .9 2.25 1.95"></path><path d="M16 9.1c-1.1.1-2 .9-2.25 1.95"></path><path d="M8.3 15.3c1.15-.05 2.1-.85 2.3-1.95"></path><path d="M15.7 15.3c-1.15-.05-2.1-.85-2.3-1.95"></path></svg></button>
|
|
396
|
+
<section id="ai-model-popover" class="ai-model-popover hidden" role="dialog" aria-labelledby="ai-model-popover-title">
|
|
397
|
+
<label id="ai-model-popover-title" for="ai-model">实际使用模型</label>
|
|
398
|
+
<select id="ai-model" aria-label="实际使用模型"><option value="">使用创作助手时加载模型</option></select>
|
|
399
|
+
</section>
|
|
395
400
|
<button id="ai-send" class="ai-send-button" type="button" data-state="send" aria-label="发送消息" title="发送消息"><svg class="ai-send-button-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M21 3 9.5 14.5M21 3l-7 18-4.5-6.5L3 10l18-7Z"></path></svg></button>
|
|
396
401
|
</div>
|
|
397
402
|
</div>
|
|
@@ -1205,6 +1210,6 @@
|
|
|
1205
1210
|
<div id="auth-loading" class="auth-loading" role="status" aria-label="正在载入工作台"></div>
|
|
1206
1211
|
<script id="vditorIconScript" src="/vendor/vditor/dist/js/icons/ant.js?v=3.11.2"></script>
|
|
1207
1212
|
<script src="/vendor/vditor/dist/index.min.js?v=3.11.2"></script>
|
|
1208
|
-
<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=calculate-time-tool-v1&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-v1&feature=volume-detail-icon-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"></script>
|
|
1213
|
+
<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-v1&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-v1&feature=volume-detail-icon-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-model-picker-v1"></script>
|
|
1209
1214
|
</body>
|
|
1210
1215
|
</html>
|
|
@@ -327,10 +327,18 @@ export function normalizeGalaxyMotionMode(value) {
|
|
|
327
327
|
return GALAXY_MOTION_MODES.includes(value) ? value : "auto";
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
+
export function isGalaxyPerformanceThresholdExceeded(nodeCount, edgeCount) {
|
|
331
|
+
return Math.max(0, Number(nodeCount) || 0) > GALAXY_REDUCED_MOTION_NODE_THRESHOLD
|
|
332
|
+
|| Math.max(0, Number(edgeCount) || 0) > GALAXY_REDUCED_MOTION_EDGE_THRESHOLD;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function shouldRenderGalaxyEdgeLabel(nodeCount, edgeCount, edgeSelected, highlighted) {
|
|
336
|
+
return Boolean(edgeSelected) || (Boolean(highlighted) && !isGalaxyPerformanceThresholdExceeded(nodeCount, edgeCount));
|
|
337
|
+
}
|
|
338
|
+
|
|
330
339
|
export function getGalaxyMotionProfile(mode, nodeCount, edgeCount, prefersReducedMotion = false) {
|
|
331
340
|
const requestedMode = normalizeGalaxyMotionMode(mode);
|
|
332
|
-
const thresholdExceeded =
|
|
333
|
-
|| Math.max(0, Number(edgeCount) || 0) > GALAXY_REDUCED_MOTION_EDGE_THRESHOLD;
|
|
341
|
+
const thresholdExceeded = isGalaxyPerformanceThresholdExceeded(nodeCount, edgeCount);
|
|
334
342
|
const reducedBySystem = requestedMode === "auto" && Boolean(prefersReducedMotion);
|
|
335
343
|
const reducedByThreshold = requestedMode === "auto" && thresholdExceeded;
|
|
336
344
|
const effectiveMode = requestedMode === "off"
|
|
@@ -2528,7 +2536,7 @@ export function createGalaxyRenderer(dialog, graph, options = {}) {
|
|
|
2528
2536
|
context.lineTo(to.x - Math.cos(angle + 0.45) * arrowSize, to.y - Math.sin(angle + 0.45) * arrowSize);
|
|
2529
2537
|
context.fill();
|
|
2530
2538
|
}
|
|
2531
|
-
if (highlighted) {
|
|
2539
|
+
if (shouldRenderGalaxyEdgeLabel(graph.stats.nodeCount, graph.stats.edgeCount, edgeSelected, highlighted)) {
|
|
2532
2540
|
const fullLabel = formatRelationshipLabel(edge);
|
|
2533
2541
|
if (!highlightedKeywordSet.has(fullLabel)) {
|
|
2534
2542
|
highlightedKeywordSet.add(fullLabel);
|
package/dist/public/styles.css
CHANGED
|
@@ -2711,9 +2711,8 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2711
2711
|
.prompt-options { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-bottom: 7px; }
|
|
2712
2712
|
.prompt-options select { padding: 6px; color: var(--muted); font-size: 10px; }
|
|
2713
2713
|
.prompt-options .ai-roleplay-character { min-width: 0; }
|
|
2714
|
+
.prompt-options .ai-roleplay-user-character { grid-column: 1 / -1; }
|
|
2714
2715
|
.ai-panel.is-roleplaying .ai-roleplay-character { border-color: color-mix(in srgb, var(--accent) 52%, var(--line)); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); color: var(--accent-dark); }
|
|
2715
|
-
.prompt-model-field { margin: 0 0 7px; }
|
|
2716
|
-
.prompt-model-field select { padding: 6px; color: var(--muted); font-size: 10px; }
|
|
2717
2716
|
.prompt-composer { position: relative; }
|
|
2718
2717
|
.ai-mention-menu { position: absolute; z-index: 20; left: 0; right: 0; bottom: calc(100% + 6px); max-height: 230px; overflow-y: auto; padding: 6px; border: 1px solid var(--line); border-radius: 6px; background: var(--panel); box-shadow: 0 12px 34px rgba(49,42,32,.18); }
|
|
2719
2718
|
.ai-mention-option { display: grid; grid-template-columns: 34px minmax(0, 1fr); gap: 8px; width: 100%; padding: 7px 8px; border: 0; border-radius: 4px; background: transparent; text-align: left; }
|
|
@@ -2736,13 +2735,24 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
2736
2735
|
.ai-context-meter b { position: relative; z-index: 1; font-size: 9px; font-weight: 600; }.ai-context-meter.is-warning { --context-meter-color: #bf6a35; }.ai-context-meter.is-danger { --context-meter-color: #ad463c; }.ai-context-meter.is-empty { opacity: .62; }
|
|
2737
2736
|
.ai-context-popover { position: absolute; right: 0; bottom: calc(100% + 10px); z-index: 40; display: grid; width: min(320px, calc(100vw - 32px)); gap: 12px; padding: 14px; border: 1px solid var(--line); border-radius: 7px; background: var(--paper); color: var(--ink); box-shadow: 0 14px 38px rgba(36, 30, 24, .18); }
|
|
2738
2737
|
.ai-context-popover.hidden { display: none; }
|
|
2739
|
-
.ai-context-popover::after { position: absolute; right:
|
|
2738
|
+
.ai-context-popover::after { position: absolute; right: 87px; bottom: -6px; width: 10px; height: 10px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--paper); content: ""; transform: rotate(45deg); }
|
|
2740
2739
|
.ai-context-popover-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding-bottom: 10px; border-bottom: 1px solid var(--line); }
|
|
2741
2740
|
.ai-context-popover-header > div { display: grid; gap: 3px; min-width: 0; }
|
|
2742
2741
|
.ai-context-popover-header strong { font-size: 12px; }
|
|
2743
2742
|
.ai-context-popover-header small { color: var(--muted); font: 9px/1.4 var(--font-latin), var(--font-cjk), sans-serif; }
|
|
2744
2743
|
.ai-context-popover-close { flex: 0 0 auto; width: 24px; height: 24px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: transparent; color: var(--muted); font: 17px/1 var(--font-latin), sans-serif; }
|
|
2745
2744
|
.ai-context-popover-close:hover, .ai-context-popover-close:focus-visible { border-color: var(--accent); color: var(--accent-dark); outline: 0; }
|
|
2745
|
+
.ai-model-picker { display: grid; flex: 0 0 32px; place-items: center; width: 32px; min-width: 32px; min-height: 32px; height: 32px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: var(--surface); color: var(--muted); }
|
|
2746
|
+
.ai-model-picker:hover, .ai-model-picker:focus-visible, .ai-model-picker[aria-expanded="true"] { border-color: color-mix(in srgb, var(--accent) 56%, var(--line)); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); color: var(--accent-dark); outline: 0; }
|
|
2747
|
+
.ai-model-picker:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent); }
|
|
2748
|
+
.ai-model-picker:disabled { cursor: wait; opacity: .56; }
|
|
2749
|
+
.ai-model-picker-icon { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.7; }
|
|
2750
|
+
.ai-model-popover { position: absolute; right: 0; bottom: calc(100% + 10px); z-index: 41; display: grid; width: min(280px, calc(100vw - 32px)); gap: 7px; padding: 12px; border: 1px solid var(--line); border-radius: 7px; background: var(--paper); color: var(--ink); box-shadow: 0 14px 38px rgba(36, 30, 24, .18); }
|
|
2751
|
+
.ai-model-popover.hidden { display: none; }
|
|
2752
|
+
.ai-model-popover::after { position: absolute; right: 49px; bottom: -6px; width: 10px; height: 10px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--paper); content: ""; transform: rotate(45deg); }
|
|
2753
|
+
.ai-model-popover > label { color: var(--muted); font-size: 10px; font-weight: 600; }
|
|
2754
|
+
.ai-model-popover select { width: 100%; min-width: 0; padding: 7px; color: var(--ink); font-size: 10px; }
|
|
2755
|
+
.ai-model-popover select:focus-visible { border-color: var(--accent); outline: 0; box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent); }
|
|
2746
2756
|
.ai-context-distribution { display: grid; gap: 10px; }
|
|
2747
2757
|
.ai-context-distribution-row { display: grid; gap: 5px; }
|
|
2748
2758
|
.ai-context-distribution-label { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font: 10px/1.3 var(--font-latin), sans-serif; }
|
|
@@ -3999,7 +4009,7 @@ body { font-size: var(--ui-font-size); }
|
|
|
3999
4009
|
.guard-card strong, .guard-card p, .message-actions button { font-size: calc(10px * var(--ai-font-scale)); }
|
|
4000
4010
|
.ai-context-warning strong { font-size: calc(11px * var(--ai-font-scale)); }
|
|
4001
4011
|
.ai-context-warning p, .ai-context-warning button { font-size: calc(9px * var(--ai-font-scale)); }
|
|
4002
|
-
.prompt-options select, .
|
|
4012
|
+
.prompt-options select, .ai-model-popover > label, .ai-model-popover select, .ai-mention-option strong, .ai-mention-empty { font-size: calc(10px * var(--ai-font-scale)); }
|
|
4003
4013
|
.ai-mention-option small { font-size: calc(8px * var(--ai-font-scale)); }
|
|
4004
4014
|
.ai-prompt { font-size: var(--ai-font-size); }
|
|
4005
4015
|
.ai-prompt-reference { font-size: calc(10px * var(--ai-font-scale)); }
|