This commit is contained in:
cam
2025-06-09 13:02:22 -04:00
parent aaa2b7da0b
commit 8f4f7cd40a
5 changed files with 172 additions and 0 deletions

View File

@@ -60,6 +60,7 @@
"@tauri-apps/plugin-updater": "~2",
"@tauri-apps/plugin-window-state": "~2",
"change-case": "^5.4.4",
"cron-parser": "^5.2.0",
"highlight.js": "^11.11.1",
"marked": "^15.0.7",
"moment": "^2.30.1",

17
pnpm-lock.yaml generated
View File

@@ -38,6 +38,9 @@ importers:
change-case:
specifier: ^5.4.4
version: 5.4.4
cron-parser:
specifier: ^5.2.0
version: 5.2.0
highlight.js:
specifier: ^11.11.1
version: 11.11.1
@@ -1051,6 +1054,10 @@ packages:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
engines: {node: '>= 0.10'}
cron-parser@5.2.0:
resolution: {integrity: sha512-ED2qJtcjelmUEuoV3btwxiOm3NI8H4/eCZNX3adUkrm+/b3Xq05+YoDXyjIN57VGhskuHx/KzC367GAklo1lRg==}
engines: {node: '>=18'}
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -1788,6 +1795,10 @@ packages:
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
luxon@3.6.1:
resolution: {integrity: sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==}
engines: {node: '>=12'}
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
@@ -3345,6 +3356,10 @@ snapshots:
object-assign: 4.1.1
vary: 1.1.2
cron-parser@5.2.0:
dependencies:
luxon: 3.6.1
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
@@ -4211,6 +4226,8 @@ snapshots:
lodash.merge@4.6.2: {}
luxon@3.6.1: {}
magic-string@0.30.17:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0

74
src/lib/models/task.ts Normal file
View File

@@ -0,0 +1,74 @@
import { invoke } from '@tauri-apps/api/core';
import Model, { type ToSqlRow } from '$lib/models/base.svelte';
export interface ITask{
id?: number;
name: string;
prompt: string;
// This is stored as a cron expression
period: string;
next_run: Date;
}
interface Row {
id: number;
name: string;
prompt: string;
period: string;
next_run: string;
}
export default class Task extends Model<ITask, Row>('tasks') {
static defaults = {
name: 'New Task',
prompt: '',
};
static async start(task: ITask) {
console.log("Starting: " + task.name + " in a webworker now...")
}
static async calculate_next_run_time(task: ITask) {
}
// static async afterCreate(server: ITask): Promise<ITask> {
// const metadata: Metadata = JSON.parse(
// await invoke('get_metadata', {
// command: server.command,
// args: server.args,
// env: server.env,
// })
// );
// const name = metadata.serverInfo?.name
// ?.replace('mcp-server/', '')
// ?.replace('/', '-') as string;
// return await this.update({
// ...server,
// name,
// metadata,
// });
// }
static async fromSql(row: Row): Promise<ITask> {
return {
id: row.id,
name: row.name,
prompt: row.prompt,
period: row.period,
next_run: new Date(row.next_run),
};
}
static async toSql(task: ITask): Promise<ToSqlRow<Row>> {
return {
name: task.name,
prompt: task.prompt,
period: task.period,
next_run: task.next_run.toISOString(),
};
}
}

View File

@@ -0,0 +1,78 @@
<script lang="ts">
import { goto } from '$app/navigation';
import Deleteable from '$components/Deleteable.svelte';
import Flex from '$components/Flex.svelte';
import Layout from '$components/Layouts/Default.svelte';
import Link from '$components/Link.svelte';
import List from '$components/List.svelte';
import Menu from '$components/Menu.svelte';
import Svg from '$components/Svg.svelte';
import Titlebar from '$components/Titlebar.svelte';
async function destroy(task: Task) {
await Task.delete(task.id as number);
goto(`/tasks`);
}
</script>
{#snippet titlebar()}
<Titlebar class="h-[60px] w-full">
<Flex
class=" border-r-light h-full w-[300px] items-center justify-between border-r px-8 pr-4"
>
<h1 class="font-[500]">Tasks</h1>
<a
href="/tasks/new"
class="border-light h-8 w-8 rounded-md border hover:cursor-pointer"
>
<p class="h-8 w-8 text-center !leading-[22px] font-[10px]">+</p>
</a>
</Flex>
</Titlebar>
{/snippet}
{#snippet TaskView(server: Task)}
<Menu items={items(server)}>
<Deleteable ondelete={() => destroy(server)}>
<Link
href={`/mcp-servers/${server.id}`}
class="w-full py-3 pl-8 text-sm hover:cursor-pointer"
activeClass="text-purple border-l border-l-purple"
>
{server.name}
</Link>
</Deleteable>
</Menu>
{/snippet}
{#snippet RegistryView(registry: Registry)}
<Link
href={`/mcp-servers/${registry.name.toLowerCase()}`}
class="mb-4 flex h-full w-full items-center pl-8"
activeClass="text-purple"
>
<Svg name={registry.icon} class="mr-4 h-6 w-6" />
{registry.name}
</Link>
{/snippet}
<Layout {titlebar}>
<Flex class="h-full items-start">
<Flex class="border-r-light h-full w-[300px] flex-col items-start border-r">
<List items={Tasks} itemView={TaskView} />
<List
items={tasks}
itemView={TaskView}
borderless
title="Tasks"
class="mt-4"
titleClass="pl-8 my-4"
/>
</Flex>
<Flex class="h-full w-[calc(100%-300px)] items-start p-8">
{@render children?.()}
</Flex>
</Flex>
</Layout>

View File

@@ -0,0 +1,2 @@
<script lang="ts">
</script>