chore(frontend): reduce redundancies

This commit is contained in:
Shivering Ocelot committed 2026-09-21 15:18:22 -05:00
1 parent dac5d201cd
commit 75dac1e459
8 files changed
+65 -97

No files matched your search

+8
View File
@@ -157,6 +157,14 @@ export function sendAction(action, body = {}, callback = null) {
return deferred.promise; return deferred.promise;
} }
export async function act (action, body, { alert = null, cache = null } = {}) {
const res = await sendAction(action, body);
if (res.error) { ui.alert(`Error: ${res.error}`); return null; }
if (cache) invalidateCache(cache);
if (alert) ui.alert(alert);
return res;
}
export function ping() { export function ping() {
if (socket && socket.readyState === WebSocket.OPEN) { if (socket && socket.readyState === WebSocket.OPEN) {
const data = JSON.stringify({ path: 'ping' }); const data = JSON.stringify({ path: 'ping' });
+10
View File
@@ -1,5 +1,6 @@
// Global utility functions related with UI functionality // Global utility functions related with UI functionality
import { ref } from 'vue';
import router from './router' import router from './router'
export function goto (path) { export function goto (path) {
@@ -33,6 +34,15 @@ export function contextMenu (entries) {
}); });
} }
// Helper object to manage track toggable ui elements
export function useToggleSet () {
const state = ref({});
return {
has(key) { return Boolean(state.value[key]); },
toggle(key) { state.value[key] = !state.value[key]; },
};
}
// === Events === // // === Events === //
+18 -34
View File
@@ -148,45 +148,35 @@
// === Configuration === // // === Configuration === //
function updateIdentifier() { async function updateIdentifier() {
const value = prompt(`Enter a new identifier for ${agent.value.name}`); const value = prompt(`Enter a new identifier for ${agent.value.name}`);
if (value) { if (value) {
backend.sendAction("update-agent", { let res = await backend.act("update-agent", {
// agentId is necessary here // agentId is necessary here
agentId: agent.value.id, agentId: agent.value.id,
// used as value instead of identifier // used as value instead of identifier
agentname: value, agentname: value,
}).then(res => { }, {
ui.alert(res.error ? cache: {agents: true},
'Error updating agent identifier' : alert: 'Agent identifier updated'
'Agent identifier updated'
);
if (!res.error) {
agent.value.agentname = value;
backend.invalidateCache({agents: true});
}
}); });
if (!res.error) agent.value.agentname = value;
} else { } else {
ui.alert("Cancelled identifier update"); ui.alert("Cancelled identifier update");
} }
} }
function updateName() { async function updateName() {
const value = prompt(`Enter a new name for ${agent.value.name}`); const value = prompt(`Enter a new name for ${agent.value.name}`);
if (value) { if (value) {
backend.sendAction("update-agent", { let res = await backend.sendAction("update-agent", {
agentname: agent.value.agentname, agentname: agent.value.agentname,
name: value, name: value,
}).then(res => { }, {
ui.alert(res.error ? alert: 'Agent name updated',
'Error updating agent name' : cache: {agents: true},
'Agent name updated'
);
if (!res.error) {
agent.value.name = value;
backend.invalidateCache({agents: true});
}
}); });
if (!res.error) agent.value.name = value;
} else { } else {
ui.alert("Cancelled name update"); ui.alert("Cancelled name update");
} }
@@ -215,19 +205,13 @@
} }
} }
let res = await backend.sendAction("update-agent", { await backend.act("update-agent", {
agentname: agent.value.agentname, agentname: agent.value.agentname,
data: dataObject, data: dataObject,
}, {
alert: 'Agent updated',
cache: {agents: true},
}); });
ui.alert(res.error ?
'Error updating agent' :
'Agent updated'
);
if (!res.error) {
backend.invalidateCache({agents: true});
}
} }
async function saveAddingTool() { async function saveAddingTool() {
@@ -292,8 +276,8 @@
<span class="label">Enable Instruction/Input</span> <span class="label">Enable Instruction/Input</span>
<input type="checkbox" v-model="promptInstructionEnabled" /> <input type="checkbox" v-model="promptInstructionEnabled" />
</div> </div>
<textarea v-if="promptInstructionEnabled" v-model="promptInstruction" class="config-input" @keydown="promptKeyDown($event, 1)" /> <textarea v-if="promptInstructionEnabled" v-model="promptInstruction" class="config-input" @keydown.ctrl.enter.prevent="promptInputElem.focus" />
<textarea v-model="promptInput" class="config-input" ref="promptInputElem" @keydown="promptKeyDown($event, 2)" /> <textarea v-model="promptInput" class="config-input" ref="promptInputElem" @keydown.ctrl.enter.prevent="sendPrompt" />
<div> <div>
<button @click="sendPrompt()">Send Prompt</button> <button @click="sendPrompt()">Send Prompt</button>
</div> </div>
+5 -6
View File
@@ -39,14 +39,13 @@
payload.temporary = true; payload.temporary = true;
} }
let response = await backend.sendAction("create-agent", payload); let response = await backend.act("create-agent", payload, {
alert: `Created Agent "${payload.name}"`,
cache: {agents: true},
});
if (response.error) { if (!response.error) {
ui.alert(`Could not create agent\n${response.error}`);
} else {
ui.alert(`Created Agent "${payload.name}"`);
backend.invalidateCache({agents: true}); backend.invalidateCache({agents: true});
ui.goto(`/agent/${payload.agentname}`);
} }
} }
+8 -17
View File
@@ -26,7 +26,9 @@
} = session; } = session;
// UI state coupled to the document itself // UI state coupled to the document itself
const expandedLogs = ref({}); // Message ids of logs currently expanded
// Message ids of logs currently expanded
const expandedLogs = ui.useToggleSet();
const inputFieldElem = ref(null); const inputFieldElem = ref(null);
const chatContainerElem = ref(null); const chatContainerElem = ref(null);
@@ -109,10 +111,6 @@
// === UI Helper Functions === // // === UI Helper Functions === //
function toggleLogExpand (message) {
expandedLogs.value[message.id] = !expandedLogs.value[message.id];
}
async function chatEntryContextMenu (message, logMeta) { async function chatEntryContextMenu (message, logMeta) {
let entries = [ let entries = [
{value: 'pin', title: "Pin message"}, {value: 'pin', title: "Pin message"},
@@ -143,7 +141,7 @@
break; break;
} }
case 'toggle-log': { case 'toggle-log': {
toggleLogExpand(message); expandedLogs.toggle(message.id);
break; break;
} }
case 'delete': { case 'delete': {
@@ -178,13 +176,6 @@
}); });
} }
function inputKeyDown (event) {
if (event.key == "Enter" && event.ctrlKey) {
event.preventDefault();
sendMessage();
}
}
</script> </script>
<template> <template>
@@ -203,10 +194,10 @@
:key="msg.id ?? ('thread-' + msg.threadId)" :key="msg.id ?? ('thread-' + msg.threadId)"
:message="msg" :message="msg"
@open-menu="chatEntryContextMenu" @open-menu="chatEntryContextMenu"
@open-thread="session.activateThread" @open-thread="session.activateThread($event)"
:log-expanded="expandedLogs[msg.id]" :log-expanded="expandedLogs.has(msg.id)"
@toggle-log="toggleLogExpand(msg)" @toggle-log="expandedLogs.toggle(msg.id)"
:cancellable="messageRunMap[msg.id]" :cancellable="messageRunMap[msg.id]"
@cancel="session.cancelMessageRun(msg.id)" @cancel="session.cancelMessageRun(msg.id)"
@@ -218,7 +209,7 @@
{{editInfo}} {{editInfo}}
</div> </div>
<div class="input-field" ref="inputFieldElem" @keydown="inputKeyDown($event)" contenteditable></div> <div class="input-field" ref="inputFieldElem" @keydown.ctrl.enter.prevent="sendMessage" contenteditable></div>
<div class="input-row"> <div class="input-row">
<button @click="session.startNewThread()"> <button @click="session.startNewThread()">
+4 -3
View File
@@ -25,13 +25,14 @@
payload.temporary = true; payload.temporary = true;
} }
let response = await backend.sendAction("create-chat", payload); let response = await backend.act("create-chat", payload, {
alert: `Created Chat "${title.value}"`,
cache: {chats: true},
});
if (response.error) { if (response.error) {
ui.alert(`Could not create chat\n${response.error}`); ui.alert(`Could not create chat\n${response.error}`);
} else { } else {
ui.alert(`Created Chat "${title.value}"`);
backend.invalidateCache({chats: true});
ui.goto(`/chat/${response.chatId}`); ui.goto(`/chat/${response.chatId}`);
} }
} }
+2 -10
View File
@@ -10,7 +10,7 @@
import * as ui from '../ui'; import * as ui from '../ui';
const models = ref([]); const models = ref([]);
const expandedModelId = ref(new Set()); const expandedModelId = ui.useToggleSet();
const createExpanded = ref(false); const createExpanded = ref(false);
const newKeyValue = ref(false); const newKeyValue = ref(false);
@@ -22,14 +22,6 @@
models.value = res.models.map(entry => ({editing: {}, ...entry})); models.value = res.models.map(entry => ({editing: {}, ...entry}));
}); });
const toggleModel = (id) => {
if (expandedModelId.value.has(id)) {
expandedModelId.value.delete(id);
} else {
expandedModelId.value.add(id);
}
};
async function createModel () { async function createModel () {
let current = Object.assign({editing: {}}, editModel.value); let current = Object.assign({editing: {}}, editModel.value);
@@ -114,7 +106,7 @@
</div> </div>
<div v-else v-for="model in models" :key="model.id" class="model-entry"> <div v-else v-for="model in models" :key="model.id" class="model-entry">
<div class="model-header" @click="toggleModel(model.id)"> <div class="model-header" @click="expandedModelId.toggle(model.id)">
<span class="model-name">{{ model.name }}</span> <span class="model-name">{{ model.name }}</span>
<span class="model-kind">{{ model.kind }}</span> <span class="model-kind">{{ model.kind }}</span>
<div class="model-actions"> <div class="model-actions">
+10 -27
View File
@@ -108,10 +108,10 @@ import * as ui from '../ui';
const upstreamRemotes = ref([]); const upstreamRemotes = ref([]);
/** Expanded remote rows, keyed by row key; several can be open at once */ /** Expanded remote rows, keyed by row key; several can be open at once */
const expandedRemotes = ref(new Set()); const expandedRemotes = ui.useToggleSet();
/** Expanded function rows, keyed by `<rowKey>|<fnKey>` */ /** Expanded function rows, keyed by `<rowKey>|<fnKey>` */
const expandedFns = ref(new Set()); const expandedFns = ui.useToggleSet();
/** Per-function call state, keyed like expandedFns */ /** Per-function call state, keyed like expandedFns */
const callStates = reactive({}); const callStates = reactive({});
@@ -222,27 +222,10 @@ function functionsOf(row) {
})); }));
} }
// ---- Expansion ---- // Function key, used in expandedFns
function toggleRemote(key) {
const set = new Set(expandedRemotes.value);
if (set.has(key)) set.delete(key); else set.add(key);
expandedRemotes.value = set;
}
function fnKey(row, fn) { function fnKey(row, fn) {
return fn.id != null ? `l${fn.id}` : `u${fn.name}`; let fkey = fn.id != null ? `l${fn.id}` : `u${fn.name}`;
} return `${row.key}|${fkey}`;
function fnStateKey(row, fn) {
return `${row.key}|${fnKey(row, fn)}`;
}
function toggleFn(row, fn) {
const key = fnStateKey(row, fn);
const set = new Set(expandedFns.value);
if (set.has(key)) set.delete(key); else set.add(key);
expandedFns.value = set;
} }
// ---- Calling ---- // ---- Calling ----
@@ -276,7 +259,7 @@ function defaultArgs(fn) {
} }
function callStateFor(row, fn) { function callStateFor(row, fn) {
const key = fnStateKey(row, fn); const key = fnKey(row, fn);
if (!callStates[key]) { if (!callStates[key]) {
callStates[key] = { args: defaultArgs(fn), result: null, error: '', busy: false }; callStates[key] = { args: defaultArgs(fn), result: null, error: '', busy: false };
} }
@@ -413,7 +396,7 @@ function removeArgument(fn, argIdx) {
<template v-for="row in remoteRows" :key="rowKey(row)"> <template v-for="row in remoteRows" :key="rowKey(row)">
<!-- Remote row --> <!-- Remote row -->
<div class="rt-row rt-remote" :class="{ open: expandedRemotes.has(row.key) }"> <div class="rt-row rt-remote" :class="{ open: expandedRemotes.has(row.key) }">
<div class="col-arrow toggle" @click="toggleRemote(row.key)"> <div class="col-arrow toggle" @click="expandedRemotes.toggle(row.key)">
{{ expandedRemotes.has(row.key) ? '▼' : '▶' }} {{ expandedRemotes.has(row.key) ? '▼' : '▶' }}
</div> </div>
<div class="col-name"> <div class="col-name">
@@ -464,8 +447,8 @@ function removeArgument(fn, argIdx) {
<template v-for="fn in functionsOf(row)" :key="fnKey(row, fn)"> <template v-for="fn in functionsOf(row)" :key="fnKey(row, fn)">
<!-- Function row --> <!-- Function row -->
<div class="rt-row rt-fn"> <div class="rt-row rt-fn">
<div class="col-arrow toggle" @click="toggleFn(row, fn)"> <div class="col-arrow toggle" @click="expandedFns.toggle(fnKey(row, fn))">
{{ expandedFns.has(fnStateKey(row, fn)) ? '▼' : '▶' }} {{ expandedFns.has(fnKey(row, fn)) ? '▼' : '▶' }}
</div> </div>
<div class="col-name"> <div class="col-name">
<input <input
@@ -499,7 +482,7 @@ function removeArgument(fn, argIdx) {
</div> </div>
<!-- Expanded function detail: left = arguments, right = output/body --> <!-- Expanded function detail: left = arguments, right = output/body -->
<div v-if="expandedFns.has(fnStateKey(row, fn))" class="rt-row rt-fndetail"> <div v-if="expandedFns.has(fnKey(row, fn))" class="rt-row rt-fndetail">
<div class="col-arrow"></div> <div class="col-arrow"></div>
<div class="detail"> <div class="detail">
<!-- Callable mode (connected remotes) --> <!-- Callable mode (connected remotes) -->