Back to line length of 100 + some svelte/ts settings

- Goes back to a max line length of 100
- Makes whitespace insignificant in Svelte files (see below)
- Avoids parens around single argument closures, in TS, when unncessary

*Whitespace Significance*

In HTML, `<p><b> 1 </b></p>` and `<p><b>1</b></p>` are not the same
thing. In the former, the "1" has spaces around it. If you were to try
to split this into multiple lines...

```html
<p>
    <b>
        1
    </b>
</p>
```

...you would lose that whitespace. The newlines reset any significance
around individual tokens. This meant prettier would format that code
as...

```html
<p>
    <b
    > 1 </b>
</p>
```

...which is insane and hideous.

We're saying all whitespace is insignificant from now on. Meaning
prettier no longer needs to retain it and can format that code as a sane
person.

This means you need to explicitly use `&nbsp;` characters if you
explicitly need whitespace around things. OR put it in a `span` and use
css.

TL;DR: do not rely on whitespace significance in HTML.
This commit is contained in:
Matte Noble
2025-05-23 12:23:32 -07:00
parent d08bc43218
commit 366f9f5b97
34 changed files with 122 additions and 133 deletions

View File

@@ -4,7 +4,8 @@
"tabWidth": 4,
"semi": true,
"trailingComma": "es5",
"printWidth": 80,
"printWidth": 100,
"arrowParens": "avoid",
"plugins": [
"prettier-plugin-svelte",
"prettier-plugin-tailwindcss"
@@ -14,7 +15,7 @@
"files": "*.svelte",
"options": {
"parser": "svelte",
"printWidth": 100
"htmlWhitespaceSensitivity": "ignore"
}
}
]

View File

@@ -13,11 +13,13 @@
></path>
</g>
<g>
<path d="m24 12h-16c-.553 0-1-.448-1-1s.447-1 1-1h16c.553 0 1 .448 1 1s-.447 1-1 1z"
<path
d="m24 12h-16c-.553 0-1-.448-1-1s.447-1 1-1h16c.553 0 1 .448 1 1s-.447 1-1 1z"
></path>
</g>
<g>
<path d="m16 16h-8c-.553 0-1-.448-1-1s.447-1 1-1h8c.553 0 1 .448 1 1s-.447 1-1 1z"
<path
d="m16 16h-8c-.553 0-1-.448-1-1s.447-1 1-1h8c.553 0 1 .448 1 1s-.447 1-1 1z"
></path>
</g>
</g>

Before

Width:  |  Height:  |  Size: 946 B

After

Width:  |  Height:  |  Size: 978 B

View File

@@ -4,8 +4,8 @@
width="100%"
height="100%"
xmlns="http://www.w3.org/2000/svg"
><path
>
<path
d="m2 12c0-1.8129.44444-3.48538 1.33333-5.01754.90059-1.53217 2.11696-2.74269 3.64913-3.63158 1.54386-.90059 3.21634-1.35088 5.01754-1.35088 1.8129 0 3.4854.45029 5.0175 1.35088 1.5322.88889 2.7427 2.10526 3.6316 3.64912.9006 1.53216 1.3509 3.1988 1.3509 5s-.4503 3.4737-1.3509 5.0175c-.8889 1.5322-2.1052 2.7486-3.6491 3.6492-1.5322.8889-3.1988 1.3333-5 1.3333s-3.47368-.4444-5.01754-1.3333c-1.53217-.9006-2.74854-2.117-3.64913-3.6492-.88889-1.5438-1.33333-3.2163-1.33333-5.0175zm4.50877 0c0 .2807.09942.5146.29825.7018l2.89473 2.8947c.22223.2105.49125.3158.80705.3158.3275 0 .5965-.1053.807-.3158l5.8947-5.89475c.1989-.17543.2983-.40935.2983-.70175 0-.2807-.0994-.51462-.2983-.70175-.1871-.19884-.421-.29825-.7017-.29825s-.5146.09941-.7018.29825l-5.2982 5.28065-2.29827-2.2807c-.17544-.1988-.40936-.2982-.70176-.2982-.269 0-.50292.0994-.70175.2982-.19883.1872-.29825.4211-.29825.7018z"
>
</path>
></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -41,7 +41,7 @@
}
async function remove(key: string) {
env = env.filter((e) => e[0] !== key);
env = env.filter(e => e[0] !== key);
await save();
}
</script>

