Changeset 0.9.3 (#53)

This commit is contained in:
2026-02-23 23:55:22 +00:00
parent ac7c7c7d59
commit 90021157e6
20 changed files with 897 additions and 414 deletions

View File

@@ -25,7 +25,7 @@ const App = {
settings: {
model: '',
stream: true,
showThinking: true,
showThinking: false,
systemPrompt: '',
maxTokens: 0, // 0 = auto from model capabilities
temperature: 0.7,
@@ -287,6 +287,7 @@ function resolveCapabilities(backendCaps, modelId) {
async function fetchModels() {
try {
const data = await API.listEnabledModels();
App.defaultModel = data.default_model || '';
// Load user model preferences
try {
const prefData = await API.getModelPreferences();
@@ -335,6 +336,7 @@ async function fetchModels() {
});
console.log(`📋 Loaded ${App.models.length} models (${App.models.filter(m => m.isPreset).length} presets, ${App.models.filter(m => m.hidden).length} hidden)`);
} catch (e) { console.warn('Model fetch failed:', e.message); }
UI.updateModelSelector();
UI.updateCapabilityBadges();
}
@@ -382,6 +384,7 @@ async function selectChat(chatId) {
model: m.model || '',
modelName: '',
timestamp: m.created_at,
tool_calls: m.tool_calls || null,
siblingCount: m.sibling_count || 1,
siblingIndex: m.sibling_index || 0,
}));
@@ -514,6 +517,7 @@ async function reloadActivePath() {
model: m.model || '',
modelName: '',
timestamp: m.created_at,
tool_calls: m.tool_calls || null,
siblingCount: m.sibling_count || 1,
siblingIndex: m.sibling_index || 0,
}));
@@ -535,12 +539,23 @@ async function regenerateMessage(messageId) {
const model = modelInfo?.baseModelId || selectedId;
const presetId = modelInfo?.isPreset ? modelInfo.presetId : null;
// Truncate display to the parent of the message being regenerated.
// This makes the stream appear in the correct position — as a fresh
// response to the parent user message, not appended at the bottom.
const chat = App.chats.find(c => c.id === App.currentChatId);
if (chat) {
const msgIdx = chat.messages.findIndex(m => m.id === messageId);
if (msgIdx > 0) {
const truncated = chat.messages.slice(0, msgIdx);
UI.renderMessages(truncated);
}
}
App.isGenerating = true;
App.abortController = new AbortController();
UI.setGenerating(true);
try {
const chat = App.chats.find(c => c.id === App.currentChatId);
const resp = await API.streamRegenerate(
App.currentChatId, messageId, App.abortController.signal,
model, presetId, modelInfo?.configId
@@ -969,7 +984,8 @@ function initListeners() {
});
document.getElementById('fetchModelsBtn')?.addEventListener('click', async () => {
await fetchModels();
UI.toast(`Loaded ${App.models.length} models`, 'success');
const visible = App.models.filter(m => !m.hidden).length;
UI.toast(`Loaded ${visible} model${visible !== 1 ? 's' : ''}`, 'success');
});
// Settings modal
@@ -1385,12 +1401,15 @@ function initListeners() {
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Escape: stop generation → close command palette → close topmost modal
// Escape: stop generation → close command palette → close side panel → close topmost modal
if (e.key === 'Escape') {
if (App.isGenerating) { stopGeneration(); return; }
if (document.getElementById('cmdPalette')?.classList.contains('active')) {
closeCmdPalette(); return;
}
if (document.getElementById('sidePanel')?.classList.contains('open')) {
closeSidePanel(); return;
}
const open = [...document.querySelectorAll('.modal-overlay.active')];
if (open.length) closeModal(open[open.length - 1].id);
return;
@@ -1690,6 +1709,10 @@ async function handleSaveAdminSettings() {
const userPresets = document.getElementById('adminUserPresetsToggle').checked;
await API.adminUpdateSetting('allow_user_personas', { value: userPresets ? 'true' : 'false' });
// Default model → default_model policy
const defaultModel = document.getElementById('adminDefaultModel').value;
await API.adminUpdateSetting('default_model', { value: defaultModel });
// Banner → global_settings (JSON value)
const banner = {
enabled: document.getElementById('adminBannerEnabled').checked,
@@ -2177,7 +2200,7 @@ var _selectedNoteIds = new Set();
var _notesSort = 'updated_desc';
async function openNotes() {
openModal('notesModal');
openSidePanel('notes');
_exitSelectMode();
await loadNotesList();
await loadNoteFolders();
@@ -2336,6 +2359,12 @@ function showNotesList() {
var _currentNote = null; // cached note for read mode
function copyNoteContent() {
if (!_currentNote) return;
const text = `# ${_currentNote.title || ''}\n\n${_currentNote.content || ''}`;
navigator.clipboard.writeText(text).then(() => UI.toast('Copied', 'success'));
}
async function openNoteEditor(noteId) {
document.getElementById('notesListView').style.display = 'none';
document.getElementById('notesEditorView').style.display = '';