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