Spaces:
Sleeping
Sleeping
add manual controls
Browse files- package.json +1 -0
- pnpm-lock.yaml +16 -0
- src/lib/components/chat/Assistant.svelte +1 -1
- src/lib/components/chat/Message.svelte +3 -1
- src/lib/components/chat/User.svelte +3 -3
- src/lib/components/chat/markdown/Code.svelte +15 -3
- src/lib/components/flow/FitViewOnResize.svelte +43 -6
- src/lib/components/flow/actions/PanelCanvasActions.svelte +67 -0
- src/lib/components/flow/actions/PanelRightActions.svelte +2 -2
- src/lib/components/ui/checkbox/checkbox.svelte +36 -0
- src/lib/components/ui/checkbox/index.ts +6 -0
- src/lib/state/view.svelte.ts +3 -0
- src/routes/+page.svelte +28 -13
package.json
CHANGED
|
@@ -50,6 +50,7 @@
|
|
| 50 |
"elkjs": "^0.11.0",
|
| 51 |
"lint": "^0.8.19",
|
| 52 |
"mode-watcher": "^1.1.0",
|
|
|
|
| 53 |
"svelte-markdown": "^0.4.1",
|
| 54 |
"tailwind-merge": "^3.4.0"
|
| 55 |
}
|
|
|
|
| 50 |
"elkjs": "^0.11.0",
|
| 51 |
"lint": "^0.8.19",
|
| 52 |
"mode-watcher": "^1.1.0",
|
| 53 |
+
"svelte-highlight": "^7.9.0",
|
| 54 |
"svelte-markdown": "^0.4.1",
|
| 55 |
"tailwind-merge": "^3.4.0"
|
| 56 |
}
|
pnpm-lock.yaml
CHANGED
|
@@ -32,6 +32,9 @@ importers:
|
|
| 32 |
mode-watcher:
|
| 33 |
specifier: ^1.1.0
|
| 34 |
version: 1.1.0(svelte@5.50.1)
|
|
|
|
|
|
|
|
|
|
| 35 |
svelte-markdown:
|
| 36 |
specifier: ^0.4.1
|
| 37 |
version: 0.4.1(svelte@5.50.1)
|
|
@@ -1322,6 +1325,10 @@ packages:
|
|
| 1322 |
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
| 1323 |
engines: {node: '>= 0.4'}
|
| 1324 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1325 |
http-signature@1.2.0:
|
| 1326 |
resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==}
|
| 1327 |
engines: {node: '>=0.8', npm: '>=1.3.7'}
|
|
@@ -1989,6 +1996,9 @@ packages:
|
|
| 1989 |
svelte:
|
| 1990 |
optional: true
|
| 1991 |
|
|
|
|
|
|
|
|
|
|
| 1992 |
svelte-markdown@0.4.1:
|
| 1993 |
resolution: {integrity: sha512-pOlLY6EruKJaWI9my/2bKX8PdTeP5CM0s4VMmwmC2prlOkjAf+AOmTM4wW/l19Y6WZ87YmP8+ZCJCCwBChWjYw==}
|
| 1994 |
peerDependencies:
|
|
@@ -3281,6 +3291,8 @@ snapshots:
|
|
| 3281 |
dependencies:
|
| 3282 |
function-bind: 1.1.2
|
| 3283 |
|
|
|
|
|
|
|
| 3284 |
http-signature@1.2.0:
|
| 3285 |
dependencies:
|
| 3286 |
assert-plus: 1.0.0
|
|
@@ -3880,6 +3892,10 @@ snapshots:
|
|
| 3880 |
optionalDependencies:
|
| 3881 |
svelte: 5.50.1
|
| 3882 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3883 |
svelte-markdown@0.4.1(svelte@5.50.1):
|
| 3884 |
dependencies:
|
| 3885 |
'@types/marked': 5.0.2
|
|
|
|
| 32 |
mode-watcher:
|
| 33 |
specifier: ^1.1.0
|
| 34 |
version: 1.1.0(svelte@5.50.1)
|
| 35 |
+
svelte-highlight:
|
| 36 |
+
specifier: ^7.9.0
|
| 37 |
+
version: 7.9.0
|
| 38 |
svelte-markdown:
|
| 39 |
specifier: ^0.4.1
|
| 40 |
version: 0.4.1(svelte@5.50.1)
|
|
|
|
| 1325 |
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
| 1326 |
engines: {node: '>= 0.4'}
|
| 1327 |
|
| 1328 |
+
highlight.js@11.11.1:
|
| 1329 |
+
resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
|
| 1330 |
+
engines: {node: '>=12.0.0'}
|
| 1331 |
+
|
| 1332 |
http-signature@1.2.0:
|
| 1333 |
resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==}
|
| 1334 |
engines: {node: '>=0.8', npm: '>=1.3.7'}
|
|
|
|
| 1996 |
svelte:
|
| 1997 |
optional: true
|
| 1998 |
|
| 1999 |
+
svelte-highlight@7.9.0:
|
| 2000 |
+
resolution: {integrity: sha512-226LBTtvTnM2L2JkQq8mZeKEeMfPLYyta7VxZatFT4UPX5zdHEerKeMTvrfbxm7MVTWc7TPThsNoVdhWC177KQ==}
|
| 2001 |
+
|
| 2002 |
svelte-markdown@0.4.1:
|
| 2003 |
resolution: {integrity: sha512-pOlLY6EruKJaWI9my/2bKX8PdTeP5CM0s4VMmwmC2prlOkjAf+AOmTM4wW/l19Y6WZ87YmP8+ZCJCCwBChWjYw==}
|
| 2004 |
peerDependencies:
|
|
|
|
| 3291 |
dependencies:
|
| 3292 |
function-bind: 1.1.2
|
| 3293 |
|
| 3294 |
+
highlight.js@11.11.1: {}
|
| 3295 |
+
|
| 3296 |
http-signature@1.2.0:
|
| 3297 |
dependencies:
|
| 3298 |
assert-plus: 1.0.0
|
|
|
|
| 3892 |
optionalDependencies:
|
| 3893 |
svelte: 5.50.1
|
| 3894 |
|
| 3895 |
+
svelte-highlight@7.9.0:
|
| 3896 |
+
dependencies:
|
| 3897 |
+
highlight.js: 11.11.1
|
| 3898 |
+
|
| 3899 |
svelte-markdown@0.4.1(svelte@5.50.1):
|
| 3900 |
dependencies:
|
| 3901 |
'@types/marked': 5.0.2
|
src/lib/components/chat/Assistant.svelte
CHANGED
|
@@ -26,7 +26,7 @@
|
|
| 26 |
</script>
|
| 27 |
|
| 28 |
<article class="w-full rounded-3xl border border-border bg-background p-5 shadow-lg/5 lg:w-[600px]">
|
| 29 |
-
<div class="nodrag cursor-auto">
|
| 30 |
<header class="mb-3 flex items-center justify-between">
|
| 31 |
<div class="flex flex-wrap items-center gap-1">
|
| 32 |
<div
|
|
|
|
| 26 |
</script>
|
| 27 |
|
| 28 |
<article class="w-full rounded-3xl border border-border bg-background p-5 shadow-lg/5 lg:w-[600px]">
|
| 29 |
+
<div class="nodrag pointer-events-auto cursor-auto">
|
| 30 |
<header class="mb-3 flex items-center justify-between">
|
| 31 |
<div class="flex flex-wrap items-center gap-1">
|
| 32 |
<div
|
src/lib/components/chat/Message.svelte
CHANGED
|
@@ -28,7 +28,9 @@
|
|
| 28 |
};
|
| 29 |
</script>
|
| 30 |
|
| 31 |
-
<main
|
|
|
|
|
|
|
| 32 |
{#if message?.role === 'user'}
|
| 33 |
<p class="">
|
| 34 |
{message.content}
|
|
|
|
| 28 |
};
|
| 29 |
</script>
|
| 30 |
|
| 31 |
+
<main
|
| 32 |
+
class="pointer-events-auto cursor-auto p-1 text-lg leading-relaxed text-accent-foreground select-text"
|
| 33 |
+
>
|
| 34 |
{#if message?.role === 'user'}
|
| 35 |
<p class="">
|
| 36 |
{message.content}
|
src/lib/components/chat/User.svelte
CHANGED
|
@@ -23,13 +23,13 @@
|
|
| 23 |
import { SUGGESTIONS_PROMPT } from '$lib/consts';
|
| 24 |
import { breakpointsState } from '$lib/state/breakpoints.svelte';
|
| 25 |
|
| 26 |
-
let { id }: NodeProps = $props();
|
| 27 |
|
| 28 |
// svelte-ignore state_referenced_locally
|
| 29 |
const nodeData = useNodesData(id);
|
| 30 |
const { update: updateNodes } = useNodes();
|
| 31 |
const { update: updateEdges } = useEdges();
|
| 32 |
-
const {
|
| 33 |
|
| 34 |
let selectedModels = $derived<ChatModel[]>(
|
| 35 |
(nodeData.current?.data.selectedModels as ChatModel[]) ?? []
|
|
@@ -185,7 +185,7 @@
|
|
| 185 |
</script>
|
| 186 |
|
| 187 |
<article class="w-full rounded-3xl border border-border bg-background p-5 shadow-lg/5 lg:w-[600px]">
|
| 188 |
-
<div class="nodrag cursor-auto">
|
| 189 |
<header class="mb-3 flex items-center justify-between">
|
| 190 |
<div class="flex flex-wrap items-center gap-1">
|
| 191 |
{#each selectedModels as model}
|
|
|
|
| 23 |
import { SUGGESTIONS_PROMPT } from '$lib/consts';
|
| 24 |
import { breakpointsState } from '$lib/state/breakpoints.svelte';
|
| 25 |
|
| 26 |
+
let { id, selected }: NodeProps = $props();
|
| 27 |
|
| 28 |
// svelte-ignore state_referenced_locally
|
| 29 |
const nodeData = useNodesData(id);
|
| 30 |
const { update: updateNodes } = useNodes();
|
| 31 |
const { update: updateEdges } = useEdges();
|
| 32 |
+
const { updateNodeData } = useSvelteFlow();
|
| 33 |
|
| 34 |
let selectedModels = $derived<ChatModel[]>(
|
| 35 |
(nodeData.current?.data.selectedModels as ChatModel[]) ?? []
|
|
|
|
| 185 |
</script>
|
| 186 |
|
| 187 |
<article class="w-full rounded-3xl border border-border bg-background p-5 shadow-lg/5 lg:w-[600px]">
|
| 188 |
+
<div class="nodrag pointer-events-auto cursor-auto">
|
| 189 |
<header class="mb-3 flex items-center justify-between">
|
| 190 |
<div class="flex flex-wrap items-center gap-1">
|
| 191 |
{#each selectedModels as model}
|
src/lib/components/chat/markdown/Code.svelte
CHANGED
|
@@ -1,4 +1,9 @@
|
|
| 1 |
<script lang="ts">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
let { lang, text }: { lang?: string; text: string } = $props();
|
| 3 |
</script>
|
| 4 |
|
|
@@ -8,7 +13,14 @@
|
|
| 8 |
<span class="font-mono text-[11px] text-muted-foreground">{lang}</span>
|
| 9 |
</div>
|
| 10 |
{/if}
|
| 11 |
-
<
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
</div>
|
|
|
|
| 1 |
<script lang="ts">
|
| 2 |
+
import Button from '$lib/components/ui/button/button.svelte';
|
| 3 |
+
import { Copy } from '@lucide/svelte';
|
| 4 |
+
import { HighlightAuto } from 'svelte-highlight';
|
| 5 |
+
import 'svelte-highlight/styles/github.css';
|
| 6 |
+
|
| 7 |
let { lang, text }: { lang?: string; text: string } = $props();
|
| 8 |
</script>
|
| 9 |
|
|
|
|
| 13 |
<span class="font-mono text-[11px] text-muted-foreground">{lang}</span>
|
| 14 |
</div>
|
| 15 |
{/if}
|
| 16 |
+
<div class="group relative">
|
| 17 |
+
<HighlightAuto code={text} class="font-mono text-[13px] leading-relaxed" />
|
| 18 |
+
<Button
|
| 19 |
+
variant="outline"
|
| 20 |
+
size="icon-xs"
|
| 21 |
+
class="absolute top-2 right-2 opacity-0 group-hover:opacity-100"
|
| 22 |
+
>
|
| 23 |
+
<Copy />
|
| 24 |
+
</Button>
|
| 25 |
+
</div>
|
| 26 |
</div>
|
src/lib/components/flow/FitViewOnResize.svelte
CHANGED
|
@@ -4,6 +4,7 @@
|
|
| 4 |
import { useNodes, useEdges } from '@xyflow/svelte';
|
| 5 |
import { useSvelteFlow } from '@xyflow/svelte';
|
| 6 |
import { breakpointsState } from '$lib/state/breakpoints.svelte';
|
|
|
|
| 7 |
|
| 8 |
let { initialNodes }: { initialNodes: Node[] } = $props();
|
| 9 |
|
|
@@ -19,6 +20,45 @@
|
|
| 19 |
|
| 20 |
let lastLayoutKey = $state<string | null>(null);
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
/** Get the actual measured height of a node, or fallback */
|
| 23 |
function getMeasuredHeight(node: Node): number {
|
| 24 |
return node.measured?.height ?? DEFAULT_HEIGHT;
|
|
@@ -67,6 +107,7 @@
|
|
| 67 |
});
|
| 68 |
|
| 69 |
function handleWindowResize() {
|
|
|
|
| 70 |
fitView({
|
| 71 |
maxZoom: 1,
|
| 72 |
minZoom: breakpointsState.isMobile ? 1 : 0.8,
|
|
@@ -81,6 +122,7 @@
|
|
| 81 |
|
| 82 |
onDestroy(() => {
|
| 83 |
window.removeEventListener('resize', handleWindowResize);
|
|
|
|
| 84 |
});
|
| 85 |
|
| 86 |
/**
|
|
@@ -197,11 +239,6 @@
|
|
| 197 |
);
|
| 198 |
}
|
| 199 |
|
| 200 |
-
|
| 201 |
-
maxZoom: 1,
|
| 202 |
-
minZoom: breakpointsState.isMobile ? 1 : 0.8,
|
| 203 |
-
interpolate: 'smooth',
|
| 204 |
-
duration: 250
|
| 205 |
-
});
|
| 206 |
}
|
| 207 |
</script>
|
|
|
|
| 4 |
import { useNodes, useEdges } from '@xyflow/svelte';
|
| 5 |
import { useSvelteFlow } from '@xyflow/svelte';
|
| 6 |
import { breakpointsState } from '$lib/state/breakpoints.svelte';
|
| 7 |
+
import { viewState } from '$lib/state/view.svelte';
|
| 8 |
|
| 9 |
let { initialNodes }: { initialNodes: Node[] } = $props();
|
| 10 |
|
|
|
|
| 20 |
|
| 21 |
let lastLayoutKey = $state<string | null>(null);
|
| 22 |
|
| 23 |
+
const FIT_VIEW_THROTTLE_MS = 500;
|
| 24 |
+
let fitViewTimer: ReturnType<typeof setTimeout> | null = null;
|
| 25 |
+
let lastFitViewTime = 0;
|
| 26 |
+
|
| 27 |
+
function throttledFitView() {
|
| 28 |
+
if (viewState.freeView) return;
|
| 29 |
+
|
| 30 |
+
const now = Date.now();
|
| 31 |
+
const elapsed = now - lastFitViewTime;
|
| 32 |
+
|
| 33 |
+
if (fitViewTimer) {
|
| 34 |
+
clearTimeout(fitViewTimer);
|
| 35 |
+
fitViewTimer = null;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
if (elapsed >= FIT_VIEW_THROTTLE_MS) {
|
| 39 |
+
lastFitViewTime = now;
|
| 40 |
+
fitView({
|
| 41 |
+
maxZoom: 1,
|
| 42 |
+
minZoom: breakpointsState.isMobile ? 1 : 0.7,
|
| 43 |
+
interpolate: 'smooth',
|
| 44 |
+
duration: 250
|
| 45 |
+
});
|
| 46 |
+
} else {
|
| 47 |
+
fitViewTimer = setTimeout(() => {
|
| 48 |
+
lastFitViewTime = Date.now();
|
| 49 |
+
fitViewTimer = null;
|
| 50 |
+
if (!viewState.freeView) {
|
| 51 |
+
fitView({
|
| 52 |
+
maxZoom: 1,
|
| 53 |
+
minZoom: breakpointsState.isMobile ? 1 : 0.7,
|
| 54 |
+
interpolate: 'smooth',
|
| 55 |
+
duration: 250
|
| 56 |
+
});
|
| 57 |
+
}
|
| 58 |
+
}, FIT_VIEW_THROTTLE_MS - elapsed);
|
| 59 |
+
}
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
/** Get the actual measured height of a node, or fallback */
|
| 63 |
function getMeasuredHeight(node: Node): number {
|
| 64 |
return node.measured?.height ?? DEFAULT_HEIGHT;
|
|
|
|
| 107 |
});
|
| 108 |
|
| 109 |
function handleWindowResize() {
|
| 110 |
+
if (viewState.freeView) return;
|
| 111 |
fitView({
|
| 112 |
maxZoom: 1,
|
| 113 |
minZoom: breakpointsState.isMobile ? 1 : 0.8,
|
|
|
|
| 122 |
|
| 123 |
onDestroy(() => {
|
| 124 |
window.removeEventListener('resize', handleWindowResize);
|
| 125 |
+
if (fitViewTimer) clearTimeout(fitViewTimer);
|
| 126 |
});
|
| 127 |
|
| 128 |
/**
|
|
|
|
| 239 |
);
|
| 240 |
}
|
| 241 |
|
| 242 |
+
throttledFitView();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
}
|
| 244 |
</script>
|
src/lib/components/flow/actions/PanelCanvasActions.svelte
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
import { Maximize, Lock, LockOpen, Minus, Plus } from '@lucide/svelte';
|
| 3 |
+
import { Panel } from '@xyflow/svelte';
|
| 4 |
+
|
| 5 |
+
import { Button } from '$lib/components/ui/button';
|
| 6 |
+
import { breakpointsState } from '$lib/state/breakpoints.svelte';
|
| 7 |
+
import { viewState } from '$lib/state/view.svelte';
|
| 8 |
+
import { useSvelteFlow } from '@xyflow/svelte';
|
| 9 |
+
import HFLogo from '$lib/assets/hf-logo.svg';
|
| 10 |
+
import { Separator } from '$lib/components/ui/separator';
|
| 11 |
+
|
| 12 |
+
const { fitView, zoomIn, zoomOut, getZoom, getViewport } = useSvelteFlow();
|
| 13 |
+
</script>
|
| 14 |
+
|
| 15 |
+
<Panel position="bottom-left" class="space-y-2 p-1 lg:p-2">
|
| 16 |
+
<div
|
| 17 |
+
class="inline-flex w-fit flex-col gap-0.5 rounded-md border border-border bg-background p-0.5"
|
| 18 |
+
>
|
| 19 |
+
<Button
|
| 20 |
+
variant="ghost"
|
| 21 |
+
size="icon-xs"
|
| 22 |
+
onclick={() => zoomIn({ duration: 200 })}
|
| 23 |
+
disabled={getZoom() >= 1}
|
| 24 |
+
>
|
| 25 |
+
<Plus />
|
| 26 |
+
</Button>
|
| 27 |
+
<Button
|
| 28 |
+
variant="ghost"
|
| 29 |
+
size="icon-xs"
|
| 30 |
+
onclick={() => zoomOut({ duration: 200 })}
|
| 31 |
+
disabled={getZoom() <= 0.7}
|
| 32 |
+
>
|
| 33 |
+
<Minus />
|
| 34 |
+
</Button>
|
| 35 |
+
<Button
|
| 36 |
+
variant="ghost"
|
| 37 |
+
size="icon-xs"
|
| 38 |
+
onclick={() =>
|
| 39 |
+
fitView({
|
| 40 |
+
maxZoom: 1,
|
| 41 |
+
minZoom: breakpointsState.isMobile ? 1 : 0.7,
|
| 42 |
+
interpolate: 'smooth',
|
| 43 |
+
duration: 250
|
| 44 |
+
})}
|
| 45 |
+
>
|
| 46 |
+
<Maximize />
|
| 47 |
+
</Button>
|
| 48 |
+
<Separator />
|
| 49 |
+
<Button
|
| 50 |
+
variant="ghost"
|
| 51 |
+
size="icon-xs"
|
| 52 |
+
onclick={() => (viewState.freeView = !viewState.freeView)}
|
| 53 |
+
>
|
| 54 |
+
{#if viewState.freeView}
|
| 55 |
+
<Lock class="fill-transparent!" />
|
| 56 |
+
{:else}
|
| 57 |
+
<LockOpen class="fill-transparent!" />
|
| 58 |
+
{/if}
|
| 59 |
+
</Button>
|
| 60 |
+
</div>
|
| 61 |
+
<div
|
| 62 |
+
class="flex items-center justify-center gap-1.5 rounded-lg border border-border bg-background py-1.5 pr-3.5 pl-2.5 shadow-xs"
|
| 63 |
+
>
|
| 64 |
+
<img src={HFLogo} alt="HF Logo" class="size-5 lg:size-7" />
|
| 65 |
+
<p class="text-xs text-accent-foreground">Hugging Face Playground</p>
|
| 66 |
+
</div>
|
| 67 |
+
</Panel>
|
src/lib/components/flow/actions/PanelRightActions.svelte
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
<script lang="ts">
|
| 2 |
-
import { Cog, Contrast, Monitor, Moon, RefreshCcw, Sun } from '@lucide/svelte';
|
| 3 |
import { Panel } from '@xyflow/svelte';
|
| 4 |
import { setMode } from 'mode-watcher';
|
| 5 |
|
|
@@ -18,7 +18,7 @@
|
|
| 18 |
}
|
| 19 |
</script>
|
| 20 |
|
| 21 |
-
<Panel position="top-right" class="flex items-center justify-end gap-2 p-1 lg:p-
|
| 22 |
<Button variant="outline" class="" onclick={handleReset}>
|
| 23 |
<RefreshCcw />
|
| 24 |
Start new chat
|
|
|
|
| 1 |
<script lang="ts">
|
| 2 |
+
import { Cog, Contrast, Monitor, Moon, Move, RefreshCcw, Sun } from '@lucide/svelte';
|
| 3 |
import { Panel } from '@xyflow/svelte';
|
| 4 |
import { setMode } from 'mode-watcher';
|
| 5 |
|
|
|
|
| 18 |
}
|
| 19 |
</script>
|
| 20 |
|
| 21 |
+
<Panel position="top-right" class="flex items-center justify-end gap-2 p-1 lg:p-2">
|
| 22 |
<Button variant="outline" class="" onclick={handleReset}>
|
| 23 |
<RefreshCcw />
|
| 24 |
Start new chat
|
src/lib/components/ui/checkbox/checkbox.svelte
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
import { Checkbox as CheckboxPrimitive } from 'bits-ui';
|
| 3 |
+
import CheckIcon from '@lucide/svelte/icons/check';
|
| 4 |
+
import MinusIcon from '@lucide/svelte/icons/minus';
|
| 5 |
+
import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
|
| 6 |
+
|
| 7 |
+
let {
|
| 8 |
+
ref = $bindable(null),
|
| 9 |
+
checked = $bindable(false),
|
| 10 |
+
indeterminate = $bindable(false),
|
| 11 |
+
class: className,
|
| 12 |
+
...restProps
|
| 13 |
+
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
|
| 14 |
+
</script>
|
| 15 |
+
|
| 16 |
+
<CheckboxPrimitive.Root
|
| 17 |
+
bind:ref
|
| 18 |
+
data-slot="checkbox"
|
| 19 |
+
class={cn(
|
| 20 |
+
'peer flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-[4px] border border-input shadow-xs transition-shadow outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:data-[state=checked]:bg-primary',
|
| 21 |
+
className
|
| 22 |
+
)}
|
| 23 |
+
bind:checked
|
| 24 |
+
bind:indeterminate
|
| 25 |
+
{...restProps}
|
| 26 |
+
>
|
| 27 |
+
{#snippet children({ checked, indeterminate })}
|
| 28 |
+
<div data-slot="checkbox-indicator" class="text-current transition-none">
|
| 29 |
+
{#if checked}
|
| 30 |
+
<CheckIcon class="size-3.5" />
|
| 31 |
+
{:else if indeterminate}
|
| 32 |
+
<MinusIcon class="size-3.5" />
|
| 33 |
+
{/if}
|
| 34 |
+
</div>
|
| 35 |
+
{/snippet}
|
| 36 |
+
</CheckboxPrimitive.Root>
|
src/lib/components/ui/checkbox/index.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import Root from "./checkbox.svelte";
|
| 2 |
+
export {
|
| 3 |
+
Root,
|
| 4 |
+
//
|
| 5 |
+
Root as Checkbox,
|
| 6 |
+
};
|
src/lib/state/view.svelte.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export const viewState = $state({
|
| 2 |
+
freeView: false
|
| 3 |
+
});
|
src/routes/+page.svelte
CHANGED
|
@@ -3,10 +3,11 @@
|
|
| 3 |
SvelteFlow,
|
| 4 |
Background,
|
| 5 |
MiniMap,
|
| 6 |
-
Panel,
|
| 7 |
BackgroundVariant,
|
|
|
|
| 8 |
type Node,
|
| 9 |
-
type Edge
|
|
|
|
| 10 |
} from '@xyflow/svelte';
|
| 11 |
import '@xyflow/svelte/dist/style.css';
|
| 12 |
import { mode } from 'mode-watcher';
|
|
@@ -15,12 +16,12 @@
|
|
| 15 |
import { tokenModalState } from '$lib/state/token-modal.svelte';
|
| 16 |
import User from '$lib/components/chat/User.svelte';
|
| 17 |
import Assistant from '$lib/components/chat/Assistant.svelte';
|
| 18 |
-
import HFLogo from '$lib/assets/hf-logo.svg';
|
| 19 |
import type { ChatModel } from '$lib/helpers/types';
|
| 20 |
import FitViewOnResize from '$lib/components/flow/FitViewOnResize.svelte';
|
| 21 |
import PanelRightActions from '$lib/components/flow/actions/PanelRightActions.svelte';
|
| 22 |
import { MAX_DEFAULT_MODELS } from '$lib';
|
| 23 |
import { breakpointsState } from '$lib/state/breakpoints.svelte';
|
|
|
|
| 24 |
|
| 25 |
const nodeTypes = {
|
| 26 |
user: User,
|
|
@@ -60,17 +61,21 @@
|
|
| 60 |
bind:nodes
|
| 61 |
bind:edges
|
| 62 |
{nodeTypes}
|
| 63 |
-
minZoom={0.
|
| 64 |
-
maxZoom={
|
| 65 |
fitView
|
|
|
|
|
|
|
|
|
|
| 66 |
colorMode={mode.current}
|
| 67 |
proOptions={{ hideAttribution: true }}
|
| 68 |
fitViewOptions={{
|
| 69 |
maxZoom: 1,
|
| 70 |
-
minZoom: breakpointsState.isMobile ? 1 : 0.
|
| 71 |
interpolate: 'smooth',
|
| 72 |
duration: 500
|
| 73 |
}}
|
|
|
|
| 74 |
defaultEdgeOptions={{ type: 'smoothstep' }}
|
| 75 |
class="bg-background!"
|
| 76 |
>
|
|
@@ -86,13 +91,23 @@
|
|
| 86 |
patternColor={mode.current === 'dark' ? 'rgba(255, 255, 255, 0.04)' : 'rgba(0, 0, 0, 0.04)'}
|
| 87 |
class="bg-background!"
|
| 88 |
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
<PanelRightActions canReset={nodes.length > 1} />
|
| 90 |
-
<
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
|
|
|
| 97 |
</SvelteFlow>
|
| 98 |
</div>
|
|
|
|
| 3 |
SvelteFlow,
|
| 4 |
Background,
|
| 5 |
MiniMap,
|
|
|
|
| 6 |
BackgroundVariant,
|
| 7 |
+
PanOnScrollMode,
|
| 8 |
type Node,
|
| 9 |
+
type Edge,
|
| 10 |
+
useSvelteFlow
|
| 11 |
} from '@xyflow/svelte';
|
| 12 |
import '@xyflow/svelte/dist/style.css';
|
| 13 |
import { mode } from 'mode-watcher';
|
|
|
|
| 16 |
import { tokenModalState } from '$lib/state/token-modal.svelte';
|
| 17 |
import User from '$lib/components/chat/User.svelte';
|
| 18 |
import Assistant from '$lib/components/chat/Assistant.svelte';
|
|
|
|
| 19 |
import type { ChatModel } from '$lib/helpers/types';
|
| 20 |
import FitViewOnResize from '$lib/components/flow/FitViewOnResize.svelte';
|
| 21 |
import PanelRightActions from '$lib/components/flow/actions/PanelRightActions.svelte';
|
| 22 |
import { MAX_DEFAULT_MODELS } from '$lib';
|
| 23 |
import { breakpointsState } from '$lib/state/breakpoints.svelte';
|
| 24 |
+
import PanelCanvasActions from '$lib/components/flow/actions/PanelCanvasActions.svelte';
|
| 25 |
|
| 26 |
const nodeTypes = {
|
| 27 |
user: User,
|
|
|
|
| 61 |
bind:nodes
|
| 62 |
bind:edges
|
| 63 |
{nodeTypes}
|
| 64 |
+
minZoom={breakpointsState.isMobile ? 1 : 0.7}
|
| 65 |
+
maxZoom={breakpointsState.isMobile ? 1 : 1}
|
| 66 |
fitView
|
| 67 |
+
zoomOnScroll={false}
|
| 68 |
+
panOnScroll={true}
|
| 69 |
+
panOnScrollMode={PanOnScrollMode.Free}
|
| 70 |
colorMode={mode.current}
|
| 71 |
proOptions={{ hideAttribution: true }}
|
| 72 |
fitViewOptions={{
|
| 73 |
maxZoom: 1,
|
| 74 |
+
minZoom: breakpointsState.isMobile ? 1 : 0.7,
|
| 75 |
interpolate: 'smooth',
|
| 76 |
duration: 500
|
| 77 |
}}
|
| 78 |
+
onbeforedelete={() => Promise.resolve(false)}
|
| 79 |
defaultEdgeOptions={{ type: 'smoothstep' }}
|
| 80 |
class="bg-background!"
|
| 81 |
>
|
|
|
|
| 91 |
patternColor={mode.current === 'dark' ? 'rgba(255, 255, 255, 0.04)' : 'rgba(0, 0, 0, 0.04)'}
|
| 92 |
class="bg-background!"
|
| 93 |
/>
|
| 94 |
+
<!-- <Controls class="bottom-16! left-2.5! max-lg:hidden" showLock={false}>
|
| 95 |
+
<ControlButton onclick={() => (viewState.freeView = !viewState.freeView)}>
|
| 96 |
+
{#if viewState.freeView}
|
| 97 |
+
<Lock class="fill-transparent!" />
|
| 98 |
+
{:else}
|
| 99 |
+
<LockOpen class="fill-transparent!" />
|
| 100 |
+
{/if}
|
| 101 |
+
</ControlButton>
|
| 102 |
+
</Controls> -->
|
| 103 |
<PanelRightActions canReset={nodes.length > 1} />
|
| 104 |
+
<PanelCanvasActions />
|
| 105 |
+
<!-- <Panel position="bottom-right" class="p-1 max-lg:hidden lg:p-2 lg:pb-40">
|
| 106 |
+
<Button variant="outline" onclick={() => (viewState.freeView = !viewState.freeView)}>
|
| 107 |
+
<Checkbox checked={viewState.freeView} />
|
| 108 |
+
<Move />
|
| 109 |
+
Free view
|
| 110 |
+
</Button>
|
| 111 |
+
</Panel> -->
|
| 112 |
</SvelteFlow>
|
| 113 |
</div>
|