View File

@@ -40,7 +40,7 @@
<Flex bind:this={ref} class="bg-medium relative h-16 w-full hover:cursor-pointer">
<Flex
onclick={(e) => toggle(e)}
onclick={e => toggle(e)}
class="border-light absolute top-0 left-0 w-full justify-between
rounded-md border p-2 px-4"
>

View File

@@ -60,13 +60,13 @@
}
function isValid() {
return config.every((prop) => prop.valid);
return config.every(prop => prop.valid);
}
function validateAll() {
config
.filter((c) => c.required)
.forEach((prop) => {
.filter(c => c.required)
.forEach(prop => {
prop.valid = validate(prop.value);
});
}
@@ -115,8 +115,13 @@
<svelte:window onmessage={onMessage} />
{#if srcdoc}
<iframe title="stdioFunction" class="hidden h-0 w-0" sandbox="allow-scripts" allow="" {srcdoc}>
</iframe>
<iframe
title="stdioFunction"
class="hidden h-0 w-0"
sandbox="allow-scripts"
allow=""
{srcdoc}
></iframe>
{/if}
{#if server && config}
@@ -126,7 +131,7 @@
{#if config.length > 0}
<div class="w-full px-8">
{#each config.filter((p) => p.required) as prop (prop.name)}
{#each config.filter(p => p.required) as prop (prop.name)}
<Input
label={prop.name}
name={prop.name}
@@ -137,7 +142,7 @@
{/each}
</div>
{#if config.some((p) => !p.required)}
{#if config.some(p => !p.required)}
<Flex class="mt-8 w-full flex-col items-start">
<button
onclick={() => toggleOptional()}
@@ -148,7 +153,7 @@
{#if optionalIsOpen}
<div class="w-full px-8">
{#each config.filter((p) => !p.required) as prop (prop.name)}
{#each config.filter(p => !p.required) as prop (prop.name)}
<Input
label={prop.name}
name={prop.name}

View File

@@ -19,7 +19,7 @@
async function install() {
update = await availableUpdate();
await update?.downloadAndInstall((event) => {
await update?.downloadAndInstall(event => {
switch (event.event) {
case 'Started':
totalDownload = (event.data.contentLength as number) / 1000.0;

View File

@@ -21,8 +21,8 @@
<h1 class="text-purple text-3xl">Welcome to Tome</h1>
<p>
Thanks for being an early adopter! We appreciate you kicking the tires of our
<strong>Technical Preview</strong> as we explore making local LLMs, MCP, and AI app composition
a better experience.
<strong>Technical Preview</strong>
as we explore making local LLMs, MCP, and AI app composition a better experience.
</p>
<p>
This is an extremely early build. There will be problems edges are rough features

View File

@@ -39,19 +39,13 @@ export const init: ClientInit = async () => {
await Config.migrate();
info('[green]✔ config migrated');
await startup.addCheck(
StartupCheck.Agreement,
async () => Config.agreedToWelcome,
);
await startup.addCheck(StartupCheck.Agreement, async () => Config.agreedToWelcome);
await startup.addCheck(
StartupCheck.UpdateAvailable,
async () => await isUpToDate(),
);
await startup.addCheck(StartupCheck.UpdateAvailable, async () => await isUpToDate());
await startup.addCheck(
StartupCheck.NoModels,
async () => Engine.all().flatMap(e => e.models).length > 0,
async () => Engine.all().flatMap(e => e.models).length > 0
);
};

View File

@@ -13,7 +13,7 @@ export enum DeepLinks {
}
export function setupDeeplinks() {
listen<string>(DeepLinks.InstallMcpServer, async (event) => {
listen<string>(DeepLinks.InstallMcpServer, async event => {
goto(`/mcp-servers/install?config=${event.payload}`);
});
}

View File

@@ -29,7 +29,7 @@ export default class Gemini implements Client {
tools?: Tool[],
options?: Options
): Promise<IMessage> {
const messages = history.map((m) => GeminiMessage.from(m)).compact();
const messages = history.map(m => GeminiMessage.from(m)).compact();
let config: GenerateContentConfig = {
temperature: options?.temperature,
@@ -50,7 +50,7 @@ export default class Gemini implements Client {
let toolCalls: ToolCall[] = [];
if (functionCalls) {
toolCalls = functionCalls.map((tc) => ({
toolCalls = functionCalls.map(tc => ({
function: {
name: tc.name as string,
arguments: tc.args || {},
@@ -69,8 +69,8 @@ export default class Gemini implements Client {
async models(): Promise<IModel[]> {
return (await this.client.models.list()).page
.filter((model) => this.supportedModels.includes(model.name as string))
.map((model) => {
.filter(model => this.supportedModels.includes(model.name as string))
.map(model => {
const metadata = model;
const name = metadata.name?.replace('models/', '') as string;

View File

@@ -67,7 +67,7 @@ function fromToolResponse(message: IMessage): Content {
// Find the `toolCall` message for this response
const session = Session.find(message.sessionId as number);
const messages = Session.messages(session);
const call = messages.flatMap((m) => m.toolCalls).find((tc) => tc.id == message.toolCallId);
const call = messages.flatMap(m => m.toolCalls).find(tc => tc.id == message.toolCallId);
return {
role: 'user',

View File

@@ -32,7 +32,7 @@ function from(tools: Tool | Tool[]): ToolListUnion {
}
function fromMany(tools: Tool[]): FunctionDeclaration[] {
return tools.map((tool) => fromOne(tool));
return tools.map(tool => fromOne(tool));
}
function fromOne(tool: Tool): FunctionDeclaration {

View File

@@ -24,7 +24,7 @@ export default class Ollama implements Client {
tools: Tool[] = [],
options: Options = {}
): Promise<IMessage> {
const messages = history.map((m) => this.message.from(m));
const messages = history.map(m => this.message.from(m));
const response = await this.client.chat({
model: model.name,
messages,
@@ -57,9 +57,7 @@ export default class Ollama implements Client {
async models(): Promise<IModel[]> {
const models = (await this.client.list()).models;
return Promise.all(
models.map(async (model) => await this.info(model.name))
);
return Promise.all(models.map(async model => await this.info(model.name)));
}
async info(name: string): Promise<IModel> {

View File

@@ -10,7 +10,7 @@ export function from(message: IMessage): Message {
return {
role: message.role,
content: message.content,
tool_calls: message.toolCalls?.map((c) => ({
tool_calls: message.toolCalls?.map(c => ({
function: {
name: c.function.name,
arguments: c.function.arguments,

View File

@@ -33,7 +33,7 @@ export default class OpenAI implements Client {
tools: Tool[] = [],
options: Options = {}
): Promise<IMessage> {
const messages = history.map((m) => OpenAiMessage.from(m));
const messages = history.map(m => OpenAiMessage.from(m));
const response = await this.client.chat.completions.create({
model: model.name,
messages,
@@ -45,7 +45,7 @@ export default class OpenAI implements Client {
let toolCalls: ToolCall[] = [];
if (tool_calls) {
toolCalls = tool_calls.map((tc) => ({
toolCalls = tool_calls.map(tc => ({
function: {
name: tc.function.name,
arguments: JSON.parse(tc.function.arguments),
@@ -66,10 +66,10 @@ export default class OpenAI implements Client {
let allModels = (await this.client.models.list()).data;
if (this.supportedModels !== 'all') {
allModels = allModels.filter((model) => this.supportedModels.includes(model.id));
allModels = allModels.filter(model => this.supportedModels.includes(model.id));
}
return allModels.map((model) => {
return allModels.map(model => {
const { id, ...metadata } = model;
const name = id.replace('models/', ''); // Gemini model ids are prefixed with "model/"

View File

@@ -53,7 +53,7 @@ function toolCalls(message: IMessage): OpenAI.ChatCompletionMessageToolCall[] |
return;
}
return message.toolCalls.map((call) => ({
return message.toolCalls.map(call => ({
id: call.id as string,
type: 'function',
function: {

View File

@@ -93,7 +93,7 @@ Array.prototype.findBy = function <T extends Obj>(
key: string,
value: any
): T | undefined {
return this.find((item) => item[key] == value);
return this.find(item => item[key] == value);
};
/**
@@ -106,5 +106,5 @@ Array.prototype.findBy = function <T extends Obj>(
* ```
*/
Array.prototype.compact = function <T>(this: T[]): Exclude<T, undefined>[] {
return this.filter((i) => i !== undefined) as Exclude<T, undefined>[];
return this.filter(i => i !== undefined) as Exclude<T, undefined>[];
};

View File

@@ -13,7 +13,10 @@ export interface HttpOptions extends RequestInit {
}
export async function fetch(url: string | URL | Request, options?: RequestInit) {
const response: globalThis.Response = await invoke('fetch', { url, options });
const response: globalThis.Response = await invoke('fetch', {
url,
options,
});
const { body, ...init } = response;
return new globalThis.Response(body, init);
}
@@ -37,7 +40,10 @@ export abstract class HttpClient {
async post<T>(uri: string, options: HttpOptions = {}): Promise<Response<T>> {
const raw = Object.remove(options, 'raw');
const raise: boolean | undefined = Object.remove(options, 'raise');
const response = await this.request(uri, { ...options, method: 'POST' });
const response = await this.request(uri, {
...options,
method: 'POST',
});
return raw ? response : await this.parse(response, raise);
}
@@ -51,14 +57,20 @@ export abstract class HttpClient {
async delete<T>(uri: string, options: HttpOptions = {}): Promise<Response<T>> {
const raw = Object.remove(options, 'raw');
const raise: boolean | undefined = Object.remove(options, 'raise');
const response = await this.request(uri, { ...options, method: 'DELETE' });
const response = await this.request(uri, {
...options,
method: 'DELETE',
});
return raw ? response : await this.parse(response, raise);
}
async head<T>(uri: string, options: HttpOptions = {}): Promise<Response<T>> {
const raw = Object.remove(options, 'raw');
const raise: boolean | undefined = Object.remove(options, 'raise');
const response = await this.request(uri, { ...options, method: 'HEAD' });
const response = await this.request(uri, {
...options,
method: 'HEAD',
});
return raw ? response : await this.parse(response, raise);
}

View File

@@ -39,7 +39,9 @@ export class OllamaClient extends HttpClient {
stream: false,
});
const response = (await this.post('/api/chat', { body })) as OllamaResponse;
const response = (await this.post('/api/chat', {
body,
})) as OllamaResponse;
let thought: string | undefined;
let content: string = response.message.content
@@ -75,7 +77,12 @@ export class OllamaClient extends HttpClient {
async connected(): Promise<boolean> {
return (
((await this.get('', { raw: true, timeout: 500 })) as globalThis.Response).status == 200
(
(await this.get('', {
raw: true,
timeout: 500,
})) as globalThis.Response
).status == 200
);
}

View File

@@ -53,7 +53,7 @@ function _log(args: any[], level: Level) {
if (typeof args[0] !== 'string') {
console[level](...fmt(level, ''), ...args);
} else {
console[level](...args.flatMap((arg) => (typeof arg === 'string' ? fmt(level, arg) : arg)));
console[level](...args.flatMap(arg => (typeof arg === 'string' ? fmt(level, arg) : arg)));
}
}
@@ -70,7 +70,7 @@ function fmt(level: Level, text: string): string[] {
function colorize(text: string): string[] {
let args: string[] = [];
text = text.replace(/\[\w+\]/g, (block) => {
text = text.replace(/\[\w+\]/g, block => {
const code = block.replace('[', '').replace(']', '');
const color = (colors as Obj)[code];
args.push(`color: ${color};`);

View File

@@ -10,7 +10,7 @@ export * from '$lib/mcp.d';
// can send to the LLM.
//
export async function getMCPTools(session: ISession): Promise<Tool[]> {
return (await invoke<McpTool[]>('get_mcp_tools', { sessionId: session.id })).map((tool) => {
return (await invoke<McpTool[]>('get_mcp_tools', { sessionId: session.id })).map(tool => {
return {
type: 'function',
function: {

View File

@@ -57,13 +57,13 @@ export default class App extends Model<IApp, Row>('apps') {
};
static hasContext(app: IApp): boolean {
return app.nodes?.find((n) => n.type == NodeType.Context) !== undefined;
return app.nodes?.find(n => n.type == NodeType.Context) !== undefined;
}
static context(app: IApp): string {
return app.nodes
.filter((n) => n.type == NodeType.Context)
.map((n) => n.config.value)
.filter(n => n.type == NodeType.Context)
.map(n => n.config.value)
.join('\n\n');
}
@@ -73,7 +73,7 @@ export default class App extends Model<IApp, Row>('apps') {
}
static removeNode(app: IApp, node: Node): IApp {
app.nodes = app.nodes.filter((n) => n.uuid !== node.uuid);
app.nodes = app.nodes.filter(n => n.uuid !== node.uuid);
return app;
}
@@ -102,7 +102,7 @@ export default class App extends Model<IApp, Row>('apps') {
]);
if (result.rowsAffected == 1) {
app.mcpServers = app.mcpServers.filter((m) => m.id == mcpServer.id);
app.mcpServers = app.mcpServers.filter(m => m.id == mcpServer.id);
return app.mcpServers;
}

View File

@@ -136,7 +136,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
static async sync(): Promise<void> {
repo = [];
(await this.query(`SELECT * FROM ${table}`)).forEach((record) => this.syncOne(record));
(await this.query(`SELECT * FROM ${table}`)).forEach(record => this.syncOne(record));
info(`[green]✔ synced ${table}`);
}
@@ -175,7 +175,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
* Find an individual record by`id`.
*/
static find(id: number | string): Interface {
return this.all().find((m) => m.id == Number(id)) as Interface;
return this.all().find(m => m.id == Number(id)) as Interface;
}
/**
@@ -189,7 +189,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
* Find a collection of records by a set of the model's properties.
*/
static where(params: Partial<Interface>): Interface[] {
return repo.filter((m) => {
return repo.filter(m => {
return Object.entries(params).every(([key, value]) => m[key] == value);
});
}
@@ -329,7 +329,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
).rowsAffected >= 1;
if (success) {
instances.forEach((instance) => {
instances.forEach(instance => {
this.syncRemove(instance);
});
}
@@ -343,7 +343,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
protected static async query(sql: string, values: unknown[] = []): Promise<Interface[]> {
const result: Row[] = await (await this.db()).select<Row[]>(sql, values);
return await Promise.all(result.map(async (row) => await this.fromSql(row)));
return await Promise.all(result.map(async row => await this.fromSql(row)));
}
/**
@@ -374,7 +374,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
* Remove an instance from the repo
*/
private static syncRemove(instance: Interface) {
repo = repo.filter((i) => i.id !== instance.id);
repo = repo.filter(i => i.id !== instance.id);
}
/**
@@ -389,7 +389,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
* This leaves only persisted records(ones with an`id`).
*/
private static removeEphemeralInstances() {
repo = repo.filter((record) => record.id !== undefined);
repo = repo.filter(record => record.id !== undefined);
}
/**
@@ -490,7 +490,7 @@ export function BareModel<T extends Obj>() {
}
static delete(instance: T) {
repo = repo.filter((i) => i !== instance);
repo = repo.filter(i => i !== instance);
}
static all(): T[] {

View File

@@ -14,11 +14,7 @@ interface Row {
value: string;
}
type ConfigKey =
| 'latest-session-id'
| 'welcome-agreed'
| 'skipped-version'
| 'default-model';
type ConfigKey = 'latest-session-id' | 'welcome-agreed' | 'skipped-version' | 'default-model';
export default class Config extends Model<IConfig, Row>('config') {
@getset('latest-session-id')
@@ -81,7 +77,7 @@ export default class Config extends Model<IConfig, Row>('config') {
}
function getset(key: ConfigKey) {
return function(target: object, property: string) {
return function (target: object, property: string) {
function get() {
return Config.get(key);
}

View File

@@ -75,6 +75,6 @@ export default class Engine extends BareModel<IEngine>() {
});
}
Model.reset(this.all().flatMap((engine) => engine.models));
Model.reset(this.all().flatMap(engine => engine.models));
}
}

View File

@@ -10,7 +10,7 @@ export function from(message: IMessage): Message {
return {
role: message.role,
content: message.content,
tool_calls: message.toolCalls?.map((c) => ({
tool_calls: message.toolCalls?.map(c => ({
function: {
name: c.function.name,
arguments: c.function.arguments,

View File

@@ -53,7 +53,7 @@ function toolCalls(message: IMessage): OpenAI.ChatCompletionMessageToolCall[] |
return;
}
return message.toolCalls.map((call) => ({
return message.toolCalls.map(call => ({
id: call.id as string,
type: 'function',
function: {

View File

@@ -86,7 +86,7 @@ export default class Session extends Base<ISession, Row>('sessions') {
static async removeMcpServer(session: ISession, server: IMcpServer): Promise<ISession> {
session.config.enabledMcpServers = session.config.enabledMcpServers.filter(
(s) => s !== server.name
s => s !== server.name
);
return await this.update(session);
}

View File

@@ -4,13 +4,13 @@ import Message from '$lib/models/message';
export async function migrate() {
await Promise.all(
Message.all().map(async (message) => {
Message.all().map(async message => {
if (message.toolCalls.length == 0) {
return;
}
await Promise.all(
message.toolCalls.map(async (tc) => {
message.toolCalls.map(async tc => {
// Ollama tool calls didn't always have an `id`. Add one
// now if that's the case.
if (!tc.id) {

View File

@@ -25,12 +25,12 @@
}
}
onNavigate((navigation) => {
onNavigate(navigation => {
if (!document.startViewTransition) {
return;
}
return new Promise((resolve) => {
return new Promise(resolve => {
document.startViewTransition(async () => {
resolve();
await navigation.complete;

View File

@@ -7,11 +7,7 @@
import Svg from '$components/Svg.svelte';
import Updater from '$components/Updater.svelte';
import Welcome from '$components/Welcome.svelte';
import startup, {
type Condition,
type OnSuccess,
StartupCheck,
} from '$lib/startup';
import startup, { type Condition, type OnSuccess, StartupCheck } from '$lib/startup';
const messages = {
[StartupCheck.NoModels]: 'No Engines Connected',
@@ -73,9 +69,6 @@
{/if}
</Modal>
{:else}
<Svg
name="Logo"
class="text-dark fixed top-[50%] left-[50%] h-32 w-32 -translate-[50%]"
/>
<Svg name="Logo" class="text-dark fixed top-[50%] left-[50%] h-32 w-32 -translate-[50%]" />
{/if}
</Layout>

View File

@@ -20,14 +20,12 @@
import Session, { type ISession } from '$lib/models/session';
const session: ISession = $derived(Session.find(page.params.session_id));
const model: IModel | undefined = $derived(
Model.find(session.config.model)
);
const model: IModel | undefined = $derived(Model.find(session.config.model));
const sessions: ISession[] = $derived(Session.all());
const mcpServers: IMcpServer[] = $derived(McpServer.all());
const engines: IEngine[] = $derived(Engine.all());
const hasModels = $derived(engines.flatMap((e) => e.models).length > 0);
const hasModels = $derived(engines.flatMap(e => e.models).length > 0);
let advancedIsOpen = $state(false);
@@ -37,8 +35,8 @@
}
async function startMcpServers(session: ISession) {
session.config.enabledMcpServers.forEach(async (name) => {
const server = mcpServers.find((s) => s.name == name);
session.config.enabledMcpServers.forEach(async name => {
const server = mcpServers.find(s => s.name == name);
if (server) {
await startMcpServer(server);
@@ -102,9 +100,7 @@
</script>
{#snippet titlebar()}
<Flex
class="border-r-light z-10 h-full w-[300px] items-center border-r px-8 pr-4"
>
<Flex class="border-r-light z-10 h-full w-[300px] items-center border-r px-8 pr-4">
<h1 class="grow font-[500]">Chat</h1>
<button
onclick={() => addSession()}
@@ -118,18 +114,14 @@
<Layout {titlebar}>
<Flex class="h-full items-start">
<Flex
class="border-light bg-medium h-content w-[300px] flex-col overflow-auto border-r"
>
<Flex class="border-light bg-medium h-content w-[300px] flex-col overflow-auto border-r">
{#each sessions as sess (sess.id)}
<Flex
class={`text-medium border-b-light w-full justify-between border-b
border-l-transparent text-sm ${sess.id == session?.id ? '!border-l-purple border-l' : ''}`}
>
<Menu items={menuItems(sess)}>
<Deleteable
ondelete={async () => await deleteSession(sess)}
>
<Deleteable ondelete={async () => await deleteSession(sess)}>
<Link
href={`/chat/${sess.id}`}
class="w-full py-3 pl-8 text-left"
@@ -145,17 +137,13 @@
</Flex>
{#if session}
<Flex
class="bg-medium h-full w-[calc(100%-600px)] grow items-start"
>
<Flex class="bg-medium h-full w-[calc(100%-600px)] grow items-start">
{#key session.id}
<Chat {session} bind:model={session.config.model} />
{/key}
</Flex>
<Flex
class="bg-medium border-light h-full w-[300px] flex-col items-start border-l p-4"
>
<Flex class="bg-medium border-light h-full w-[300px] flex-col items-start border-l p-4">
{#key session.config.model}
<ModelMenu
{engines}
@@ -190,10 +178,8 @@
<Flex class="text-light z-0 mb-4 ml-2">
<Toggle
label={server.name}
value={Session.hasMcpServer(
session,
server.name
) && model?.supportsTools
value={Session.hasMcpServer(session, server.name) &&
model?.supportsTools
? 'on'
: 'off'}
disabled={!model?.supportsTools}
@@ -209,19 +195,14 @@
class="text-dark mb-4 ml-2 self-start text-sm font-medium hover:cursor-pointer"
onclick={() => toggleAdvanced()}
>
Advanced <span class="ml-4"
>{advancedIsOpen ? '⏷' : '⏵'}</span
>
Advanced <span class="ml-4">
{advancedIsOpen ? '⏷' : '⏵'}
</span>
</button>
{#if advancedIsOpen}
<Flex
class="m-auto w-full flex-col items-start px-4 pt-4 pl-0"
>
<label
for="ctx_num"
class="text-medium mb-1 ml-2 text-sm"
>
<Flex class="m-auto w-full flex-col items-start px-4 pt-4 pl-0">
<label for="ctx_num" class="text-medium mb-1 ml-2 text-sm">
Context Window Size
</label>
<input
@@ -234,10 +215,7 @@
bind:value={session.config.contextWindow}
/>
<label
for="temperature"
class="text-medium mt-4 mb-1 ml-2 text-sm"
>
<label for="temperature" class="text-medium mt-4 mb-1 ml-2 text-sm">
Temperature
</label>

View File

@@ -28,7 +28,10 @@
{#if config.type !== 'stdio'}
<Flex class="text-red w-full">
<Svg name="Warning" class="mr-8 h-6 w-6" />
<p>Tome only supports <code>stdio</code> MCP servers</p>
<p>
Tome only supports <code>stdio</code>
MCP servers
</p>
</Flex>
{:else}
<Flex class="w-full flex-col items-start">