├── src ├── templates.js ├── app.pcss ├── vite-env.d.ts ├── main.js ├── svelte-marked │ ├── markdown │ │ ├── extensions │ │ │ ├── index.ts │ │ │ └── container.ts │ │ ├── components │ │ │ ├── MarkdownInlineKatex.svelte │ │ │ ├── MarkdownBlockKatex.svelte │ │ │ ├── MarkdownBr.svelte │ │ │ ├── MarkdownHr.svelte │ │ │ ├── MarkdownText.svelte │ │ │ ├── MarkdownSpace.svelte │ │ │ ├── MarkdownEm.svelte │ │ │ ├── MarkdownDel.svelte │ │ │ ├── MarkdownDfn.svelte │ │ │ ├── MarkdownParagraph.svelte │ │ │ ├── MarkdownStrong.svelte │ │ │ ├── list │ │ │ │ ├── MarkdownListItem.svelte │ │ │ │ └── MarkdownList.svelte │ │ │ ├── MarkdownBloquote.svelte │ │ │ ├── code │ │ │ │ ├── MarkdownCodeSpan.svelte │ │ │ │ └── MarkdownCode.svelte │ │ │ ├── MarkdownHtml.svelte │ │ │ ├── MarkdownImage.svelte │ │ │ ├── MarkdownLink.svelte │ │ │ ├── MarkdownHeading.svelte │ │ │ ├── table │ │ │ │ └── MarkdownTable.svelte │ │ │ └── index.ts │ │ ├── suppressWarnings.ts │ │ ├── MarkdownTokens.svelte │ │ ├── MarkdownToken.svelte │ │ ├── Markdown.svelte │ │ └── markedConfiguration.ts │ ├── utils │ │ └── url.ts │ └── index.ts ├── Tooltip.svelte ├── Icon.svelte ├── ToolPill.svelte ├── actions.js ├── Checkbox.svelte ├── date.js ├── Button.svelte ├── ClientTool.svelte ├── ToolcallButton.svelte ├── localstorage.js ├── share.js ├── MessageContent.svelte ├── Modal.svelte ├── FilePreview.svelte ├── stores.js ├── Choice.svelte ├── CompanyLogo.svelte ├── ReasoningEffortRangeDropdown.svelte ├── KnobsSidebar.svelte ├── util.js ├── marked-katex-extension │ └── index.js ├── svelte-json-view │ └── JsonView.svelte ├── tools.js ├── ToolDropdown.svelte ├── ModelSelector.svelte ├── tooltip.js ├── Toolcall.svelte ├── ClientToolSetting.svelte ├── sync.js ├── providers.js ├── convo.js └── SettingsModal.svelte ├── public ├── ios │ └── 180.png ├── logos │ ├── cohere.png │ ├── google.png │ ├── groq.png │ ├── meta.png │ ├── nous.png │ ├── openai.ico │ ├── deepseek.ico │ ├── mistral.png │ ├── anthropic.jpeg │ ├── qwen.svg │ └── perplexity.svg ├── macos │ └── icon.png ├── android │ └── android-launchericon-512-512.png ├── manifest.json ├── convert-favicon-to-mac-icon.sh └── llum5.svg ├── .vscode └── extensions.json ├── svelte.config.js ├── .prettierignore ├── sync ├── go.mod └── go.sum ├── postcss.config.cjs ├── .prettierrc ├── .gitignore ├── vite.config.js ├── index.html ├── server ├── go.mod ├── toolfns │ ├── generated_toolfns.go │ └── toolfns.go ├── release.sh ├── main.go └── go.sum ├── package.json ├── jsconfig.json ├── LICENSE ├── generatefeathericons.sh ├── tailwind.config.cjs └── README.md /src/templates.js: -------------------------------------------------------------------------------- 1 | export const conversationTemplates = {}; 2 | -------------------------------------------------------------------------------- /public/ios/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/ios/180.png -------------------------------------------------------------------------------- /src/app.pcss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["svelte.svelte-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /public/logos/cohere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/logos/cohere.png -------------------------------------------------------------------------------- /public/logos/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/logos/google.png -------------------------------------------------------------------------------- /public/logos/groq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/logos/groq.png -------------------------------------------------------------------------------- /public/logos/meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/logos/meta.png -------------------------------------------------------------------------------- /public/logos/nous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/logos/nous.png -------------------------------------------------------------------------------- /public/logos/openai.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/logos/openai.ico -------------------------------------------------------------------------------- /public/macos/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/macos/icon.png -------------------------------------------------------------------------------- /public/logos/deepseek.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/logos/deepseek.ico -------------------------------------------------------------------------------- /public/logos/mistral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/logos/mistral.png -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /public/logos/anthropic.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/logos/anthropic.jpeg -------------------------------------------------------------------------------- /public/android/android-launchericon-512-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakkor/llum/HEAD/public/android/android-launchericon-512-512.png -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import './app.pcss'; 2 | import App from './App.svelte'; 3 | 4 | const app = new App({ 5 | target: document.getElementById('app'), 6 | }); 7 | 8 | export default app; 9 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/extensions/index.ts: -------------------------------------------------------------------------------- 1 | import containerExtension from './container' 2 | 3 | const extensions = { 4 | containerExtension, 5 | } 6 | 7 | export { extensions } 8 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 2 | import { fastDimension } from 'svelte-fast-dimension'; 3 | 4 | export default { 5 | preprocess: [vitePreprocess({}), fastDimension()], 6 | }; 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /dist 5 | /.svelte-kit 6 | /package 7 | .env 8 | .env.* 9 | !.env.example 10 | 11 | # Ignore files for PNPM, NPM and YARN 12 | pnpm-lock.yaml 13 | package-lock.json 14 | yarn.lock 15 | -------------------------------------------------------------------------------- /sync/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zakkor/sync 2 | 3 | go 1.23.4 4 | 5 | require ( 6 | github.com/go-chi/chi/v5 v5.2.1 7 | github.com/go-chi/cors v1.2.1 8 | github.com/go-chi/httprate v0.14.1 9 | ) 10 | 11 | require github.com/cespare/xxhash/v2 v2.3.0 // indirect 12 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownInlineKatex.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | {@html html} 10 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const tailwindcss = require('tailwindcss'); 2 | const tailwindcssNesting = require('tailwindcss/nesting'); 3 | const autoprefixer = require('autoprefixer'); 4 | 5 | const config = { 6 | plugins: [tailwindcss(), tailwindcssNesting(), autoprefixer], 7 | }; 8 | 9 | module.exports = config; 10 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownBlockKatex.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | {@html html} 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "es5", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], 7 | "overrides": [ 8 | { 9 | "files": "*.svelte", 10 | "options": { 11 | "parser": "svelte" 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownBr.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownHr.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownText.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownSpace.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Tooltip.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownEm.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownDel.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownDfn.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownParagraph.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |

11 | 12 |

13 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownStrong.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/list/MarkdownListItem.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
  • 12 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownBloquote.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |
    11 | 12 |
    13 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/code/MarkdownCodeSpan.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | {token.raw.slice(1, token.raw.length - 1)} 11 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/suppressWarnings.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import { onMount } from 'svelte' 3 | 4 | export function suppressWarnings() { 5 | const origWarn = console.warn 6 | 7 | console.warn = (message: string) => { 8 | if (message.includes('unknown prop')) return 9 | if (message.includes('unexpected slot')) return 10 | origWarn(message) 11 | } 12 | 13 | onMount(() => { 14 | console.warn = origWarn 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownHtml.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | {token.text} 13 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "llum", 3 | "short_name": "llum", 4 | "start_url": "/", 5 | "display": "standalone", 6 | "background_color": "#ffffff", 7 | "theme_color": "#ffffff", 8 | "description": "A simple, lightweight, and open LLM playground.", 9 | "icons": [ 10 | { 11 | "src": "macos/icon.png", 12 | "sizes": "512x512" 13 | }, 14 | { 15 | "src": "android/android-launchericon-512-512.png", 16 | "sizes": "512x512" 17 | }, 18 | { 19 | "src": "ios/180.png", 20 | "sizes": "180x180" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/MarkdownTokens.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | {#if tokens} 14 | {#each tokens as token} 15 | 16 | {/each} 17 | {/if} 18 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownImage.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | {token.text} 16 | 17 | 22 | -------------------------------------------------------------------------------- /src/svelte-marked/utils/url.ts: -------------------------------------------------------------------------------- 1 | import type Slugger from 'github-slugger' 2 | 3 | export function joinUrlPaths(...paths: string[]): string { 4 | return ( 5 | '/' + 6 | paths 7 | .flatMap((path) => path.split('/')) 8 | .filter((path) => !!path) 9 | .join('/') 10 | ) 11 | } 12 | 13 | export function isRelative(url: string): boolean { 14 | return url.startsWith('/') || url.startsWith('#') 15 | } 16 | 17 | export function generatePathSegment(name: string, slugger: Slugger) { 18 | return slugger.slug(name).replace(/--+/g, '-') // Replaces -- with - 19 | } 20 | -------------------------------------------------------------------------------- /src/Icon.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 25 | {@html icon} 26 | 27 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownLink.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist-client 12 | dist-client.tar.gz 13 | dist 14 | dist-ssr 15 | *.local 16 | 17 | server/sandbox 18 | server/server 19 | server/testdata 20 | indexed 21 | server/toolfns/cmd/embed/embed 22 | 23 | deploy.sh 24 | jackin.sh 25 | tunnel.sh 26 | 27 | # Editor directories and files 28 | .vscode/* 29 | !.vscode/extensions.json 30 | .idea 31 | .DS_Store 32 | *.suo 33 | *.ntvs* 34 | *.njsproj 35 | *.sln 36 | *.sw? 37 | 38 | storage.json 39 | sync/sync 40 | -------------------------------------------------------------------------------- /src/ToolPill.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/MarkdownHeading.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/convert-favicon-to-mac-icon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # Create necessary directories 4 | mkdir -p android ios macos 5 | 6 | # Create macOS icon with padding 7 | padding=50 && convert -background none -density 300 llum5.svg -resize $((512-2*$padding))x$((512-2*$padding)) -gravity center -extent 512x512 macos/icon.png 8 | 9 | # Create Android launcher icon (no padding) 10 | convert -background none -density 300 llum5.svg -resize 512x512 android/android-launchericon-512-512.png 11 | 12 | # Create iOS icon (no padding) 13 | convert -background none -density 300 llum5.svg -resize 180x180 ios/180.png 14 | 15 | echo "✅ All icons generated successfully!" -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { svelte } from '@sveltejs/vite-plugin-svelte'; 3 | import viteCompression from 'vite-plugin-compression'; 4 | 5 | export default defineConfig(function () { 6 | const buildTimestamp = new Date(); 7 | return { 8 | plugins: [ 9 | svelte(), 10 | viteCompression({ 11 | filter: /^(?!.*pdf\.worker\.min-[A-Z0-9]+\.mjs$).*\.(js|mjs|json|css|html)$/i, 12 | }), 13 | ], 14 | define: { 15 | 'import.meta.env.BUILD_TIMESTAMP': JSON.stringify(buildTimestamp.toLocaleString()), 16 | }, 17 | optimizeDeps: { 18 | include: ['svelte-fast-dimension/action'], 19 | }, 20 | }; 21 | }); 22 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/list/MarkdownList.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | {#each token.items as item} 16 | 17 | {/each} 18 | 19 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/MarkdownToken.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | {#if renderers[token.type]} 14 | 15 | {#if 'tokens' in token && token['tokens']} 16 | 17 | {:else} 18 | {token.raw} 19 | {/if} 20 | 21 | {/if} 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 17 | llum 18 | 19 | 20 |
    21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sync/go.sum: -------------------------------------------------------------------------------- 1 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 2 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 3 | github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= 4 | github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= 5 | github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= 6 | github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= 7 | github.com/go-chi/httprate v0.14.1 h1:EKZHYEZ58Cg6hWcYzoZILsv7ppb46Wt4uQ738IRtpZs= 8 | github.com/go-chi/httprate v0.14.1/go.mod h1:TUepLXaz/pCjmCtf/obgOQJ2Sz6rC8fSf5cAt5cnTt0= 9 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 10 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 11 | -------------------------------------------------------------------------------- /src/actions.js: -------------------------------------------------------------------------------- 1 | const flashClasses = { 2 | success: '!bg-green-200 !border-green-300', 3 | error: '!bg-red-100 !border-red-300', 4 | }; 5 | 6 | export function flash(node) { 7 | const successFn = () => { 8 | const classes = flashClasses['success'].split(' '); 9 | node.classList.add(...classes); 10 | setTimeout(() => { 11 | node.classList.remove(...classes); 12 | }, 1000); 13 | }; 14 | const errorFn = () => { 15 | const classes = flashClasses['error'].split(' '); 16 | node.classList.add(...classes); 17 | setTimeout(() => { 18 | node.classList.remove(...classes); 19 | }, 1000); 20 | }; 21 | 22 | node.addEventListener('flashSuccess', successFn); 23 | node.addEventListener('flashError', errorFn); 24 | 25 | return { 26 | destroy() { 27 | node.removeEventListener('flashSuccess', successFn); 28 | node.removeEventListener('flashError', errorFn); 29 | }, 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /server/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zakkor/server 2 | 3 | go 1.22.2 4 | 5 | toolchain go1.22.4 6 | 7 | require ( 8 | github.com/PuerkitoBio/goquery v1.9.2 9 | github.com/byte-sat/llum-tools v0.0.0-20240622105019-b64412474dd9 10 | github.com/go-chi/chi/v5 v5.0.14 11 | github.com/go-chi/cors v1.2.1 12 | github.com/noonien/codoc v0.0.0-20240519154704-25b5fe95209b 13 | github.com/playwright-community/playwright-go v0.4501.0 14 | github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82 15 | ) 16 | 17 | require ( 18 | github.com/andybalholm/cascadia v1.3.2 // indirect 19 | github.com/deckarep/golang-set/v2 v2.6.0 // indirect 20 | github.com/go-jose/go-jose/v3 v3.0.3 // indirect 21 | github.com/go-stack/stack v1.8.1 // indirect 22 | go.uber.org/multierr v1.11.0 // indirect 23 | golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect 24 | golang.org/x/net v0.27.0 // indirect 25 | ) 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "llum", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@sveltejs/vite-plugin-svelte": "^3.0.1", 13 | "@tailwindcss/typography": "^0.5.10", 14 | "autoprefixer": "^10.4.16", 15 | "postcss": "^8.4.32", 16 | "postcss-load-config": "^5.0.2", 17 | "prettier": "^3.2.5", 18 | "prettier-plugin-svelte": "^3.2.3", 19 | "prettier-plugin-tailwindcss": "^0.5.14", 20 | "svelte": "^4.2.12", 21 | "svelte-fast-dimension": "^1.1.0", 22 | "tailwindcss": "^3.3.6", 23 | "vite": "^5.2.0", 24 | "vite-plugin-compression": "^0.5.1" 25 | }, 26 | "dependencies": { 27 | "github-slugger": "^2.0.0", 28 | "katex": "^0.16.10", 29 | "marked": "^15.0.7", 30 | "pdfjs-dist": "^4.6.82", 31 | "uuid": "^11.1.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Checkbox.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | {#if checked} 24 | 25 | {/if} 26 | 27 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/table/MarkdownTable.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | {#each token.header as item} 15 | 18 | {/each} 19 | 20 | 21 | 22 | {#each token.rows as row} 23 | 24 | {#each row as col} 25 | 28 | {/each} 29 | 30 | {/each} 31 | 32 |
    16 | 17 |
    26 | 27 |
    33 | -------------------------------------------------------------------------------- /src/date.js: -------------------------------------------------------------------------------- 1 | export function getRelativeDate(inputDate) { 2 | const today = new Date(); 3 | const yesterday = new Date(today); 4 | yesterday.setDate(yesterday.getDate() - 1); 5 | 6 | // Resetting the time part for accurate comparison 7 | const input = new Date(inputDate); 8 | input.setHours(0, 0, 0, 0); 9 | today.setHours(0, 0, 0, 0); 10 | yesterday.setHours(0, 0, 0, 0); 11 | 12 | const oneDay = 24 * 60 * 60 * 1000; // milliseconds in one day 13 | const sevenDays = 7 * oneDay; 14 | const thirtyDays = 30 * oneDay; 15 | const diff = today.getTime() - input.getTime(); 16 | 17 | if (diff === 0) { 18 | return 'Today'; 19 | } else if (diff <= oneDay) { 20 | return 'Yesterday'; 21 | } else if (diff <= sevenDays) { 22 | return 'Previous 7 days'; 23 | } else if (diff <= thirtyDays) { 24 | return 'Previous 30 days'; 25 | } else { 26 | // Formatting the date as "Month" 27 | return input.toLocaleDateString('en-US', { month: 'long' }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/toolfns/generated_toolfns.go: -------------------------------------------------------------------------------- 1 | // generated @ 2025-03-08T22:54:18+02:00 by gendoc 2 | package toolfns 3 | 4 | import "github.com/noonien/codoc" 5 | 6 | func init() { 7 | codoc.Register(codoc.Package{ 8 | ID: "github.com/zakkor/server/toolfns", 9 | Name: "toolfns", 10 | Doc: "generated @ 2025-03-08T22:54:11+02:00 by gendoc", 11 | Functions: map[string]codoc.Function{ 12 | "NewGroup": { 13 | Name: "NewGroup", 14 | Args: []string{ 15 | "name", 16 | "fns", 17 | }, 18 | }, 19 | "Shell": { 20 | Name: "Shell", 21 | Doc: "Executes the given bash command and returns the output of the command.\ncommand: The bash command to execute.", 22 | Args: []string{ 23 | "command", 24 | }, 25 | }, 26 | "init": { 27 | Name: "init", 28 | }, 29 | }, 30 | Structs: map[string]codoc.Struct{ 31 | "ContentTypeResponse": { 32 | Name: "ContentTypeResponse", 33 | }, 34 | "Group": { 35 | Name: "Group", 36 | }, 37 | }, 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /src/Button.svelte: -------------------------------------------------------------------------------- 1 | 23 | 24 | 35 | -------------------------------------------------------------------------------- /src/ClientTool.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
    13 | {definition.name}({'{ '}{definition.arguments.map(arg => `${arg.name}: ${arg.type}`).join(', ')}{' }'}) 14 | 15 | 23 | 31 |
    32 | -------------------------------------------------------------------------------- /src/ToolcallButton.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 36 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "bundler", 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | /** 7 | * svelte-preprocess cannot figure out whether you have 8 | * a value or a type, so tell TypeScript to enforce using 9 | * `import type` instead of `import` for Types. 10 | */ 11 | "verbatimModuleSyntax": true, 12 | "isolatedModules": true, 13 | "resolveJsonModule": true, 14 | /** 15 | * To have warnings / errors of the Svelte compiler at the 16 | * correct position, enable source maps by default. 17 | */ 18 | "sourceMap": true, 19 | "esModuleInterop": true, 20 | "skipLibCheck": true, 21 | /** 22 | * Typecheck JS in `.svelte` and `.js` files by default. 23 | * Disable this if you'd like to use dynamic types. 24 | */ 25 | "checkJs": true 26 | }, 27 | /** 28 | * Use global.d.ts instead of compilerOptions.types 29 | * to avoid limiting type declarations. 30 | */ 31 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] 32 | } 33 | -------------------------------------------------------------------------------- /src/svelte-marked/index.ts: -------------------------------------------------------------------------------- 1 | import * as MarkdownComponents from './markdown/components' 2 | import { extensions } from './markdown/extensions' 3 | import { generatePathSegment, isRelative, joinUrlPaths } from './utils/url' 4 | import Markdown from './markdown/Markdown.svelte' 5 | import MarkdownToken from './markdown/MarkdownToken.svelte' 6 | import MarkdownTokens from './markdown/MarkdownTokens.svelte' 7 | import type { 8 | ContainerOptions, 9 | TokenExtractionParameters, 10 | TokenExtractor, 11 | } from './markdown/extensions/container' 12 | import type { 13 | MarkdownOptions, 14 | RendererType, 15 | Renderers, 16 | } from './markdown/markedConfiguration' 17 | 18 | export default Markdown 19 | export { MarkdownComponents } 20 | export { MarkdownToken, MarkdownTokens } 21 | export { extensions } 22 | export type { 23 | ContainerOptions, 24 | TokenExtractionParameters, 25 | TokenExtractor, 26 | MarkdownOptions, 27 | RendererType, 28 | Renderers, 29 | } 30 | 31 | const urlUtils = { 32 | joinUrlPaths, 33 | isRelative, 34 | generatePathSegment, 35 | } 36 | 37 | export { urlUtils } 38 | -------------------------------------------------------------------------------- /src/localstorage.js: -------------------------------------------------------------------------------- 1 | import { writable, get } from 'svelte/store'; 2 | 3 | export function persisted(key, initial) { 4 | const store = writable(initial); 5 | const { subscribe, set } = store; 6 | 7 | if (initial !== undefined && localStorage.getItem(key) === null) { 8 | // If initial data is passed in, use it to initialize only if there is no persisted data. 9 | const toPersist = JSON.stringify(initial); 10 | localStorage.setItem(key, toPersist); 11 | } else { 12 | // Otherwise, read persisted data from localStorage and set value of store to that. 13 | const persisted = JSON.parse(localStorage.getItem(key)); 14 | set(persisted); 15 | } 16 | 17 | return { 18 | subscribe, 19 | set: (data) => { 20 | // On set persist data, then update store value. 21 | const toPersist = JSON.stringify(data); 22 | localStorage.setItem(key, toPersist); 23 | set(data); 24 | }, 25 | update: (updateFn) => { 26 | const updatedData = updateFn(get(store)); 27 | const toPersist = JSON.stringify(updatedData); 28 | localStorage.setItem(key, toPersist); 29 | set(updatedData); 30 | }, 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 zakkor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /server/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Define the list of GOOS values 4 | GOOS_VALUES=("darwin" "linux" "windows") 5 | 6 | rm -r ./dist 7 | mkdir -p ./dist 8 | 9 | # Loop through each GOOS value 10 | for GOOS in "${GOOS_VALUES[@]}"; do 11 | # Set GOARCH to arm64 for Mac (darwin) 12 | if [ "$GOOS" == "darwin" ]; then 13 | GOARCH="arm64" 14 | else 15 | GOARCH="amd64" 16 | fi 17 | 18 | # Set the output file name 19 | OUTPUT_FILE="./dist/llum-${GOOS}-${GOARCH}" 20 | 21 | # Append .exe for Windows builds 22 | if [ "$GOOS" == "windows" ]; then 23 | OUTPUT_FILE="${OUTPUT_FILE}.exe" 24 | fi 25 | 26 | echo "Building for GOOS=$GOOS GOARCH=$GOARCH" 27 | 28 | # Build the Go project 29 | GOOS=$GOOS GOARCH=$GOARCH go build -tags release -o $OUTPUT_FILE 30 | 31 | # Check if the build was successful 32 | if [ $? -ne 0 ]; then 33 | echo "Build failed for GOOS=$GOOS GOARCH=$GOARCH" 34 | exit 1 35 | fi 36 | done 37 | 38 | # Create a new release with the new builds 39 | gh release create v0.0.6 dist/* --notes '' && echo "Build and release process completed successfully." 40 | -------------------------------------------------------------------------------- /server/toolfns/toolfns.go: -------------------------------------------------------------------------------- 1 | package toolfns 2 | 3 | import ( 4 | "github.com/byte-sat/llum-tools/tools" 5 | "log" 6 | "os/exec" 7 | ) 8 | 9 | // Note: Generated filename is significant. The init function for the generated file must run first. 10 | //go:generate go run github.com/noonien/codoc/cmd/codoc@latest -out generated_toolfns.go -pkg toolfns . 11 | 12 | var ToolGroups []*Group 13 | 14 | func init() { 15 | ToolGroups = []*Group{ 16 | NewGroup("System", 17 | Shell, 18 | ), 19 | } 20 | } 21 | 22 | type Group struct { 23 | Name string `json:"name"` 24 | Repo *tools.Repo `json:"-"` 25 | } 26 | 27 | func NewGroup(name string, fns ...any) *Group { 28 | repo, err := tools.New(nil, fns...) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | return &Group{ 33 | Name: name, 34 | Repo: repo, 35 | } 36 | } 37 | 38 | type ContentTypeResponse struct { 39 | ContentType string `json:"contentType"` 40 | Content string `json:"content"` 41 | } 42 | 43 | // Executes the given bash command and returns the output of the command. 44 | // command: The bash command to execute. 45 | func Shell(command string) string { 46 | cmd := exec.Command("bash", "-c", command) 47 | out, err := cmd.CombinedOutput() 48 | if err != nil { 49 | return err.Error() + "\n" + string(out) 50 | } 51 | return string(out) 52 | } 53 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/Markdown.svelte: -------------------------------------------------------------------------------- 1 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /generatefeathericons.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check if the target directory is provided as an argument 4 | if [ -z "$1" ]; then 5 | echo "Usage: $0 " 6 | exit 1 7 | fi 8 | 9 | # Define the target directory and output file 10 | target_directory="$1" 11 | output_file="./src/feather.js" 12 | 13 | # Start the feather.js file with a comment 14 | echo "// feather.js" > $output_file 15 | 16 | # Iterate over all .svg files in the target directory 17 | for svg_file in "$target_directory"/*.svg; do 18 | # Check if there are no .svg files in the directory 19 | if [ ! -e "$svg_file" ]; then 20 | echo "No .svg files found in the directory." 21 | exit 1 22 | fi 23 | 24 | # Extract the base name of the file (without extension) 25 | base_name=$(basename "$svg_file" .svg) 26 | 27 | # Extract the inner contents of the SVG file 28 | inner_content=$(sed -n 's/.*]*>\(.*\)<\/svg>.*/\1/p' "$svg_file") 29 | 30 | # Convert the base name to camel case for the variable name 31 | var_name=$(echo "$base_name" | awk -F'-' '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1' OFS='') 32 | 33 | # Append the extracted content to the feather.js file 34 | echo "export const fe$var_name = \`$inner_content\`;" >> $output_file 35 | done 36 | 37 | echo "feather.js file has been created successfully." -------------------------------------------------------------------------------- /src/share.js: -------------------------------------------------------------------------------- 1 | export async function compressAndEncode(data) { 2 | const json = JSON.stringify(data); 3 | const encoder = new TextEncoder(); 4 | const uint8Array = encoder.encode(json); 5 | 6 | const compressedStream = new CompressionStream('gzip'); 7 | const compressed = new Blob([uint8Array]).stream().pipeThrough(compressedStream); 8 | const compressedArrayBuffer = await new Response(compressed).arrayBuffer(); 9 | const compressedUint8Array = new Uint8Array(compressedArrayBuffer); 10 | let base64 = btoa(String.fromCharCode(...compressedUint8Array)); 11 | // Make base64 URL-safe: 12 | base64 = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); 13 | return base64; 14 | } 15 | 16 | export async function decodeAndDecompress(encoded) { 17 | // Reverse the URL-safe transformations: 18 | encoded = encoded.replace(/-/g, '+').replace(/_/g, '/'); 19 | const binaryString = atob(encoded); 20 | const len = binaryString.length; 21 | const uint8Array = new Uint8Array(new ArrayBuffer(len)); 22 | for (let i = 0; i < len; i++) { 23 | uint8Array[i] = binaryString.charCodeAt(i); 24 | } 25 | 26 | const decompressedStream = new DecompressionStream('gzip'); 27 | const decompressedBlob = new Blob([uint8Array]).stream().pipeThrough(decompressedStream); 28 | const decompressedArrayBuffer = await new Response(decompressedBlob).arrayBuffer(); 29 | const decoder = new TextDecoder(); 30 | return JSON.parse(decoder.decode(decompressedArrayBuffer)); 31 | } -------------------------------------------------------------------------------- /src/MessageContent.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | {#if message.error} 10 | {message.error} 11 | {:else if message.content} 12 |
    16 | {#if message.contentParts} 17 | {#each message.contentParts as part} 18 | 23 | {/each} 24 | {/if} 25 | 26 |
    27 | {:else if message.generatedImageUrl} 28 | 33 | {/if} 34 | -------------------------------------------------------------------------------- /src/svelte-marked/markdown/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as MarkdownHeading } from './MarkdownHeading.svelte' 2 | export { default as MarkdownBloquote } from './MarkdownBloquote.svelte' 3 | export { default as MarkdownList } from './list/MarkdownList.svelte' 4 | export { default as MarkdownListItem } from './list/MarkdownListItem.svelte' 5 | export { default as MarkdownBr } from './MarkdownBr.svelte' 6 | export { default as MarkdownCode } from './code/MarkdownCode.svelte' 7 | export { default as MarkdownCodeSpan } from './code/MarkdownCodeSpan.svelte' 8 | export { default as MarkdownTable } from './table/MarkdownTable.svelte' 9 | export { default as MarkdownHtml } from './MarkdownHtml.svelte' 10 | export { default as MarkdownParagraph } from './MarkdownParagraph.svelte' 11 | export { default as MarkdownLink } from './MarkdownLink.svelte' 12 | export { default as MarkdownText } from './MarkdownText.svelte' 13 | export { default as MarkdownDfn } from './MarkdownDfn.svelte' 14 | export { default as MarkdownDel } from './MarkdownDel.svelte' 15 | export { default as MarkdownEm } from './MarkdownEm.svelte' 16 | export { default as MarkdownHr } from './MarkdownHr.svelte' 17 | export { default as MarkdownStrong } from './MarkdownStrong.svelte' 18 | export { default as MarkdownImage } from './MarkdownImage.svelte' 19 | export { default as MarkdownSpace } from './MarkdownSpace.svelte' 20 | export { default as MarkdownInlineKatex } from './MarkdownInlineKatex.svelte'; 21 | export { default as MarkdownBlockKatex } from './MarkdownBlockKatex.svelte'; -------------------------------------------------------------------------------- /src/Modal.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | { 19 | if (!trigger) return; 20 | if (event.target.closest(`[data-trigger=${trigger}]`)) { 21 | open = !open; 22 | if (!open) { 23 | dispatch('close'); 24 | } 25 | } 26 | }} 27 | /> 28 | 29 | {#if open} 30 |