First build
Browse files- .dockerignore +8 -0
- .github/workflows/deploy-release.yml +22 -0
- Dockerfile +34 -0
- README.md +50 -17
- package-lock.json +589 -30
- package.json +18 -0
- src/app.css +131 -0
- src/app.html +40 -0
- src/lib/ChatTemplateViewer/ChatTemplateViewer.svelte +137 -0
- src/lib/ChatTemplateViewer/types.ts +9 -0
- src/lib/CodeMirror/CodeMirror.svelte +166 -0
- src/lib/CodeMirror/basicSetup.ts +57 -0
- src/lib/CodeMirror/indentationMarkers.ts +541 -0
- src/lib/CodeMirrorSearch/CodeMirrorSearch.svelte +219 -0
- src/lib/CopyButton/CopyButton.svelte +85 -0
- src/lib/Icons/IconArrowLeft.svelte +18 -0
- src/lib/Icons/IconCaretV2.svelte +14 -0
- src/lib/Icons/IconCodeGeneration.svelte +34 -0
- src/lib/Icons/IconCopy.svelte +26 -0
- src/lib/Icons/IconCross.svelte +21 -0
- src/lib/Icons/IconLineWrap.svelte +20 -0
- src/lib/Icons/IconReplace.svelte +17 -0
- src/lib/Icons/IconReplaceAll.svelte +17 -0
- src/lib/Icons/IconRestart.svelte +22 -0
- src/lib/Icons/IconSpin.svelte +32 -0
- src/lib/JsonEditor/JsonEditor.svelte +256 -0
- src/lib/LineWrapButton/LineWrapButton.svelte +43 -0
- src/lib/OutputViewer/OutputViewer.svelte +50 -0
- src/lib/example-inputs/helloWorld.ts +61 -0
- src/lib/example-inputs/reasoning.ts +66 -0
- src/lib/example-inputs/toolUsage.ts +396 -0
- src/lib/utils/tooltip.ts +463 -0
- src/lib/utils/transformInput.ts +7 -0
- src/routes/+page.svelte +289 -2
- svelte.config.js +1 -1
.dockerignore
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Dockerfile
|
2 |
+
.vscode/
|
3 |
+
.idea
|
4 |
+
.gitignore
|
5 |
+
LICENSE
|
6 |
+
README.md
|
7 |
+
node_modules/
|
8 |
+
.svelte-kit/
|
.github/workflows/deploy-release.yml
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Deploy to Hf Hub
|
2 |
+
|
3 |
+
on:
|
4 |
+
push:
|
5 |
+
branches:
|
6 |
+
- main
|
7 |
+
|
8 |
+
# to run this workflow manually from the Actions tab
|
9 |
+
workflow_dispatch:
|
10 |
+
|
11 |
+
jobs:
|
12 |
+
sync-to-hub:
|
13 |
+
runs-on: ubuntu-latest
|
14 |
+
steps:
|
15 |
+
- uses: actions/checkout@v3
|
16 |
+
with:
|
17 |
+
fetch-depth: 0
|
18 |
+
lfs: true
|
19 |
+
- name: Push to hub
|
20 |
+
env:
|
21 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
22 |
+
run: git push https://mishig:$HF_TOKEN@huggingface.co/spaces/huggingfacejs/chat-template-playground main -f
|
Dockerfile
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# syntax=docker/dockerfile:1
|
2 |
+
# For more info: https://huggingface.co/docs/hub/spaces-sdks-docker
|
3 |
+
|
4 |
+
# ---- Base image ----
|
5 |
+
FROM node:20 as base
|
6 |
+
WORKDIR /app
|
7 |
+
|
8 |
+
# ---- Dependencies (production only) ----
|
9 |
+
FROM base as deps
|
10 |
+
COPY --link --chown=1000 package-lock.json package.json ./
|
11 |
+
RUN --mount=type=cache,target=/app/.npm \
|
12 |
+
npm set cache /app/.npm && \
|
13 |
+
npm ci --omit=dev
|
14 |
+
|
15 |
+
# ---- Builder (all dependencies) ----
|
16 |
+
FROM deps as builder
|
17 |
+
RUN --mount=type=cache,target=/app/.npm \
|
18 |
+
npm set cache /app/.npm && \
|
19 |
+
npm ci
|
20 |
+
COPY --link --chown=1000 . .
|
21 |
+
RUN npm run build
|
22 |
+
|
23 |
+
# ---- Runner ----
|
24 |
+
FROM node:20-slim as runner
|
25 |
+
RUN npm install -g pm2
|
26 |
+
WORKDIR /app
|
27 |
+
COPY --from=deps /app/node_modules /app/node_modules
|
28 |
+
COPY --link --chown=1000 package.json /app/package.json
|
29 |
+
COPY --from=builder /app/build /app/build
|
30 |
+
|
31 |
+
# Expose the port your app runs on
|
32 |
+
EXPOSE 3000
|
33 |
+
|
34 |
+
CMD pm2 start /app/build/index.js -i $CPU_CORES --no-daemon
|
README.md
CHANGED
@@ -1,38 +1,71 @@
|
|
1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
-
|
4 |
|
5 |
-
|
6 |
|
7 |
-
|
8 |
|
9 |
-
|
10 |
-
# create a new project in the current directory
|
11 |
-
npx sv create
|
12 |
|
13 |
-
|
14 |
-
|
|
|
|
|
|
|
|
|
15 |
```
|
16 |
|
17 |
-
## Developing
|
18 |
|
19 |
-
|
20 |
|
21 |
```bash
|
22 |
npm run dev
|
23 |
-
|
24 |
# or start the server and open the app in a new browser tab
|
25 |
-
|
26 |
```
|
27 |
|
28 |
-
## Building
|
29 |
|
30 |
-
To create a production
|
31 |
|
32 |
```bash
|
33 |
npm run build
|
34 |
```
|
35 |
|
36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
|
38 |
-
|
|
|
1 |
+
---
|
2 |
+
title: Chat Template Playground
|
3 |
+
emoji: 💻
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: green
|
6 |
+
sdk: docker
|
7 |
+
app_port: 3000
|
8 |
+
pinned: false
|
9 |
+
license: apache-2.0
|
10 |
+
---
|
11 |
|
12 |
+
# Chat Template Playground
|
13 |
|
14 |
+
A modern SvelteKit playground for experimenting with chat UIs, templates, and interactive components.
|
15 |
|
16 |
+
## 📦 Installing Dependencies
|
17 |
|
18 |
+
Install the project dependencies before starting development:
|
|
|
|
|
19 |
|
20 |
+
```bash
|
21 |
+
npm install
|
22 |
+
# or
|
23 |
+
yarn install
|
24 |
+
# or
|
25 |
+
pnpm install
|
26 |
```
|
27 |
|
28 |
+
## 💻 Developing Locally
|
29 |
|
30 |
+
Start a development server with hot reload:
|
31 |
|
32 |
```bash
|
33 |
npm run dev
|
|
|
34 |
# or start the server and open the app in a new browser tab
|
35 |
+
yarn dev -- --open
|
36 |
```
|
37 |
|
38 |
+
## 🏗️ Building for Production
|
39 |
|
40 |
+
To create a production build:
|
41 |
|
42 |
```bash
|
43 |
npm run build
|
44 |
```
|
45 |
|
46 |
+
Preview the production build:
|
47 |
+
|
48 |
+
```bash
|
49 |
+
npm run preview
|
50 |
+
```
|
51 |
+
|
52 |
+
> **Note:** To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
53 |
+
|
54 |
+
## 🧰 Tech Stack
|
55 |
+
|
56 |
+
- [SvelteKit](https://kit.svelte.dev/)
|
57 |
+
- [Vite](https://vitejs.dev/)
|
58 |
+
- [sv](https://github.com/sveltejs/cli)
|
59 |
+
- [Tailwind CSS](https://tailwindcss.com/) (if enabled)
|
60 |
+
|
61 |
+
## 🤝 Contributing
|
62 |
+
|
63 |
+
Contributions, issues, and feature requests are welcome! Feel free to open an issue or submit a pull request.
|
64 |
+
|
65 |
+
## 📄 License
|
66 |
+
|
67 |
+
This project is licensed under the MIT License.
|
68 |
+
|
69 |
+
## 📬 Contact
|
70 |
|
71 |
+
For questions or feedback, please contact the project maintainer.
|
package-lock.json
CHANGED
@@ -7,13 +7,31 @@
|
|
7 |
"": {
|
8 |
"name": "chat-template-playground",
|
9 |
"version": "0.0.1",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
"devDependencies": {
|
11 |
"@eslint/compat": "^1.2.5",
|
12 |
"@eslint/js": "^9.18.0",
|
13 |
"@sveltejs/adapter-auto": "^6.0.0",
|
|
|
14 |
"@sveltejs/kit": "^2.16.0",
|
15 |
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
16 |
"@tailwindcss/vite": "^4.0.0",
|
|
|
17 |
"eslint": "^9.18.0",
|
18 |
"eslint-config-prettier": "^10.0.1",
|
19 |
"eslint-plugin-svelte": "^3.0.0",
|
@@ -43,6 +61,255 @@
|
|
43 |
"node": ">=6.0.0"
|
44 |
}
|
45 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
"node_modules/@esbuild/aix-ppc64": {
|
47 |
"version": "0.25.4",
|
48 |
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz",
|
@@ -637,6 +904,15 @@
|
|
637 |
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
638 |
}
|
639 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
640 |
"node_modules/@humanfs/core": {
|
641 |
"version": "0.19.1",
|
642 |
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
@@ -756,6 +1032,58 @@
|
|
756 |
"@jridgewell/sourcemap-codec": "^1.4.14"
|
757 |
}
|
758 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
759 |
"node_modules/@modelcontextprotocol/sdk": {
|
760 |
"version": "1.11.0",
|
761 |
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.0.tgz",
|
@@ -823,6 +1151,112 @@
|
|
823 |
"dev": true,
|
824 |
"license": "MIT"
|
825 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
826 |
"node_modules/@rollup/rollup-android-arm-eabi": {
|
827 |
"version": "4.40.2",
|
828 |
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz",
|
@@ -1126,6 +1560,22 @@
|
|
1126 |
"@sveltejs/kit": "^2.0.0"
|
1127 |
}
|
1128 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1129 |
"node_modules/@sveltejs/kit": {
|
1130 |
"version": "2.20.8",
|
1131 |
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.8.tgz",
|
@@ -1487,6 +1937,20 @@
|
|
1487 |
"dev": true,
|
1488 |
"license": "MIT"
|
1489 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1490 |
"node_modules/@typescript-eslint/eslint-plugin": {
|
1491 |
"version": "8.32.0",
|
1492 |
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.0.tgz",
|
@@ -1956,6 +2420,13 @@
|
|
1956 |
"dev": true,
|
1957 |
"license": "MIT"
|
1958 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1959 |
"node_modules/concat-map": {
|
1960 |
"version": "0.0.1",
|
1961 |
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
@@ -2020,6 +2491,12 @@
|
|
2020 |
"node": ">= 0.10"
|
2021 |
}
|
2022 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
2023 |
"node_modules/cross-spawn": {
|
2024 |
"version": "7.0.6",
|
2025 |
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
@@ -2460,6 +2937,13 @@
|
|
2460 |
"node": ">=4.0"
|
2461 |
}
|
2462 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2463 |
"node_modules/esutils": {
|
2464 |
"version": "2.0.3",
|
2465 |
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
@@ -2998,6 +3482,22 @@
|
|
2998 |
"node": ">= 0.10"
|
2999 |
}
|
3000 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3001 |
"node_modules/is-extglob": {
|
3002 |
"version": "2.1.1",
|
3003 |
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
@@ -3021,6 +3521,13 @@
|
|
3021 |
"node": ">=0.10.0"
|
3022 |
}
|
3023 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3024 |
"node_modules/is-number": {
|
3025 |
"version": "7.0.0",
|
3026 |
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
@@ -3099,6 +3606,18 @@
|
|
3099 |
"dev": true,
|
3100 |
"license": "MIT"
|
3101 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3102 |
"node_modules/keyv": {
|
3103 |
"version": "4.5.4",
|
3104 |
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
@@ -3486,6 +4005,19 @@
|
|
3486 |
"node": ">=8.6"
|
3487 |
}
|
3488 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3489 |
"node_modules/mime-db": {
|
3490 |
"version": "1.54.0",
|
3491 |
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
@@ -3724,6 +4256,13 @@
|
|
3724 |
"node": ">=8"
|
3725 |
}
|
3726 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3727 |
"node_modules/path-to-regexp": {
|
3728 |
"version": "8.2.0",
|
3729 |
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
|
@@ -3742,13 +4281,13 @@
|
|
3742 |
"license": "ISC"
|
3743 |
},
|
3744 |
"node_modules/picomatch": {
|
3745 |
-
"version": "
|
3746 |
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-
|
3747 |
-
"integrity": "sha512-
|
3748 |
"dev": true,
|
3749 |
"license": "MIT",
|
3750 |
"engines": {
|
3751 |
-
"node": ">=
|
3752 |
},
|
3753 |
"funding": {
|
3754 |
"url": "https://github.com/sponsors/jonschlinkert"
|
@@ -4118,6 +4657,27 @@
|
|
4118 |
"url": "https://paulmillr.com/funding/"
|
4119 |
}
|
4120 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4121 |
"node_modules/resolve-from": {
|
4122 |
"version": "4.0.0",
|
4123 |
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
@@ -4474,6 +5034,12 @@
|
|
4474 |
"url": "https://github.com/sponsors/sindresorhus"
|
4475 |
}
|
4476 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
4477 |
"node_modules/supports-color": {
|
4478 |
"version": "7.2.0",
|
4479 |
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
@@ -4487,6 +5053,19 @@
|
|
4487 |
"node": ">=8"
|
4488 |
}
|
4489 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4490 |
"node_modules/svelte": {
|
4491 |
"version": "5.28.2",
|
4492 |
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.28.2.tgz",
|
@@ -4600,19 +5179,6 @@
|
|
4600 |
"url": "https://github.com/sponsors/SuperchupuDev"
|
4601 |
}
|
4602 |
},
|
4603 |
-
"node_modules/tinyglobby/node_modules/picomatch": {
|
4604 |
-
"version": "4.0.2",
|
4605 |
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
4606 |
-
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
4607 |
-
"dev": true,
|
4608 |
-
"license": "MIT",
|
4609 |
-
"engines": {
|
4610 |
-
"node": ">=12"
|
4611 |
-
},
|
4612 |
-
"funding": {
|
4613 |
-
"url": "https://github.com/sponsors/jonschlinkert"
|
4614 |
-
}
|
4615 |
-
},
|
4616 |
"node_modules/to-regex-range": {
|
4617 |
"version": "5.0.1",
|
4618 |
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
@@ -4836,19 +5402,6 @@
|
|
4836 |
}
|
4837 |
}
|
4838 |
},
|
4839 |
-
"node_modules/vite/node_modules/picomatch": {
|
4840 |
-
"version": "4.0.2",
|
4841 |
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
4842 |
-
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
4843 |
-
"dev": true,
|
4844 |
-
"license": "MIT",
|
4845 |
-
"engines": {
|
4846 |
-
"node": ">=12"
|
4847 |
-
},
|
4848 |
-
"funding": {
|
4849 |
-
"url": "https://github.com/sponsors/jonschlinkert"
|
4850 |
-
}
|
4851 |
-
},
|
4852 |
"node_modules/vitefu": {
|
4853 |
"version": "1.0.6",
|
4854 |
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.6.tgz",
|
@@ -4868,6 +5421,12 @@
|
|
4868 |
}
|
4869 |
}
|
4870 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
4871 |
"node_modules/which": {
|
4872 |
"version": "2.0.2",
|
4873 |
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
|
|
7 |
"": {
|
8 |
"name": "chat-template-playground",
|
9 |
"version": "0.0.1",
|
10 |
+
"dependencies": {
|
11 |
+
"@codemirror/autocomplete": "^6.16.0",
|
12 |
+
"@codemirror/basic-setup": "^0.20.0",
|
13 |
+
"@codemirror/commands": "^6.2.1",
|
14 |
+
"@codemirror/lang-javascript": "^6.2.3",
|
15 |
+
"@codemirror/lang-json": "^6.0.1",
|
16 |
+
"@codemirror/language": "^6.10.1",
|
17 |
+
"@codemirror/legacy-modes": "^6.3.0",
|
18 |
+
"@codemirror/lint": "^6.8.5",
|
19 |
+
"@codemirror/search": "^6.5.11",
|
20 |
+
"@codemirror/state": "^6.4.1",
|
21 |
+
"@codemirror/theme-one-dark": "^6.1.2",
|
22 |
+
"@codemirror/view": "^6.26.3",
|
23 |
+
"@huggingface/jinja": "^0.5.0",
|
24 |
+
"json5": "^2.2.3"
|
25 |
+
},
|
26 |
"devDependencies": {
|
27 |
"@eslint/compat": "^1.2.5",
|
28 |
"@eslint/js": "^9.18.0",
|
29 |
"@sveltejs/adapter-auto": "^6.0.0",
|
30 |
+
"@sveltejs/adapter-node": "^5.2.12",
|
31 |
"@sveltejs/kit": "^2.16.0",
|
32 |
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
33 |
"@tailwindcss/vite": "^4.0.0",
|
34 |
+
"@types/json5": "^0.0.30",
|
35 |
"eslint": "^9.18.0",
|
36 |
"eslint-config-prettier": "^10.0.1",
|
37 |
"eslint-plugin-svelte": "^3.0.0",
|
|
|
61 |
"node": ">=6.0.0"
|
62 |
}
|
63 |
},
|
64 |
+
"node_modules/@codemirror/autocomplete": {
|
65 |
+
"version": "6.16.0",
|
66 |
+
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.0.tgz",
|
67 |
+
"integrity": "sha512-P/LeCTtZHRTCU4xQsa89vSKWecYv1ZqwzOd5topheGRf+qtacFgBeIMQi3eL8Kt/BUNvxUWkx+5qP2jlGoARrg==",
|
68 |
+
"license": "MIT",
|
69 |
+
"dependencies": {
|
70 |
+
"@codemirror/language": "^6.0.0",
|
71 |
+
"@codemirror/state": "^6.0.0",
|
72 |
+
"@codemirror/view": "^6.17.0",
|
73 |
+
"@lezer/common": "^1.0.0"
|
74 |
+
},
|
75 |
+
"peerDependencies": {
|
76 |
+
"@codemirror/language": "^6.0.0",
|
77 |
+
"@codemirror/state": "^6.0.0",
|
78 |
+
"@codemirror/view": "^6.0.0",
|
79 |
+
"@lezer/common": "^1.0.0"
|
80 |
+
}
|
81 |
+
},
|
82 |
+
"node_modules/@codemirror/basic-setup": {
|
83 |
+
"version": "0.20.0",
|
84 |
+
"resolved": "https://registry.npmjs.org/@codemirror/basic-setup/-/basic-setup-0.20.0.tgz",
|
85 |
+
"integrity": "sha512-W/ERKMLErWkrVLyP5I8Yh8PXl4r+WFNkdYVSzkXYPQv2RMPSkWpr2BgggiSJ8AHF/q3GuApncDD8I4BZz65fyg==",
|
86 |
+
"deprecated": "In version 6.0, this package has been renamed to just 'codemirror'",
|
87 |
+
"license": "MIT",
|
88 |
+
"dependencies": {
|
89 |
+
"@codemirror/autocomplete": "^0.20.0",
|
90 |
+
"@codemirror/commands": "^0.20.0",
|
91 |
+
"@codemirror/language": "^0.20.0",
|
92 |
+
"@codemirror/lint": "^0.20.0",
|
93 |
+
"@codemirror/search": "^0.20.0",
|
94 |
+
"@codemirror/state": "^0.20.0",
|
95 |
+
"@codemirror/view": "^0.20.0"
|
96 |
+
}
|
97 |
+
},
|
98 |
+
"node_modules/@codemirror/basic-setup/node_modules/@codemirror/autocomplete": {
|
99 |
+
"version": "0.20.3",
|
100 |
+
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-0.20.3.tgz",
|
101 |
+
"integrity": "sha512-lYB+NPGP+LEzAudkWhLfMxhTrxtLILGl938w+RcFrGdrIc54A+UgmCoz+McE3IYRFp4xyQcL4uFJwo+93YdgHw==",
|
102 |
+
"license": "MIT",
|
103 |
+
"dependencies": {
|
104 |
+
"@codemirror/language": "^0.20.0",
|
105 |
+
"@codemirror/state": "^0.20.0",
|
106 |
+
"@codemirror/view": "^0.20.0",
|
107 |
+
"@lezer/common": "^0.16.0"
|
108 |
+
}
|
109 |
+
},
|
110 |
+
"node_modules/@codemirror/basic-setup/node_modules/@codemirror/commands": {
|
111 |
+
"version": "0.20.0",
|
112 |
+
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-0.20.0.tgz",
|
113 |
+
"integrity": "sha512-v9L5NNVA+A9R6zaFvaTbxs30kc69F6BkOoiEbeFw4m4I0exmDEKBILN6mK+GksJtvTzGBxvhAPlVFTdQW8GB7Q==",
|
114 |
+
"license": "MIT",
|
115 |
+
"dependencies": {
|
116 |
+
"@codemirror/language": "^0.20.0",
|
117 |
+
"@codemirror/state": "^0.20.0",
|
118 |
+
"@codemirror/view": "^0.20.0",
|
119 |
+
"@lezer/common": "^0.16.0"
|
120 |
+
}
|
121 |
+
},
|
122 |
+
"node_modules/@codemirror/basic-setup/node_modules/@codemirror/language": {
|
123 |
+
"version": "0.20.2",
|
124 |
+
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-0.20.2.tgz",
|
125 |
+
"integrity": "sha512-WB3Bnuusw0xhVvhBocieYKwJm04SOk5bPoOEYksVHKHcGHFOaYaw+eZVxR4gIqMMcGzOIUil0FsCmFk8yrhHpw==",
|
126 |
+
"license": "MIT",
|
127 |
+
"dependencies": {
|
128 |
+
"@codemirror/state": "^0.20.0",
|
129 |
+
"@codemirror/view": "^0.20.0",
|
130 |
+
"@lezer/common": "^0.16.0",
|
131 |
+
"@lezer/highlight": "^0.16.0",
|
132 |
+
"@lezer/lr": "^0.16.0",
|
133 |
+
"style-mod": "^4.0.0"
|
134 |
+
}
|
135 |
+
},
|
136 |
+
"node_modules/@codemirror/basic-setup/node_modules/@codemirror/lint": {
|
137 |
+
"version": "0.20.3",
|
138 |
+
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-0.20.3.tgz",
|
139 |
+
"integrity": "sha512-06xUScbbspZ8mKoODQCEx6hz1bjaq9m8W8DxdycWARMiiX1wMtfCh/MoHpaL7ws/KUMwlsFFfp2qhm32oaCvVA==",
|
140 |
+
"license": "MIT",
|
141 |
+
"dependencies": {
|
142 |
+
"@codemirror/state": "^0.20.0",
|
143 |
+
"@codemirror/view": "^0.20.2",
|
144 |
+
"crelt": "^1.0.5"
|
145 |
+
}
|
146 |
+
},
|
147 |
+
"node_modules/@codemirror/basic-setup/node_modules/@codemirror/search": {
|
148 |
+
"version": "0.20.1",
|
149 |
+
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-0.20.1.tgz",
|
150 |
+
"integrity": "sha512-ROe6gRboQU5E4z6GAkNa2kxhXqsGNbeLEisbvzbOeB7nuDYXUZ70vGIgmqPu0tB+1M3F9yWk6W8k2vrFpJaD4Q==",
|
151 |
+
"license": "MIT",
|
152 |
+
"dependencies": {
|
153 |
+
"@codemirror/state": "^0.20.0",
|
154 |
+
"@codemirror/view": "^0.20.0",
|
155 |
+
"crelt": "^1.0.5"
|
156 |
+
}
|
157 |
+
},
|
158 |
+
"node_modules/@codemirror/basic-setup/node_modules/@codemirror/state": {
|
159 |
+
"version": "0.20.1",
|
160 |
+
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.20.1.tgz",
|
161 |
+
"integrity": "sha512-ms0tlV5A02OK0pFvTtSUGMLkoarzh1F8mr6jy1cD7ucSC2X/VLHtQCxfhdSEGqTYlQF2hoZtmLv+amqhdgbwjQ==",
|
162 |
+
"license": "MIT"
|
163 |
+
},
|
164 |
+
"node_modules/@codemirror/basic-setup/node_modules/@codemirror/view": {
|
165 |
+
"version": "0.20.7",
|
166 |
+
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.20.7.tgz",
|
167 |
+
"integrity": "sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ==",
|
168 |
+
"license": "MIT",
|
169 |
+
"dependencies": {
|
170 |
+
"@codemirror/state": "^0.20.0",
|
171 |
+
"style-mod": "^4.0.0",
|
172 |
+
"w3c-keyname": "^2.2.4"
|
173 |
+
}
|
174 |
+
},
|
175 |
+
"node_modules/@codemirror/basic-setup/node_modules/@lezer/common": {
|
176 |
+
"version": "0.16.1",
|
177 |
+
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.16.1.tgz",
|
178 |
+
"integrity": "sha512-qPmG7YTZ6lATyTOAWf8vXE+iRrt1NJd4cm2nJHK+v7X9TsOF6+HtuU/ctaZy2RCrluxDb89hI6KWQ5LfQGQWuA==",
|
179 |
+
"license": "MIT"
|
180 |
+
},
|
181 |
+
"node_modules/@codemirror/basic-setup/node_modules/@lezer/highlight": {
|
182 |
+
"version": "0.16.0",
|
183 |
+
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-0.16.0.tgz",
|
184 |
+
"integrity": "sha512-iE5f4flHlJ1g1clOStvXNLbORJoiW4Kytso6ubfYzHnaNo/eo5SKhxs4wv/rtvwZQeZrK3we8S9SyA7OGOoRKQ==",
|
185 |
+
"license": "MIT",
|
186 |
+
"dependencies": {
|
187 |
+
"@lezer/common": "^0.16.0"
|
188 |
+
}
|
189 |
+
},
|
190 |
+
"node_modules/@codemirror/basic-setup/node_modules/@lezer/lr": {
|
191 |
+
"version": "0.16.3",
|
192 |
+
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.16.3.tgz",
|
193 |
+
"integrity": "sha512-pau7um4eAw94BEuuShUIeQDTf3k4Wt6oIUOYxMmkZgDHdqtIcxWND4LRxi8nI9KuT4I1bXQv67BCapkxt7Ywqw==",
|
194 |
+
"license": "MIT",
|
195 |
+
"dependencies": {
|
196 |
+
"@lezer/common": "^0.16.0"
|
197 |
+
}
|
198 |
+
},
|
199 |
+
"node_modules/@codemirror/commands": {
|
200 |
+
"version": "6.2.1",
|
201 |
+
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.2.1.tgz",
|
202 |
+
"integrity": "sha512-FFiNKGuHA5O8uC6IJE5apI5rT9gyjlw4whqy4vlcX0wE/myxL6P1s0upwDhY4HtMWLOwzwsp0ap3bjdQhvfDOA==",
|
203 |
+
"license": "MIT",
|
204 |
+
"dependencies": {
|
205 |
+
"@codemirror/language": "^6.0.0",
|
206 |
+
"@codemirror/state": "^6.2.0",
|
207 |
+
"@codemirror/view": "^6.0.0",
|
208 |
+
"@lezer/common": "^1.0.0"
|
209 |
+
}
|
210 |
+
},
|
211 |
+
"node_modules/@codemirror/lang-javascript": {
|
212 |
+
"version": "6.2.3",
|
213 |
+
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.3.tgz",
|
214 |
+
"integrity": "sha512-8PR3vIWg7pSu7ur8A07pGiYHgy3hHj+mRYRCSG8q+mPIrl0F02rgpGv+DsQTHRTc30rydOsf5PZ7yjKFg2Ackw==",
|
215 |
+
"license": "MIT",
|
216 |
+
"dependencies": {
|
217 |
+
"@codemirror/autocomplete": "^6.0.0",
|
218 |
+
"@codemirror/language": "^6.6.0",
|
219 |
+
"@codemirror/lint": "^6.0.0",
|
220 |
+
"@codemirror/state": "^6.0.0",
|
221 |
+
"@codemirror/view": "^6.17.0",
|
222 |
+
"@lezer/common": "^1.0.0",
|
223 |
+
"@lezer/javascript": "^1.0.0"
|
224 |
+
}
|
225 |
+
},
|
226 |
+
"node_modules/@codemirror/lang-json": {
|
227 |
+
"version": "6.0.1",
|
228 |
+
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
229 |
+
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
|
230 |
+
"license": "MIT",
|
231 |
+
"dependencies": {
|
232 |
+
"@codemirror/language": "^6.0.0",
|
233 |
+
"@lezer/json": "^1.0.0"
|
234 |
+
}
|
235 |
+
},
|
236 |
+
"node_modules/@codemirror/language": {
|
237 |
+
"version": "6.10.1",
|
238 |
+
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz",
|
239 |
+
"integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
|
240 |
+
"license": "MIT",
|
241 |
+
"dependencies": {
|
242 |
+
"@codemirror/state": "^6.0.0",
|
243 |
+
"@codemirror/view": "^6.23.0",
|
244 |
+
"@lezer/common": "^1.1.0",
|
245 |
+
"@lezer/highlight": "^1.0.0",
|
246 |
+
"@lezer/lr": "^1.0.0",
|
247 |
+
"style-mod": "^4.0.0"
|
248 |
+
}
|
249 |
+
},
|
250 |
+
"node_modules/@codemirror/legacy-modes": {
|
251 |
+
"version": "6.3.0",
|
252 |
+
"resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.3.0.tgz",
|
253 |
+
"integrity": "sha512-54ncmguzXZQ3u+8gx4/mpBRbkn6TLzfIrCGvFNLgB3giFYY3E2UETzv1RCFP0hM2L2bQBKBoI92i4ahrDAiE6g==",
|
254 |
+
"license": "MIT",
|
255 |
+
"dependencies": {
|
256 |
+
"@codemirror/language": "^6.0.0"
|
257 |
+
}
|
258 |
+
},
|
259 |
+
"node_modules/@codemirror/lint": {
|
260 |
+
"version": "6.8.5",
|
261 |
+
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz",
|
262 |
+
"integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==",
|
263 |
+
"license": "MIT",
|
264 |
+
"dependencies": {
|
265 |
+
"@codemirror/state": "^6.0.0",
|
266 |
+
"@codemirror/view": "^6.35.0",
|
267 |
+
"crelt": "^1.0.5"
|
268 |
+
}
|
269 |
+
},
|
270 |
+
"node_modules/@codemirror/search": {
|
271 |
+
"version": "6.5.11",
|
272 |
+
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz",
|
273 |
+
"integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==",
|
274 |
+
"license": "MIT",
|
275 |
+
"dependencies": {
|
276 |
+
"@codemirror/state": "^6.0.0",
|
277 |
+
"@codemirror/view": "^6.0.0",
|
278 |
+
"crelt": "^1.0.5"
|
279 |
+
}
|
280 |
+
},
|
281 |
+
"node_modules/@codemirror/state": {
|
282 |
+
"version": "6.5.2",
|
283 |
+
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
|
284 |
+
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
285 |
+
"license": "MIT",
|
286 |
+
"dependencies": {
|
287 |
+
"@marijn/find-cluster-break": "^1.0.0"
|
288 |
+
}
|
289 |
+
},
|
290 |
+
"node_modules/@codemirror/theme-one-dark": {
|
291 |
+
"version": "6.1.2",
|
292 |
+
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
|
293 |
+
"integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
|
294 |
+
"license": "MIT",
|
295 |
+
"dependencies": {
|
296 |
+
"@codemirror/language": "^6.0.0",
|
297 |
+
"@codemirror/state": "^6.0.0",
|
298 |
+
"@codemirror/view": "^6.0.0",
|
299 |
+
"@lezer/highlight": "^1.0.0"
|
300 |
+
}
|
301 |
+
},
|
302 |
+
"node_modules/@codemirror/view": {
|
303 |
+
"version": "6.36.7",
|
304 |
+
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.7.tgz",
|
305 |
+
"integrity": "sha512-kCWGW/chWGPgZqfZ36Um9Iz0X2IVpmCjg1P/qY6B6a2ecXtWRRAigmpJ6YgUQ5lTWXMyyVdfmpzhLZmsZQMbtg==",
|
306 |
+
"license": "MIT",
|
307 |
+
"dependencies": {
|
308 |
+
"@codemirror/state": "^6.5.0",
|
309 |
+
"style-mod": "^4.1.0",
|
310 |
+
"w3c-keyname": "^2.2.4"
|
311 |
+
}
|
312 |
+
},
|
313 |
"node_modules/@esbuild/aix-ppc64": {
|
314 |
"version": "0.25.4",
|
315 |
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz",
|
|
|
904 |
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
905 |
}
|
906 |
},
|
907 |
+
"node_modules/@huggingface/jinja": {
|
908 |
+
"version": "0.5.0",
|
909 |
+
"resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.0.tgz",
|
910 |
+
"integrity": "sha512-Ptc03/jGRiYRoi0bUYKZ14MkDslsBRT24oxmsvUlfYrvQMldrxCevhPnT+hfX8awKTT8/f/0ZBBWldoeAcMHdQ==",
|
911 |
+
"license": "MIT",
|
912 |
+
"engines": {
|
913 |
+
"node": ">=18"
|
914 |
+
}
|
915 |
+
},
|
916 |
"node_modules/@humanfs/core": {
|
917 |
"version": "0.19.1",
|
918 |
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
|
|
1032 |
"@jridgewell/sourcemap-codec": "^1.4.14"
|
1033 |
}
|
1034 |
},
|
1035 |
+
"node_modules/@lezer/common": {
|
1036 |
+
"version": "1.2.3",
|
1037 |
+
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz",
|
1038 |
+
"integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==",
|
1039 |
+
"license": "MIT"
|
1040 |
+
},
|
1041 |
+
"node_modules/@lezer/highlight": {
|
1042 |
+
"version": "1.2.1",
|
1043 |
+
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz",
|
1044 |
+
"integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==",
|
1045 |
+
"license": "MIT",
|
1046 |
+
"dependencies": {
|
1047 |
+
"@lezer/common": "^1.0.0"
|
1048 |
+
}
|
1049 |
+
},
|
1050 |
+
"node_modules/@lezer/javascript": {
|
1051 |
+
"version": "1.5.1",
|
1052 |
+
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.1.tgz",
|
1053 |
+
"integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
|
1054 |
+
"license": "MIT",
|
1055 |
+
"dependencies": {
|
1056 |
+
"@lezer/common": "^1.2.0",
|
1057 |
+
"@lezer/highlight": "^1.1.3",
|
1058 |
+
"@lezer/lr": "^1.3.0"
|
1059 |
+
}
|
1060 |
+
},
|
1061 |
+
"node_modules/@lezer/json": {
|
1062 |
+
"version": "1.0.3",
|
1063 |
+
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz",
|
1064 |
+
"integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==",
|
1065 |
+
"license": "MIT",
|
1066 |
+
"dependencies": {
|
1067 |
+
"@lezer/common": "^1.2.0",
|
1068 |
+
"@lezer/highlight": "^1.0.0",
|
1069 |
+
"@lezer/lr": "^1.0.0"
|
1070 |
+
}
|
1071 |
+
},
|
1072 |
+
"node_modules/@lezer/lr": {
|
1073 |
+
"version": "1.4.2",
|
1074 |
+
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz",
|
1075 |
+
"integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
|
1076 |
+
"license": "MIT",
|
1077 |
+
"dependencies": {
|
1078 |
+
"@lezer/common": "^1.0.0"
|
1079 |
+
}
|
1080 |
+
},
|
1081 |
+
"node_modules/@marijn/find-cluster-break": {
|
1082 |
+
"version": "1.0.2",
|
1083 |
+
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
|
1084 |
+
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",
|
1085 |
+
"license": "MIT"
|
1086 |
+
},
|
1087 |
"node_modules/@modelcontextprotocol/sdk": {
|
1088 |
"version": "1.11.0",
|
1089 |
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.0.tgz",
|
|
|
1151 |
"dev": true,
|
1152 |
"license": "MIT"
|
1153 |
},
|
1154 |
+
"node_modules/@rollup/plugin-commonjs": {
|
1155 |
+
"version": "28.0.3",
|
1156 |
+
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.3.tgz",
|
1157 |
+
"integrity": "sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ==",
|
1158 |
+
"dev": true,
|
1159 |
+
"license": "MIT",
|
1160 |
+
"dependencies": {
|
1161 |
+
"@rollup/pluginutils": "^5.0.1",
|
1162 |
+
"commondir": "^1.0.1",
|
1163 |
+
"estree-walker": "^2.0.2",
|
1164 |
+
"fdir": "^6.2.0",
|
1165 |
+
"is-reference": "1.2.1",
|
1166 |
+
"magic-string": "^0.30.3",
|
1167 |
+
"picomatch": "^4.0.2"
|
1168 |
+
},
|
1169 |
+
"engines": {
|
1170 |
+
"node": ">=16.0.0 || 14 >= 14.17"
|
1171 |
+
},
|
1172 |
+
"peerDependencies": {
|
1173 |
+
"rollup": "^2.68.0||^3.0.0||^4.0.0"
|
1174 |
+
},
|
1175 |
+
"peerDependenciesMeta": {
|
1176 |
+
"rollup": {
|
1177 |
+
"optional": true
|
1178 |
+
}
|
1179 |
+
}
|
1180 |
+
},
|
1181 |
+
"node_modules/@rollup/plugin-commonjs/node_modules/is-reference": {
|
1182 |
+
"version": "1.2.1",
|
1183 |
+
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
|
1184 |
+
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
|
1185 |
+
"dev": true,
|
1186 |
+
"license": "MIT",
|
1187 |
+
"dependencies": {
|
1188 |
+
"@types/estree": "*"
|
1189 |
+
}
|
1190 |
+
},
|
1191 |
+
"node_modules/@rollup/plugin-json": {
|
1192 |
+
"version": "6.1.0",
|
1193 |
+
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
|
1194 |
+
"integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
|
1195 |
+
"dev": true,
|
1196 |
+
"license": "MIT",
|
1197 |
+
"dependencies": {
|
1198 |
+
"@rollup/pluginutils": "^5.1.0"
|
1199 |
+
},
|
1200 |
+
"engines": {
|
1201 |
+
"node": ">=14.0.0"
|
1202 |
+
},
|
1203 |
+
"peerDependencies": {
|
1204 |
+
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
1205 |
+
},
|
1206 |
+
"peerDependenciesMeta": {
|
1207 |
+
"rollup": {
|
1208 |
+
"optional": true
|
1209 |
+
}
|
1210 |
+
}
|
1211 |
+
},
|
1212 |
+
"node_modules/@rollup/plugin-node-resolve": {
|
1213 |
+
"version": "16.0.1",
|
1214 |
+
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz",
|
1215 |
+
"integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==",
|
1216 |
+
"dev": true,
|
1217 |
+
"license": "MIT",
|
1218 |
+
"dependencies": {
|
1219 |
+
"@rollup/pluginutils": "^5.0.1",
|
1220 |
+
"@types/resolve": "1.20.2",
|
1221 |
+
"deepmerge": "^4.2.2",
|
1222 |
+
"is-module": "^1.0.0",
|
1223 |
+
"resolve": "^1.22.1"
|
1224 |
+
},
|
1225 |
+
"engines": {
|
1226 |
+
"node": ">=14.0.0"
|
1227 |
+
},
|
1228 |
+
"peerDependencies": {
|
1229 |
+
"rollup": "^2.78.0||^3.0.0||^4.0.0"
|
1230 |
+
},
|
1231 |
+
"peerDependenciesMeta": {
|
1232 |
+
"rollup": {
|
1233 |
+
"optional": true
|
1234 |
+
}
|
1235 |
+
}
|
1236 |
+
},
|
1237 |
+
"node_modules/@rollup/pluginutils": {
|
1238 |
+
"version": "5.1.4",
|
1239 |
+
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
|
1240 |
+
"integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
|
1241 |
+
"dev": true,
|
1242 |
+
"license": "MIT",
|
1243 |
+
"dependencies": {
|
1244 |
+
"@types/estree": "^1.0.0",
|
1245 |
+
"estree-walker": "^2.0.2",
|
1246 |
+
"picomatch": "^4.0.2"
|
1247 |
+
},
|
1248 |
+
"engines": {
|
1249 |
+
"node": ">=14.0.0"
|
1250 |
+
},
|
1251 |
+
"peerDependencies": {
|
1252 |
+
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
1253 |
+
},
|
1254 |
+
"peerDependenciesMeta": {
|
1255 |
+
"rollup": {
|
1256 |
+
"optional": true
|
1257 |
+
}
|
1258 |
+
}
|
1259 |
+
},
|
1260 |
"node_modules/@rollup/rollup-android-arm-eabi": {
|
1261 |
"version": "4.40.2",
|
1262 |
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz",
|
|
|
1560 |
"@sveltejs/kit": "^2.0.0"
|
1561 |
}
|
1562 |
},
|
1563 |
+
"node_modules/@sveltejs/adapter-node": {
|
1564 |
+
"version": "5.2.12",
|
1565 |
+
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.12.tgz",
|
1566 |
+
"integrity": "sha512-0bp4Yb3jKIEcZWVcJC/L1xXp9zzJS4hDwfb4VITAkfT4OVdkspSHsx7YhqJDbb2hgLl6R9Vs7VQR+fqIVOxPUQ==",
|
1567 |
+
"dev": true,
|
1568 |
+
"license": "MIT",
|
1569 |
+
"dependencies": {
|
1570 |
+
"@rollup/plugin-commonjs": "^28.0.1",
|
1571 |
+
"@rollup/plugin-json": "^6.1.0",
|
1572 |
+
"@rollup/plugin-node-resolve": "^16.0.0",
|
1573 |
+
"rollup": "^4.9.5"
|
1574 |
+
},
|
1575 |
+
"peerDependencies": {
|
1576 |
+
"@sveltejs/kit": "^2.4.0"
|
1577 |
+
}
|
1578 |
+
},
|
1579 |
"node_modules/@sveltejs/kit": {
|
1580 |
"version": "2.20.8",
|
1581 |
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.8.tgz",
|
|
|
1937 |
"dev": true,
|
1938 |
"license": "MIT"
|
1939 |
},
|
1940 |
+
"node_modules/@types/json5": {
|
1941 |
+
"version": "0.0.30",
|
1942 |
+
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.30.tgz",
|
1943 |
+
"integrity": "sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==",
|
1944 |
+
"dev": true,
|
1945 |
+
"license": "MIT"
|
1946 |
+
},
|
1947 |
+
"node_modules/@types/resolve": {
|
1948 |
+
"version": "1.20.2",
|
1949 |
+
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
1950 |
+
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
1951 |
+
"dev": true,
|
1952 |
+
"license": "MIT"
|
1953 |
+
},
|
1954 |
"node_modules/@typescript-eslint/eslint-plugin": {
|
1955 |
"version": "8.32.0",
|
1956 |
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.0.tgz",
|
|
|
2420 |
"dev": true,
|
2421 |
"license": "MIT"
|
2422 |
},
|
2423 |
+
"node_modules/commondir": {
|
2424 |
+
"version": "1.0.1",
|
2425 |
+
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
2426 |
+
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
|
2427 |
+
"dev": true,
|
2428 |
+
"license": "MIT"
|
2429 |
+
},
|
2430 |
"node_modules/concat-map": {
|
2431 |
"version": "0.0.1",
|
2432 |
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
|
|
2491 |
"node": ">= 0.10"
|
2492 |
}
|
2493 |
},
|
2494 |
+
"node_modules/crelt": {
|
2495 |
+
"version": "1.0.6",
|
2496 |
+
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
2497 |
+
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
|
2498 |
+
"license": "MIT"
|
2499 |
+
},
|
2500 |
"node_modules/cross-spawn": {
|
2501 |
"version": "7.0.6",
|
2502 |
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
|
|
2937 |
"node": ">=4.0"
|
2938 |
}
|
2939 |
},
|
2940 |
+
"node_modules/estree-walker": {
|
2941 |
+
"version": "2.0.2",
|
2942 |
+
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
2943 |
+
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
2944 |
+
"dev": true,
|
2945 |
+
"license": "MIT"
|
2946 |
+
},
|
2947 |
"node_modules/esutils": {
|
2948 |
"version": "2.0.3",
|
2949 |
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
|
|
3482 |
"node": ">= 0.10"
|
3483 |
}
|
3484 |
},
|
3485 |
+
"node_modules/is-core-module": {
|
3486 |
+
"version": "2.16.1",
|
3487 |
+
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
3488 |
+
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
3489 |
+
"dev": true,
|
3490 |
+
"license": "MIT",
|
3491 |
+
"dependencies": {
|
3492 |
+
"hasown": "^2.0.2"
|
3493 |
+
},
|
3494 |
+
"engines": {
|
3495 |
+
"node": ">= 0.4"
|
3496 |
+
},
|
3497 |
+
"funding": {
|
3498 |
+
"url": "https://github.com/sponsors/ljharb"
|
3499 |
+
}
|
3500 |
+
},
|
3501 |
"node_modules/is-extglob": {
|
3502 |
"version": "2.1.1",
|
3503 |
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
|
|
3521 |
"node": ">=0.10.0"
|
3522 |
}
|
3523 |
},
|
3524 |
+
"node_modules/is-module": {
|
3525 |
+
"version": "1.0.0",
|
3526 |
+
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
3527 |
+
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
|
3528 |
+
"dev": true,
|
3529 |
+
"license": "MIT"
|
3530 |
+
},
|
3531 |
"node_modules/is-number": {
|
3532 |
"version": "7.0.0",
|
3533 |
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
|
|
3606 |
"dev": true,
|
3607 |
"license": "MIT"
|
3608 |
},
|
3609 |
+
"node_modules/json5": {
|
3610 |
+
"version": "2.2.3",
|
3611 |
+
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
3612 |
+
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
3613 |
+
"license": "MIT",
|
3614 |
+
"bin": {
|
3615 |
+
"json5": "lib/cli.js"
|
3616 |
+
},
|
3617 |
+
"engines": {
|
3618 |
+
"node": ">=6"
|
3619 |
+
}
|
3620 |
+
},
|
3621 |
"node_modules/keyv": {
|
3622 |
"version": "4.5.4",
|
3623 |
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
|
|
4005 |
"node": ">=8.6"
|
4006 |
}
|
4007 |
},
|
4008 |
+
"node_modules/micromatch/node_modules/picomatch": {
|
4009 |
+
"version": "2.3.1",
|
4010 |
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
4011 |
+
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
4012 |
+
"dev": true,
|
4013 |
+
"license": "MIT",
|
4014 |
+
"engines": {
|
4015 |
+
"node": ">=8.6"
|
4016 |
+
},
|
4017 |
+
"funding": {
|
4018 |
+
"url": "https://github.com/sponsors/jonschlinkert"
|
4019 |
+
}
|
4020 |
+
},
|
4021 |
"node_modules/mime-db": {
|
4022 |
"version": "1.54.0",
|
4023 |
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
|
|
4256 |
"node": ">=8"
|
4257 |
}
|
4258 |
},
|
4259 |
+
"node_modules/path-parse": {
|
4260 |
+
"version": "1.0.7",
|
4261 |
+
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
4262 |
+
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
4263 |
+
"dev": true,
|
4264 |
+
"license": "MIT"
|
4265 |
+
},
|
4266 |
"node_modules/path-to-regexp": {
|
4267 |
"version": "8.2.0",
|
4268 |
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
|
|
|
4281 |
"license": "ISC"
|
4282 |
},
|
4283 |
"node_modules/picomatch": {
|
4284 |
+
"version": "4.0.2",
|
4285 |
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
4286 |
+
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
4287 |
"dev": true,
|
4288 |
"license": "MIT",
|
4289 |
"engines": {
|
4290 |
+
"node": ">=12"
|
4291 |
},
|
4292 |
"funding": {
|
4293 |
"url": "https://github.com/sponsors/jonschlinkert"
|
|
|
4657 |
"url": "https://paulmillr.com/funding/"
|
4658 |
}
|
4659 |
},
|
4660 |
+
"node_modules/resolve": {
|
4661 |
+
"version": "1.22.10",
|
4662 |
+
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
4663 |
+
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
4664 |
+
"dev": true,
|
4665 |
+
"license": "MIT",
|
4666 |
+
"dependencies": {
|
4667 |
+
"is-core-module": "^2.16.0",
|
4668 |
+
"path-parse": "^1.0.7",
|
4669 |
+
"supports-preserve-symlinks-flag": "^1.0.0"
|
4670 |
+
},
|
4671 |
+
"bin": {
|
4672 |
+
"resolve": "bin/resolve"
|
4673 |
+
},
|
4674 |
+
"engines": {
|
4675 |
+
"node": ">= 0.4"
|
4676 |
+
},
|
4677 |
+
"funding": {
|
4678 |
+
"url": "https://github.com/sponsors/ljharb"
|
4679 |
+
}
|
4680 |
+
},
|
4681 |
"node_modules/resolve-from": {
|
4682 |
"version": "4.0.0",
|
4683 |
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
|
|
5034 |
"url": "https://github.com/sponsors/sindresorhus"
|
5035 |
}
|
5036 |
},
|
5037 |
+
"node_modules/style-mod": {
|
5038 |
+
"version": "4.1.2",
|
5039 |
+
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
|
5040 |
+
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==",
|
5041 |
+
"license": "MIT"
|
5042 |
+
},
|
5043 |
"node_modules/supports-color": {
|
5044 |
"version": "7.2.0",
|
5045 |
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
|
|
5053 |
"node": ">=8"
|
5054 |
}
|
5055 |
},
|
5056 |
+
"node_modules/supports-preserve-symlinks-flag": {
|
5057 |
+
"version": "1.0.0",
|
5058 |
+
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
5059 |
+
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
5060 |
+
"dev": true,
|
5061 |
+
"license": "MIT",
|
5062 |
+
"engines": {
|
5063 |
+
"node": ">= 0.4"
|
5064 |
+
},
|
5065 |
+
"funding": {
|
5066 |
+
"url": "https://github.com/sponsors/ljharb"
|
5067 |
+
}
|
5068 |
+
},
|
5069 |
"node_modules/svelte": {
|
5070 |
"version": "5.28.2",
|
5071 |
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.28.2.tgz",
|
|
|
5179 |
"url": "https://github.com/sponsors/SuperchupuDev"
|
5180 |
}
|
5181 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5182 |
"node_modules/to-regex-range": {
|
5183 |
"version": "5.0.1",
|
5184 |
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
|
|
5402 |
}
|
5403 |
}
|
5404 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5405 |
"node_modules/vitefu": {
|
5406 |
"version": "1.0.6",
|
5407 |
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.6.tgz",
|
|
|
5421 |
}
|
5422 |
}
|
5423 |
},
|
5424 |
+
"node_modules/w3c-keyname": {
|
5425 |
+
"version": "2.2.8",
|
5426 |
+
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
5427 |
+
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
5428 |
+
"license": "MIT"
|
5429 |
+
},
|
5430 |
"node_modules/which": {
|
5431 |
"version": "2.0.2",
|
5432 |
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
package.json
CHANGED
@@ -17,9 +17,11 @@
|
|
17 |
"@eslint/compat": "^1.2.5",
|
18 |
"@eslint/js": "^9.18.0",
|
19 |
"@sveltejs/adapter-auto": "^6.0.0",
|
|
|
20 |
"@sveltejs/kit": "^2.16.0",
|
21 |
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
22 |
"@tailwindcss/vite": "^4.0.0",
|
|
|
23 |
"eslint": "^9.18.0",
|
24 |
"eslint-config-prettier": "^10.0.1",
|
25 |
"eslint-plugin-svelte": "^3.0.0",
|
@@ -33,5 +35,21 @@
|
|
33 |
"typescript": "^5.0.0",
|
34 |
"typescript-eslint": "^8.20.0",
|
35 |
"vite": "^6.2.6"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
}
|
37 |
}
|
|
|
17 |
"@eslint/compat": "^1.2.5",
|
18 |
"@eslint/js": "^9.18.0",
|
19 |
"@sveltejs/adapter-auto": "^6.0.0",
|
20 |
+
"@sveltejs/adapter-node": "^5.2.12",
|
21 |
"@sveltejs/kit": "^2.16.0",
|
22 |
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
23 |
"@tailwindcss/vite": "^4.0.0",
|
24 |
+
"@types/json5": "^0.0.30",
|
25 |
"eslint": "^9.18.0",
|
26 |
"eslint-config-prettier": "^10.0.1",
|
27 |
"eslint-plugin-svelte": "^3.0.0",
|
|
|
35 |
"typescript": "^5.0.0",
|
36 |
"typescript-eslint": "^8.20.0",
|
37 |
"vite": "^6.2.6"
|
38 |
+
},
|
39 |
+
"dependencies": {
|
40 |
+
"@codemirror/autocomplete": "^6.16.0",
|
41 |
+
"@codemirror/basic-setup": "^0.20.0",
|
42 |
+
"@codemirror/commands": "^6.2.1",
|
43 |
+
"@codemirror/lang-javascript": "^6.2.3",
|
44 |
+
"@codemirror/lang-json": "^6.0.1",
|
45 |
+
"@codemirror/language": "^6.10.1",
|
46 |
+
"@codemirror/legacy-modes": "^6.3.0",
|
47 |
+
"@codemirror/lint": "^6.8.5",
|
48 |
+
"@codemirror/search": "^6.5.11",
|
49 |
+
"@codemirror/state": "^6.4.1",
|
50 |
+
"@codemirror/theme-one-dark": "^6.1.2",
|
51 |
+
"@codemirror/view": "^6.26.3",
|
52 |
+
"@huggingface/jinja": "^0.5.0",
|
53 |
+
"json5": "^2.2.3"
|
54 |
}
|
55 |
}
|
src/app.css
CHANGED
@@ -1 +1,132 @@
|
|
1 |
@import 'tailwindcss';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
@import 'tailwindcss';
|
2 |
+
|
3 |
+
@custom-variant dark (&:where(.dark, .dark *));
|
4 |
+
|
5 |
+
.codemirror-wrapper {
|
6 |
+
@apply overflow-auto text-sm;
|
7 |
+
}
|
8 |
+
.codemirror-wrapper .cm-editor {
|
9 |
+
@apply bg-transparent;
|
10 |
+
}
|
11 |
+
.codemirror-wrapper .cm-content {
|
12 |
+
@apply min-h-[12rem] font-mono;
|
13 |
+
}
|
14 |
+
.codemirror-wrapper .cm-gutter {
|
15 |
+
@apply min-h-[12rem];
|
16 |
+
}
|
17 |
+
.codemirror-wrapper .cm-gutters {
|
18 |
+
@apply border-gray-200 bg-gray-50 text-gray-400 dark:border-gray-900 dark:bg-gray-900;
|
19 |
+
}
|
20 |
+
.codemirror-wrapper .cm-line {
|
21 |
+
@apply pl-2 selection:bg-blue-200! dark:bg-gray-900 dark:selection:bg-gray-700!;
|
22 |
+
}
|
23 |
+
.codemirror-wrapper .cm-activeLine {
|
24 |
+
@apply bg-blue-50 dark:bg-gray-900;
|
25 |
+
}
|
26 |
+
.codemirror-wrapper .cm-activeLineGutter {
|
27 |
+
@apply bg-blue-100 dark:bg-gray-900;
|
28 |
+
}
|
29 |
+
.codemirror-wrapper .cm-scroller,
|
30 |
+
.codemirror-wrapper .cm-editor.cm-focused {
|
31 |
+
@apply outline-none;
|
32 |
+
}
|
33 |
+
|
34 |
+
/* Hide codemirror default search component since we have CodeMirrorSearch.svelte */
|
35 |
+
.codemirror-wrapper .cm-search {
|
36 |
+
@apply hidden;
|
37 |
+
}
|
38 |
+
|
39 |
+
@utility btn-base {
|
40 |
+
@apply inline-flex cursor-pointer items-center justify-center rounded-lg border px-3 py-1 whitespace-nowrap select-none focus:ring-3 focus:outline-hidden;
|
41 |
+
}
|
42 |
+
|
43 |
+
@utility btn {
|
44 |
+
@apply btn-base;
|
45 |
+
@apply border-gray-200 bg-linear-to-b from-white to-gray-100 text-gray-800 hover:shadow-inner dark:border-gray-800 dark:from-gray-800 dark:to-gray-950 dark:text-gray-200 dark:hover:from-gray-700 dark:hover:to-gray-950;
|
46 |
+
&:disabled {
|
47 |
+
@apply cursor-not-allowed opacity-50;
|
48 |
+
}
|
49 |
+
&.btn-lg {
|
50 |
+
@apply px-4 py-1.5 font-normal;
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
@utility btn-widget {
|
55 |
+
@apply btn-base;
|
56 |
+
@apply h-8 bg-linear-to-b from-gray-50 to-gray-200 hover:from-gray-100 hover:to-gray-200 dark:border-gray-800 dark:from-gray-800 dark:to-gray-950 dark:hover:from-gray-700 dark:hover:to-gray-950;
|
57 |
+
&:disabled {
|
58 |
+
@apply cursor-not-allowed opacity-50;
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
@utility btn-warning {
|
63 |
+
@apply btn-base;
|
64 |
+
@apply border-orange-200 bg-linear-to-b from-white to-orange-100 text-orange-700 hover:shadow-inner dark:border-orange-800 dark:from-orange-800 dark:to-orange-900 dark:text-orange-200 dark:hover:from-orange-700 dark:hover:to-orange-900;
|
65 |
+
}
|
66 |
+
|
67 |
+
@utility btn-green {
|
68 |
+
@apply btn-base;
|
69 |
+
@apply border-green-500 bg-green-500 text-white hover:shadow-inner dark:border-gray-800 dark:from-gray-800 dark:to-gray-950 dark:text-gray-200 dark:hover:from-gray-700 dark:hover:to-gray-950;
|
70 |
+
}
|
71 |
+
|
72 |
+
@utility btn-pink {
|
73 |
+
@apply btn-base;
|
74 |
+
@apply border-red-400 bg-red-400 text-white hover:shadow-inner dark:border-gray-800 dark:from-gray-800 dark:to-gray-950 dark:text-gray-200 dark:hover:from-gray-700 dark:hover:to-gray-950;
|
75 |
+
}
|
76 |
+
|
77 |
+
@utility btn-sky {
|
78 |
+
@apply btn-base;
|
79 |
+
@apply border-sky-600 bg-sky-600 text-white hover:shadow-inner dark:border-gray-800 dark:from-gray-800 dark:to-gray-950 dark:text-gray-200 dark:hover:from-gray-700 dark:hover:to-gray-950;
|
80 |
+
}
|
81 |
+
|
82 |
+
@utility btn-sky-ligher {
|
83 |
+
@apply btn-base;
|
84 |
+
@apply border-sky-500 bg-sky-500 text-white hover:shadow-inner dark:border-gray-800 dark:from-gray-800 dark:to-gray-950 dark:text-gray-200 dark:hover:from-gray-700 dark:hover:to-gray-950;
|
85 |
+
}
|
86 |
+
|
87 |
+
@utility btn-fuchsia {
|
88 |
+
@apply btn-base;
|
89 |
+
@apply border-fuchsia-600 bg-fuchsia-600 text-white hover:shadow-inner dark:border-gray-800 dark:from-gray-800 dark:to-gray-950 dark:text-gray-200 dark:hover:from-gray-700;
|
90 |
+
}
|
91 |
+
|
92 |
+
@utility btn-disabled {
|
93 |
+
@apply cursor-not-allowed opacity-50;
|
94 |
+
}
|
95 |
+
|
96 |
+
@utility btn-widget-disabled {
|
97 |
+
@apply cursor-not-allowed opacity-50;
|
98 |
+
}
|
99 |
+
|
100 |
+
@utility btn-lg {
|
101 |
+
&.btn {
|
102 |
+
@apply px-4 py-1.5 font-normal;
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
@utility btn-green-lg {
|
107 |
+
@apply px-10 py-2;
|
108 |
+
}
|
109 |
+
|
110 |
+
@utility btn-fuchsia-lg {
|
111 |
+
@apply px-10 py-2;
|
112 |
+
}
|
113 |
+
|
114 |
+
@utility tooltip-mask {
|
115 |
+
@apply pointer-events-none absolute overflow-visible bg-transparent;
|
116 |
+
}
|
117 |
+
@utility tooltip {
|
118 |
+
@apply pointer-events-auto absolute z-50 w-max max-w-44 transform rounded-lg border-black bg-black p-2 text-xs leading-tight font-normal break-words text-white shadow transition-opacity dark:bg-gray-800;
|
119 |
+
}
|
120 |
+
@utility tooltip-arrow {
|
121 |
+
@apply absolute z-0 size-2 rotate-45 transform bg-black dark:bg-gray-800;
|
122 |
+
}
|
123 |
+
|
124 |
+
.alert {
|
125 |
+
@apply rounded-md border border-blue-100 bg-blue-50 px-3 py-2 text-blue-900 dark:border-blue-800/30 dark:bg-blue-800/30 dark:text-blue-200;
|
126 |
+
}
|
127 |
+
.alert a {
|
128 |
+
@apply underline;
|
129 |
+
}
|
130 |
+
.alert-error {
|
131 |
+
@apply border-red-100 bg-red-50 text-red-900 dark:border-red-800/30 dark:bg-red-800/30 dark:text-red-200;
|
132 |
+
}
|
src/app.html
CHANGED
@@ -6,7 +6,47 @@
|
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
7 |
%sveltekit.head%
|
8 |
</head>
|
|
|
9 |
<body data-sveltekit-preload-data="hover">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
<div style="display: contents">%sveltekit.body%</div>
|
11 |
</body>
|
12 |
</html>
|
|
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
7 |
%sveltekit.head%
|
8 |
</head>
|
9 |
+
|
10 |
<body data-sveltekit-preload-data="hover">
|
11 |
+
<style>
|
12 |
+
body {
|
13 |
+
background: white;
|
14 |
+
}
|
15 |
+
body.dark {
|
16 |
+
background: #101828;
|
17 |
+
}
|
18 |
+
</style>
|
19 |
+
<script>
|
20 |
+
(function () {
|
21 |
+
const urlParams = new URLSearchParams(window.location.search);
|
22 |
+
const theme = urlParams.get('_theme');
|
23 |
+
|
24 |
+
let systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
25 |
+
|
26 |
+
function updateTheme() {
|
27 |
+
if (theme === 'dark') {
|
28 |
+
document.body.classList.add('dark');
|
29 |
+
} else if (theme === 'light') {
|
30 |
+
document.body.classList.remove('dark');
|
31 |
+
} else if (theme === 'system' || theme === null || theme === undefined) {
|
32 |
+
if (systemPrefersDark) {
|
33 |
+
document.body.classList.add('dark');
|
34 |
+
} else {
|
35 |
+
document.body.classList.remove('dark');
|
36 |
+
}
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
+
// Initial theme update
|
41 |
+
updateTheme();
|
42 |
+
|
43 |
+
// Listen for system preference changes
|
44 |
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => {
|
45 |
+
systemPrefersDark = event.matches;
|
46 |
+
updateTheme();
|
47 |
+
});
|
48 |
+
})();
|
49 |
+
</script>
|
50 |
<div style="display: contents">%sveltekit.body%</div>
|
51 |
</body>
|
52 |
</html>
|
src/lib/ChatTemplateViewer/ChatTemplateViewer.svelte
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { StreamLanguage } from '@codemirror/language';
|
3 |
+
import CodeMirror from '$lib/CodeMirror/CodeMirror.svelte';
|
4 |
+
import { jinja2 } from '@codemirror/legacy-modes/mode/jinja2';
|
5 |
+
import { EditorView, lineNumbers } from '@codemirror/view';
|
6 |
+
import CopyButton from '$lib/CopyButton/CopyButton.svelte';
|
7 |
+
import IconCodeGeneration from '$lib/Icons/IconCodeGeneration.svelte';
|
8 |
+
import { tooltip } from '$lib/utils/tooltip';
|
9 |
+
import type { FormattedChatTemplate } from './types';
|
10 |
+
import LineWrapButton from '$lib/LineWrapButton/LineWrapButton.svelte';
|
11 |
+
import { createEventDispatcher } from 'svelte';
|
12 |
+
import IconRestart from '$lib/Icons/IconRestart.svelte';
|
13 |
+
|
14 |
+
export let modelId: string;
|
15 |
+
export let formattedTemplates: FormattedChatTemplate[] = [];
|
16 |
+
export let selectedTemplate: FormattedChatTemplate | undefined = undefined;
|
17 |
+
export let showFormattedTemplate = true;
|
18 |
+
|
19 |
+
let wrapLines = true;
|
20 |
+
|
21 |
+
const dispatch = createEventDispatcher<{ modelIdChange: string; templateChange: string }>();
|
22 |
+
|
23 |
+
async function handleUpdateEditor(e: CustomEvent<string>) {
|
24 |
+
const currentCode = e.detail;
|
25 |
+
if (selectedTemplate) {
|
26 |
+
showFormattedTemplate
|
27 |
+
? (selectedTemplate.formattedTemplate = currentCode)
|
28 |
+
: (selectedTemplate.template = currentCode);
|
29 |
+
}
|
30 |
+
}
|
31 |
+
</script>
|
32 |
+
|
33 |
+
<div class="h-full overflow-scroll bg-white dark:bg-gray-900">
|
34 |
+
<div class="sticky top-0 z-10 bg-white dark:bg-gray-900">
|
35 |
+
<div
|
36 |
+
class="text-semibold flex items-center gap-x-2 border-b border-gray-500 bg-linear-to-r from-green-200 to-white px-3 py-1.5 text-lg dark:from-green-700 dark:to-green-900 dark:text-gray-200"
|
37 |
+
>
|
38 |
+
Chat template{formattedTemplates.length > 1 ? 's' : ''} for
|
39 |
+
<a class="font-mono underline" href="https://huggingface.co/{modelId}" target="_blank"
|
40 |
+
>{modelId}</a
|
41 |
+
>
|
42 |
+
<button
|
43 |
+
class="btn ml-auto text-sm"
|
44 |
+
on:click={() => {
|
45 |
+
const newModelId = prompt('Enter model ID (ex: deepseek-ai/DeepSeek-R1)')?.trim();
|
46 |
+
if (newModelId) {
|
47 |
+
dispatch('modelIdChange', newModelId);
|
48 |
+
}
|
49 |
+
}}>change model</button
|
50 |
+
>
|
51 |
+
</div>
|
52 |
+
<div class="flex items-center border-b px-3 py-2">
|
53 |
+
{#if formattedTemplates.length > 1}
|
54 |
+
<div class="my-1.5 flex flex-wrap items-center gap-x-1 gap-y-0.5">
|
55 |
+
{#each formattedTemplates as template (template.name)}
|
56 |
+
<button
|
57 |
+
class="text-md flex items-center rounded-lg border px-1.5 py-1 leading-none select-none
|
58 |
+
{selectedTemplate?.name === template.name
|
59 |
+
? 'border-gray-800 bg-black text-white dark:bg-gray-700'
|
60 |
+
: 'cursor-pointer text-gray-500 opacity-90 hover:text-gray-700 hover:shadow-xs dark:hover:text-gray-200'}"
|
61 |
+
type="button"
|
62 |
+
on:click={() => {
|
63 |
+
selectedTemplate = template;
|
64 |
+
dispatch('templateChange', template.name);
|
65 |
+
}}
|
66 |
+
>
|
67 |
+
{template.name}
|
68 |
+
</button>
|
69 |
+
{/each}
|
70 |
+
</div>
|
71 |
+
{/if}
|
72 |
+
<div class="ml-auto flex items-center gap-x-2">
|
73 |
+
<!-- reset button -->
|
74 |
+
{#if showFormattedTemplate ? selectedTemplate?.formattedTemplate !== selectedTemplate?.formattedTemplateUnedited : selectedTemplate?.template !== selectedTemplate?.templateUnedited}
|
75 |
+
<button
|
76 |
+
class="relative inline-flex h-6! cursor-pointer items-center justify-center rounded-md border border-gray-500 bg-white p-0! px-1.5! text-sm shadow-xs focus:outline-hidden dark:bg-gray-900 dark:text-white [&_svg]:translate-x-px! [&_svg]:translate-y-px! [&_svg]:text-base!"
|
77 |
+
type="button"
|
78 |
+
on:click={() => {
|
79 |
+
if (selectedTemplate) {
|
80 |
+
showFormattedTemplate
|
81 |
+
? (selectedTemplate.formattedTemplate =
|
82 |
+
selectedTemplate.formattedTemplateUnedited)
|
83 |
+
: (selectedTemplate.template = selectedTemplate.templateUnedited);
|
84 |
+
}
|
85 |
+
}}
|
86 |
+
use:tooltip={'Reset template to original'}
|
87 |
+
><IconRestart classNames="dark:text-gray-200!" />
|
88 |
+
<span class="ml-1 text-sm select-none dark:text-gray-200!"> Reset </span>
|
89 |
+
</button>
|
90 |
+
{/if}
|
91 |
+
|
92 |
+
<CopyButton
|
93 |
+
label="Copy"
|
94 |
+
value={showFormattedTemplate
|
95 |
+
? (selectedTemplate?.formattedTemplate ?? '')
|
96 |
+
: (selectedTemplate?.template ?? '')}
|
97 |
+
style="button-clear"
|
98 |
+
classNames="h-6! [&_svg]:text-[0.7rem]! px-1.5! text-black! dark:text-gray-200!"
|
99 |
+
/>
|
100 |
+
|
101 |
+
<!-- format button -->
|
102 |
+
<button
|
103 |
+
class="relative inline-flex h-6! cursor-pointer items-center justify-center rounded-md border border-gray-500 bg-white p-0! px-1.5! text-sm shadow-xs focus:outline-hidden dark:bg-gray-900 dark:text-white [&_svg]:translate-x-px! [&_svg]:translate-y-px! [&_svg]:text-base!"
|
104 |
+
type="button"
|
105 |
+
on:click={() => {
|
106 |
+
showFormattedTemplate = !showFormattedTemplate;
|
107 |
+
}}
|
108 |
+
use:tooltip={'Format with @huggingface/jinja'}
|
109 |
+
><IconCodeGeneration classNames={showFormattedTemplate ? 'opacity-100' : 'opacity-40'} />
|
110 |
+
<span
|
111 |
+
class="ml-1 text-sm select-none {showFormattedTemplate ? 'opacity-100' : 'opacity-40'}"
|
112 |
+
>
|
113 |
+
Formatted
|
114 |
+
</span>
|
115 |
+
</button>
|
116 |
+
|
117 |
+
<LineWrapButton
|
118 |
+
style="button-clear"
|
119 |
+
bind:wrapLines
|
120 |
+
classNames="[&_svg]:text-xs! size-6! p-0!"
|
121 |
+
/>
|
122 |
+
</div>
|
123 |
+
</div>
|
124 |
+
</div>
|
125 |
+
|
126 |
+
<CodeMirror
|
127 |
+
value={showFormattedTemplate
|
128 |
+
? (selectedTemplate?.formattedTemplate ?? '')
|
129 |
+
: (selectedTemplate?.template ?? '')}
|
130 |
+
on:change={handleUpdateEditor}
|
131 |
+
extensions={[
|
132 |
+
lineNumbers(),
|
133 |
+
StreamLanguage.define(jinja2),
|
134 |
+
...[wrapLines ? [EditorView.lineWrapping] : []]
|
135 |
+
]}
|
136 |
+
/>
|
137 |
+
</div>
|
src/lib/ChatTemplateViewer/types.ts
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export interface FormattedChatTemplate {
|
2 |
+
name: string;
|
3 |
+
template: string;
|
4 |
+
formattedTemplate: string;
|
5 |
+
templateUnedited: string;
|
6 |
+
formattedTemplateUnedited: string;
|
7 |
+
}
|
8 |
+
|
9 |
+
export type ChatTemplate = string | Omit<FormattedChatTemplate, 'formattedTemplate'>[];
|
src/lib/CodeMirror/CodeMirror.svelte
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!-- original from: https://github.com/touchifyapp/svelte-codemirror-editor/blob/main/src/lib/CodeMirror.svelte -->
|
2 |
+
<script lang="ts">
|
3 |
+
import type { ViewUpdate } from '@codemirror/view';
|
4 |
+
|
5 |
+
import { createEventDispatcher, onMount } from 'svelte';
|
6 |
+
import { EditorView, keymap, placeholder as placeholderExt } from '@codemirror/view';
|
7 |
+
import { StateEffect, EditorState, type Extension } from '@codemirror/state';
|
8 |
+
import { indentWithTab } from '@codemirror/commands';
|
9 |
+
import { oneDark } from '@codemirror/theme-one-dark';
|
10 |
+
|
11 |
+
import IconSpin from '../Icons/IconSpin.svelte';
|
12 |
+
|
13 |
+
import { basicSetup } from './basicSetup';
|
14 |
+
import CodeMirrorSearch from '$lib/CodeMirrorSearch/CodeMirrorSearch.svelte';
|
15 |
+
|
16 |
+
export let classNames = '';
|
17 |
+
export let loaderClassNames = '';
|
18 |
+
export let value = '';
|
19 |
+
export let fontSize: string | undefined = undefined;
|
20 |
+
|
21 |
+
export let basic = true;
|
22 |
+
export let extensions: Extension[] = [];
|
23 |
+
|
24 |
+
export let useTab = true;
|
25 |
+
|
26 |
+
export let editable = true;
|
27 |
+
export let readonly = false;
|
28 |
+
export let placeholder: string | HTMLElement | null | undefined = undefined;
|
29 |
+
export let focusOnMount = false;
|
30 |
+
export let view: EditorView | undefined = undefined;
|
31 |
+
|
32 |
+
const isBrowser = typeof window !== 'undefined';
|
33 |
+
const dispatch = createEventDispatcher<{ change: string }>();
|
34 |
+
let element: HTMLDivElement;
|
35 |
+
let isSearchOpen = false;
|
36 |
+
|
37 |
+
$: reconfigure(), extensions;
|
38 |
+
$: setDoc(value);
|
39 |
+
|
40 |
+
function setDoc(newDoc: string) {
|
41 |
+
if (view && newDoc !== view.state.doc.toString()) {
|
42 |
+
view.dispatch({
|
43 |
+
changes: {
|
44 |
+
from: 0,
|
45 |
+
to: view.state.doc.length,
|
46 |
+
insert: newDoc
|
47 |
+
}
|
48 |
+
});
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
function createEditorView(): EditorView {
|
53 |
+
return new EditorView({
|
54 |
+
parent: element,
|
55 |
+
state: createEditorState(value)
|
56 |
+
});
|
57 |
+
}
|
58 |
+
|
59 |
+
function handleChange(vu: ViewUpdate): void {
|
60 |
+
if (vu.docChanged) {
|
61 |
+
const doc = vu.state.doc;
|
62 |
+
const text = doc.toString();
|
63 |
+
dispatch('change', text);
|
64 |
+
}
|
65 |
+
}
|
66 |
+
|
67 |
+
function getExtensions() {
|
68 |
+
const stateExtensions = [
|
69 |
+
...getBaseExtensions(basic, useTab, placeholder, editable, readonly),
|
70 |
+
...getTheme(),
|
71 |
+
...extensions
|
72 |
+
];
|
73 |
+
return stateExtensions;
|
74 |
+
}
|
75 |
+
|
76 |
+
function createEditorState(value: string | null | undefined): EditorState {
|
77 |
+
return EditorState.create({
|
78 |
+
doc: value ?? undefined,
|
79 |
+
extensions: getExtensions()
|
80 |
+
});
|
81 |
+
}
|
82 |
+
|
83 |
+
function getBaseExtensions(
|
84 |
+
basic: boolean,
|
85 |
+
useTab: boolean,
|
86 |
+
placeholder: string | HTMLElement | null | undefined,
|
87 |
+
editable: boolean,
|
88 |
+
readonly: boolean
|
89 |
+
): Extension[] {
|
90 |
+
const extensions: Extension[] = [
|
91 |
+
EditorView.editable.of(editable),
|
92 |
+
EditorState.readOnly.of(readonly)
|
93 |
+
];
|
94 |
+
|
95 |
+
if (basic) {
|
96 |
+
extensions.push(basicSetup);
|
97 |
+
}
|
98 |
+
if (useTab) {
|
99 |
+
extensions.push(keymap.of([indentWithTab]));
|
100 |
+
}
|
101 |
+
if (placeholder) {
|
102 |
+
extensions.push(placeholderExt(placeholder));
|
103 |
+
}
|
104 |
+
if (fontSize) {
|
105 |
+
extensions.push(
|
106 |
+
EditorView.theme({
|
107 |
+
'&': {
|
108 |
+
fontSize: fontSize
|
109 |
+
}
|
110 |
+
})
|
111 |
+
);
|
112 |
+
}
|
113 |
+
|
114 |
+
extensions.push(EditorView.updateListener.of(handleChange));
|
115 |
+
return extensions;
|
116 |
+
}
|
117 |
+
|
118 |
+
function getTheme(): Extension[] {
|
119 |
+
const extensions: Extension[] = [];
|
120 |
+
const isDarkMode = document.querySelector('body')?.classList.contains('dark') ?? false;
|
121 |
+
if (isDarkMode) {
|
122 |
+
extensions.push(oneDark);
|
123 |
+
}
|
124 |
+
return extensions;
|
125 |
+
}
|
126 |
+
|
127 |
+
function reconfigure(): void {
|
128 |
+
view?.dispatch({
|
129 |
+
effects: StateEffect.reconfigure.of(getExtensions())
|
130 |
+
});
|
131 |
+
}
|
132 |
+
|
133 |
+
function onKeyDown(e: KeyboardEvent) {
|
134 |
+
const { ctrlKey, metaKey, key } = e;
|
135 |
+
const isOpenShortcut = key === 'f3' || ((metaKey || ctrlKey) && key === 'f');
|
136 |
+
if (isOpenShortcut) {
|
137 |
+
isSearchOpen = true;
|
138 |
+
e.preventDefault();
|
139 |
+
}
|
140 |
+
}
|
141 |
+
|
142 |
+
onMount(() => {
|
143 |
+
view = createEditorView();
|
144 |
+
if (view && focusOnMount) {
|
145 |
+
const tr = view.state.update({
|
146 |
+
selection: { anchor: view.state.doc.length }
|
147 |
+
});
|
148 |
+
view.dispatch(tr);
|
149 |
+
view.focus();
|
150 |
+
}
|
151 |
+
return () => view?.destroy();
|
152 |
+
});
|
153 |
+
</script>
|
154 |
+
|
155 |
+
{#if isBrowser}
|
156 |
+
<div class="relative">
|
157 |
+
<div class="codemirror-wrapper {classNames}" bind:this={element} on:keydown={onKeyDown} />
|
158 |
+
{#if isSearchOpen && view}
|
159 |
+
<CodeMirrorSearch {view} on:close={() => (isSearchOpen = false)} />
|
160 |
+
{/if}
|
161 |
+
</div>
|
162 |
+
{:else}
|
163 |
+
<div class="flex h-64 items-center justify-center {loaderClassNames}">
|
164 |
+
<IconSpin classNames="animate-spin text-xs" />
|
165 |
+
</div>
|
166 |
+
{/if}
|
src/lib/CodeMirror/basicSetup.ts
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/// Mostly similar to https://codemirror.net/docs/ref/#codemirror.basicSetup
|
2 |
+
/// src: https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts
|
3 |
+
import type { Extension } from '@codemirror/state';
|
4 |
+
import {
|
5 |
+
highlightActiveLineGutter,
|
6 |
+
highlightSpecialChars,
|
7 |
+
drawSelection,
|
8 |
+
dropCursor,
|
9 |
+
rectangularSelection,
|
10 |
+
crosshairCursor,
|
11 |
+
highlightActiveLine,
|
12 |
+
keymap
|
13 |
+
} from '@codemirror/view';
|
14 |
+
export { EditorView } from '@codemirror/view';
|
15 |
+
import { EditorState } from '@codemirror/state';
|
16 |
+
import {
|
17 |
+
indentOnInput,
|
18 |
+
syntaxHighlighting,
|
19 |
+
defaultHighlightStyle,
|
20 |
+
bracketMatching,
|
21 |
+
foldKeymap
|
22 |
+
} from '@codemirror/language';
|
23 |
+
import { history, defaultKeymap, historyKeymap } from '@codemirror/commands';
|
24 |
+
import {
|
25 |
+
closeBrackets,
|
26 |
+
autocompletion,
|
27 |
+
closeBracketsKeymap,
|
28 |
+
completionKeymap
|
29 |
+
} from '@codemirror/autocomplete';
|
30 |
+
import { indentationMarkers } from './indentationMarkers';
|
31 |
+
|
32 |
+
export const basicSetup: Extension = /*@__PURE__*/ (() => [
|
33 |
+
highlightActiveLineGutter(),
|
34 |
+
highlightSpecialChars(),
|
35 |
+
history(),
|
36 |
+
drawSelection(),
|
37 |
+
dropCursor(),
|
38 |
+
EditorState.allowMultipleSelections.of(true),
|
39 |
+
indentOnInput(),
|
40 |
+
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
41 |
+
bracketMatching(),
|
42 |
+
closeBrackets(),
|
43 |
+
autocompletion(),
|
44 |
+
rectangularSelection(),
|
45 |
+
crosshairCursor(),
|
46 |
+
highlightActiveLine(),
|
47 |
+
keymap.of([
|
48 |
+
...closeBracketsKeymap,
|
49 |
+
...defaultKeymap,
|
50 |
+
...historyKeymap,
|
51 |
+
...foldKeymap,
|
52 |
+
...completionKeymap
|
53 |
+
]),
|
54 |
+
indentationMarkers({
|
55 |
+
highlightActiveBlock: false
|
56 |
+
})
|
57 |
+
])();
|
src/lib/CodeMirror/indentationMarkers.ts
ADDED
@@ -0,0 +1,541 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// original: https://github.com/replit/codemirror-indentation-markers/tree/f2f8ccdc58fcc51dbe6a82784f2f83fa6ac4648c/src/
|
2 |
+
// combined all 3 files (index.ts, map.ts, utils.ts) from original source into this one file
|
3 |
+
import { getIndentUnit } from '@codemirror/language';
|
4 |
+
import type { EditorState, Extension, Line } from '@codemirror/state';
|
5 |
+
import { combineConfig, Facet, RangeSetBuilder } from '@codemirror/state';
|
6 |
+
import type { DecorationSet, ViewUpdate, PluginValue } from '@codemirror/view';
|
7 |
+
import { Decoration, ViewPlugin, EditorView } from '@codemirror/view';
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Gets the visible lines in the editor. Lines will not be repeated.
|
11 |
+
*
|
12 |
+
* @param view - The editor view to get the visible lines from.
|
13 |
+
* @param state - The editor state. Defaults to the view's current one.
|
14 |
+
*/
|
15 |
+
export function getVisibleLines(view: EditorView, state = view.state): Set<Line> {
|
16 |
+
const lines = new Set<Line>();
|
17 |
+
|
18 |
+
for (const { from, to } of view.visibleRanges) {
|
19 |
+
let pos = from;
|
20 |
+
|
21 |
+
while (pos <= to) {
|
22 |
+
const line = state.doc.lineAt(pos);
|
23 |
+
|
24 |
+
if (!lines.has(line)) {
|
25 |
+
lines.add(line);
|
26 |
+
}
|
27 |
+
|
28 |
+
pos = line.to + 1;
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
return lines;
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Gets the line at the position of the primary cursor.
|
37 |
+
*
|
38 |
+
* @param state - The editor state from which to extract the line.
|
39 |
+
*/
|
40 |
+
export function getCurrentLine(state: EditorState): Line {
|
41 |
+
const currentPos = state.selection.main.head;
|
42 |
+
return state.doc.lineAt(currentPos);
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Returns the number of columns that a string is indented, controlling for
|
47 |
+
* tabs. This is useful for determining the indentation level of a line.
|
48 |
+
*
|
49 |
+
* Note that this only returns the number of _visible_ columns, not the number
|
50 |
+
* of whitespace characters at the start of the string.
|
51 |
+
*
|
52 |
+
* @param str - The string to check.
|
53 |
+
* @param tabSize - The size of a tab character. Usually 2 or 4.
|
54 |
+
*/
|
55 |
+
export function numColumns(str: string, tabSize: number): number {
|
56 |
+
// as far as I can tell, this is pretty much the fastest way to do this,
|
57 |
+
// at least involving iteration. `str.length - str.trimStart().length` is
|
58 |
+
// much faster, but it has some edge cases that are hard to deal with.
|
59 |
+
|
60 |
+
let col = 0;
|
61 |
+
|
62 |
+
// eslint-disable-next-line no-restricted-syntax, @typescript-eslint/prefer-for-of
|
63 |
+
loop: for (let i = 0; i < str.length; i++) {
|
64 |
+
switch (str[i]) {
|
65 |
+
case ' ': {
|
66 |
+
col += 1;
|
67 |
+
continue loop;
|
68 |
+
}
|
69 |
+
|
70 |
+
case '\t': {
|
71 |
+
// if the current column is a multiple of the tab size, we can just
|
72 |
+
// add the tab size to the column. otherwise, we need to add the
|
73 |
+
// difference between the tab size and the current column.
|
74 |
+
col += tabSize - (col % tabSize);
|
75 |
+
continue loop;
|
76 |
+
}
|
77 |
+
|
78 |
+
case '\r': {
|
79 |
+
continue loop;
|
80 |
+
}
|
81 |
+
|
82 |
+
default: {
|
83 |
+
break loop;
|
84 |
+
}
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
return col;
|
89 |
+
}
|
90 |
+
|
91 |
+
export interface IndentEntry {
|
92 |
+
line: Line;
|
93 |
+
col: number;
|
94 |
+
level: number;
|
95 |
+
empty: boolean;
|
96 |
+
active?: number;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Indentation map for a set of lines.
|
101 |
+
*
|
102 |
+
* This map will contain the indentation for lines that are not a part of the given set,
|
103 |
+
* but this is because calculating the indentation for those lines was necessary to
|
104 |
+
* calculate the indentation for the lines provided to the constructor.
|
105 |
+
*
|
106 |
+
* @see {@link IndentEntry}
|
107 |
+
*/
|
108 |
+
export class IndentationMap {
|
109 |
+
/** The {@link EditorState} indentation is derived from. */
|
110 |
+
private state: EditorState;
|
111 |
+
|
112 |
+
/** The set of lines that are used as an entrypoint. */
|
113 |
+
private lines: Set<Line>;
|
114 |
+
|
115 |
+
/** The internal mapping of line numbers to {@link IndentEntry} objects. */
|
116 |
+
private map: Map<number, IndentEntry>;
|
117 |
+
|
118 |
+
/** The width of the editor's indent unit. */
|
119 |
+
private unitWidth: number;
|
120 |
+
|
121 |
+
/**
|
122 |
+
* @param lines - The set of lines to get the indentation map for.
|
123 |
+
* @param state - The {@link EditorState} to derive the indentation map from.
|
124 |
+
* @param unitWidth - The width of the editor's indent unit.
|
125 |
+
*/
|
126 |
+
constructor(lines: Set<Line>, state: EditorState, unitWidth: number) {
|
127 |
+
this.lines = lines;
|
128 |
+
this.state = state;
|
129 |
+
this.map = new Map();
|
130 |
+
this.unitWidth = unitWidth;
|
131 |
+
|
132 |
+
for (const line of this.lines) {
|
133 |
+
this.add(line);
|
134 |
+
}
|
135 |
+
|
136 |
+
if (this.state.facet(indentationMarkerConfig).highlightActiveBlock) {
|
137 |
+
this.findAndSetActiveLines();
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Checks if the indentation map has an entry for the given line.
|
143 |
+
*
|
144 |
+
* @param line - The {@link Line} or line number to check for.
|
145 |
+
*/
|
146 |
+
has(line: Line | number): boolean {
|
147 |
+
return this.map.has(typeof line === 'number' ? line : line.number);
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Returns the {@link IndentEntry} for the given line.
|
152 |
+
*
|
153 |
+
* Note that this function will throw an error if the line does not exist in the map.
|
154 |
+
*
|
155 |
+
* @param line - The {@link Line} or line number to get the entry for.
|
156 |
+
*/
|
157 |
+
get(line: Line | number): IndentEntry {
|
158 |
+
const entry = this.map.get(typeof line === 'number' ? line : line.number);
|
159 |
+
|
160 |
+
if (!entry) {
|
161 |
+
throw new Error('Line not found in indentation map');
|
162 |
+
}
|
163 |
+
|
164 |
+
return entry;
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Sets the {@link IndentEntry} for the given line.
|
169 |
+
*
|
170 |
+
* @param line - The {@link Line} to set the entry for.
|
171 |
+
* @param col - The visual beginning whitespace width of the line.
|
172 |
+
* @param level - The indentation level of the line.
|
173 |
+
*/
|
174 |
+
private set(line: Line, col: number, level: number) {
|
175 |
+
const empty = !line.text.trim().length;
|
176 |
+
const entry: IndentEntry = { line, col, level, empty };
|
177 |
+
this.map.set(entry.line.number, entry);
|
178 |
+
|
179 |
+
return entry;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Adds a line to the indentation map.
|
184 |
+
*
|
185 |
+
* @param line - The {@link Line} to add to the map.
|
186 |
+
*/
|
187 |
+
private add(line: Line) {
|
188 |
+
if (this.has(line)) {
|
189 |
+
return this.get(line);
|
190 |
+
}
|
191 |
+
|
192 |
+
// empty lines continue their indentation from surrounding lines
|
193 |
+
if (!line.length || !line.text.trim().length) {
|
194 |
+
// the very first line, if empty, is just ignored and set as a 0 indent level
|
195 |
+
if (line.number === 1) {
|
196 |
+
return this.set(line, 0, 0);
|
197 |
+
}
|
198 |
+
|
199 |
+
// if we're at the end, we'll just use the previous line's indentation
|
200 |
+
if (line.number === this.state.doc.lines) {
|
201 |
+
const prev = this.closestNonEmpty(line, -1);
|
202 |
+
|
203 |
+
return this.set(line, 0, prev.level);
|
204 |
+
}
|
205 |
+
|
206 |
+
const prev = this.closestNonEmpty(line, -1);
|
207 |
+
const next = this.closestNonEmpty(line, 1);
|
208 |
+
|
209 |
+
// if the next line ends the block, we'll use the previous line's indentation
|
210 |
+
if (prev.level >= next.level) {
|
211 |
+
return this.set(line, 0, prev.level);
|
212 |
+
}
|
213 |
+
|
214 |
+
// having an indent marker that starts from an empty line looks weird
|
215 |
+
if (prev.empty && prev.level === 0 && next.level !== 0) {
|
216 |
+
return this.set(line, 0, 0);
|
217 |
+
}
|
218 |
+
|
219 |
+
// if the next indentation level is greater than the previous,
|
220 |
+
// we'll only increment up to the next indentation level. this prevents
|
221 |
+
// a weirdly "backwards propagating" indentation.
|
222 |
+
if (next.level > prev.level) {
|
223 |
+
return this.set(line, 0, prev.level + 1);
|
224 |
+
}
|
225 |
+
|
226 |
+
// else, we default to the next line's indentation
|
227 |
+
return this.set(line, 0, next.level);
|
228 |
+
}
|
229 |
+
|
230 |
+
const col = numColumns(line.text, this.state.tabSize);
|
231 |
+
const level = Math.floor(col / this.unitWidth);
|
232 |
+
|
233 |
+
return this.set(line, col, level);
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Finds the closest non-empty line, starting from the given line.
|
238 |
+
*
|
239 |
+
* @param from - The {@link Line} to start from.
|
240 |
+
* @param dir - The direction to search in. Either `1` or `-1`.
|
241 |
+
*/
|
242 |
+
private closestNonEmpty(from: Line, dir: -1 | 1) {
|
243 |
+
let lineNo = from.number + dir;
|
244 |
+
|
245 |
+
while (dir === -1 ? lineNo >= 1 : lineNo <= this.state.doc.lines) {
|
246 |
+
if (this.has(lineNo)) {
|
247 |
+
const entry = this.get(lineNo);
|
248 |
+
if (!entry.empty) {
|
249 |
+
return entry;
|
250 |
+
}
|
251 |
+
}
|
252 |
+
|
253 |
+
// we can check if the line is empty, if it's not, we can
|
254 |
+
// just create a new entry for it and return it.
|
255 |
+
// this prevents us from hitting the beginning/end of the document unnecessarily.
|
256 |
+
|
257 |
+
const line = this.state.doc.line(lineNo);
|
258 |
+
|
259 |
+
if (line.text.trim().length) {
|
260 |
+
const col = numColumns(line.text, this.state.tabSize);
|
261 |
+
const level = Math.floor(col / this.unitWidth);
|
262 |
+
|
263 |
+
return this.set(line, col, level);
|
264 |
+
}
|
265 |
+
|
266 |
+
lineNo += dir;
|
267 |
+
}
|
268 |
+
|
269 |
+
// if we're here, we didn't find anything.
|
270 |
+
// that means we're at the beginning/end of the document,
|
271 |
+
// and the first/last line is empty.
|
272 |
+
|
273 |
+
const line = this.state.doc.line(dir === -1 ? 1 : this.state.doc.lines);
|
274 |
+
|
275 |
+
return this.set(line, 0, 0);
|
276 |
+
}
|
277 |
+
|
278 |
+
/**
|
279 |
+
* Finds the state's active block (via the current selection) and sets all
|
280 |
+
* the active indent level for the lines in the block.
|
281 |
+
*/
|
282 |
+
private findAndSetActiveLines() {
|
283 |
+
const currentLine = getCurrentLine(this.state);
|
284 |
+
|
285 |
+
if (!this.has(currentLine)) {
|
286 |
+
return;
|
287 |
+
}
|
288 |
+
|
289 |
+
let current = this.get(currentLine);
|
290 |
+
|
291 |
+
// check if the current line is starting a new block, if yes, we want to
|
292 |
+
// start from inside the block.
|
293 |
+
if (this.has(current.line.number + 1)) {
|
294 |
+
const next = this.get(current.line.number + 1);
|
295 |
+
if (next.level > current.level) {
|
296 |
+
current = next;
|
297 |
+
}
|
298 |
+
}
|
299 |
+
|
300 |
+
// same, but if the current line is ending a block
|
301 |
+
if (this.has(current.line.number - 1)) {
|
302 |
+
const prev = this.get(current.line.number - 1);
|
303 |
+
if (prev.level > current.level) {
|
304 |
+
current = prev;
|
305 |
+
}
|
306 |
+
}
|
307 |
+
|
308 |
+
if (current.level === 0) {
|
309 |
+
return;
|
310 |
+
}
|
311 |
+
|
312 |
+
current.active = current.level;
|
313 |
+
|
314 |
+
let start: number;
|
315 |
+
let end: number;
|
316 |
+
|
317 |
+
// iterate to the start of the block
|
318 |
+
for (start = current.line.number; start > 1; start--) {
|
319 |
+
if (!this.has(start - 1)) {
|
320 |
+
continue;
|
321 |
+
}
|
322 |
+
|
323 |
+
const prev = this.get(start - 1);
|
324 |
+
|
325 |
+
if (prev.level < current.level) {
|
326 |
+
break;
|
327 |
+
}
|
328 |
+
|
329 |
+
prev.active = current.level;
|
330 |
+
}
|
331 |
+
|
332 |
+
// iterate to the end of the block
|
333 |
+
for (end = current.line.number; end < this.state.doc.lines; end++) {
|
334 |
+
if (!this.has(end + 1)) {
|
335 |
+
continue;
|
336 |
+
}
|
337 |
+
|
338 |
+
const next = this.get(end + 1);
|
339 |
+
|
340 |
+
if (next.level < current.level) {
|
341 |
+
break;
|
342 |
+
}
|
343 |
+
|
344 |
+
next.active = current.level;
|
345 |
+
}
|
346 |
+
}
|
347 |
+
}
|
348 |
+
|
349 |
+
// CSS classes:
|
350 |
+
// - .cm-indent-markers
|
351 |
+
|
352 |
+
// CSS variables:
|
353 |
+
// - --indent-marker-bg-part
|
354 |
+
// - --indent-marker-active-bg-part
|
355 |
+
|
356 |
+
/** Color of inactive indent markers. Based on RUI's var(--background-higher) */
|
357 |
+
const MARKER_COLOR_LIGHT = '#F0F1F2';
|
358 |
+
const MARKER_COLOR_DARK = '#2B3245';
|
359 |
+
|
360 |
+
/** Color of active indent markers. Based on RUI's var(--background-highest) */
|
361 |
+
const MARKER_COLOR_ACTIVE_LIGHT = '#E4E5E6';
|
362 |
+
const MARKER_COLOR_ACTIVE_DARK = '#3C445C';
|
363 |
+
|
364 |
+
/** Thickness of indent markers. Probably should be integer pixel values. */
|
365 |
+
const MARKER_THICKNESS = '1px';
|
366 |
+
|
367 |
+
const indentTheme = EditorView.baseTheme({
|
368 |
+
'&light': {
|
369 |
+
'--indent-marker-bg-color': MARKER_COLOR_LIGHT,
|
370 |
+
'--indent-marker-active-bg-color': MARKER_COLOR_ACTIVE_LIGHT
|
371 |
+
},
|
372 |
+
|
373 |
+
'&dark': {
|
374 |
+
'--indent-marker-bg-color': MARKER_COLOR_DARK,
|
375 |
+
'--indent-marker-active-bg-color': MARKER_COLOR_ACTIVE_DARK
|
376 |
+
},
|
377 |
+
|
378 |
+
'.cm-line': {
|
379 |
+
position: 'relative'
|
380 |
+
},
|
381 |
+
|
382 |
+
// this pseudo-element is used to draw the indent markers,
|
383 |
+
// while still allowing the line to have its own background.
|
384 |
+
'.cm-indent-markers::before': {
|
385 |
+
content: '""',
|
386 |
+
position: 'absolute',
|
387 |
+
top: 0,
|
388 |
+
left: '2px',
|
389 |
+
right: 0,
|
390 |
+
bottom: 0,
|
391 |
+
background: 'var(--indent-markers)',
|
392 |
+
pointerEvents: 'none'
|
393 |
+
// zIndex: '-1',
|
394 |
+
}
|
395 |
+
});
|
396 |
+
|
397 |
+
function createGradient(
|
398 |
+
markerCssProperty: string,
|
399 |
+
indentWidth: number,
|
400 |
+
startOffset: number,
|
401 |
+
columns: number
|
402 |
+
) {
|
403 |
+
const gradient = `repeating-linear-gradient(to right, var(${markerCssProperty}) 0 ${MARKER_THICKNESS}, transparent ${MARKER_THICKNESS} ${indentWidth}ch)`;
|
404 |
+
// Subtract one pixel from the background width to get rid of artifacts of pixel rounding
|
405 |
+
return `${gradient} ${startOffset * indentWidth}.5ch/calc(${indentWidth * columns}ch - 1px) no-repeat`;
|
406 |
+
}
|
407 |
+
|
408 |
+
function makeBackgroundCSS(
|
409 |
+
entry: IndentEntry,
|
410 |
+
indentWidth: number,
|
411 |
+
hideFirstIndent: boolean
|
412 |
+
): string {
|
413 |
+
const { level, active } = entry;
|
414 |
+
if (hideFirstIndent && level === 0) {
|
415 |
+
return '';
|
416 |
+
}
|
417 |
+
const startAt = hideFirstIndent ? 1 : 0;
|
418 |
+
const backgrounds: string[] = [];
|
419 |
+
|
420 |
+
if (active !== undefined) {
|
421 |
+
const markersBeforeActive = active - startAt - 1;
|
422 |
+
if (markersBeforeActive > 0) {
|
423 |
+
backgrounds.push(
|
424 |
+
createGradient('--indent-marker-bg-color', indentWidth, startAt, markersBeforeActive)
|
425 |
+
);
|
426 |
+
}
|
427 |
+
backgrounds.push(createGradient('--indent-marker-active-bg-color', indentWidth, active - 1, 1));
|
428 |
+
if (active !== level) {
|
429 |
+
backgrounds.push(
|
430 |
+
createGradient('--indent-marker-bg-color', indentWidth, active, level - active)
|
431 |
+
);
|
432 |
+
}
|
433 |
+
} else {
|
434 |
+
backgrounds.push(
|
435 |
+
createGradient('--indent-marker-bg-color', indentWidth, startAt, level - startAt)
|
436 |
+
);
|
437 |
+
}
|
438 |
+
|
439 |
+
return backgrounds.join(',');
|
440 |
+
}
|
441 |
+
|
442 |
+
interface IndentationMarkerConfiguration {
|
443 |
+
/**
|
444 |
+
* Determines whether active block marker is styled differently.
|
445 |
+
*/
|
446 |
+
highlightActiveBlock?: boolean;
|
447 |
+
|
448 |
+
/**
|
449 |
+
* Determines whether markers in the first column are omitted.
|
450 |
+
*/
|
451 |
+
hideFirstIndent?: boolean;
|
452 |
+
}
|
453 |
+
|
454 |
+
export const indentationMarkerConfig = Facet.define<
|
455 |
+
IndentationMarkerConfiguration,
|
456 |
+
Required<IndentationMarkerConfiguration>
|
457 |
+
>({
|
458 |
+
combine(configs) {
|
459 |
+
return combineConfig(configs, {
|
460 |
+
highlightActiveBlock: true,
|
461 |
+
hideFirstIndent: false
|
462 |
+
});
|
463 |
+
}
|
464 |
+
});
|
465 |
+
|
466 |
+
class IndentMarkersClass implements PluginValue {
|
467 |
+
view: EditorView;
|
468 |
+
decorations!: DecorationSet;
|
469 |
+
|
470 |
+
private unitWidth: number;
|
471 |
+
private currentLineNumber: number;
|
472 |
+
|
473 |
+
constructor(view: EditorView) {
|
474 |
+
this.view = view;
|
475 |
+
this.unitWidth = getIndentUnit(view.state);
|
476 |
+
this.currentLineNumber = getCurrentLine(view.state).number;
|
477 |
+
this.generate(view.state);
|
478 |
+
}
|
479 |
+
|
480 |
+
update(update: ViewUpdate) {
|
481 |
+
const unitWidth = getIndentUnit(update.state);
|
482 |
+
const unitWidthChanged = unitWidth !== this.unitWidth;
|
483 |
+
if (unitWidthChanged) {
|
484 |
+
this.unitWidth = unitWidth;
|
485 |
+
}
|
486 |
+
const lineNumber = getCurrentLine(update.state).number;
|
487 |
+
const lineNumberChanged = lineNumber !== this.currentLineNumber;
|
488 |
+
this.currentLineNumber = lineNumber;
|
489 |
+
const activeBlockUpdateRequired =
|
490 |
+
update.state.facet(indentationMarkerConfig).highlightActiveBlock && lineNumberChanged;
|
491 |
+
if (
|
492 |
+
update.docChanged ||
|
493 |
+
update.viewportChanged ||
|
494 |
+
unitWidthChanged ||
|
495 |
+
activeBlockUpdateRequired
|
496 |
+
) {
|
497 |
+
this.generate(update.state);
|
498 |
+
}
|
499 |
+
}
|
500 |
+
|
501 |
+
private generate(state: EditorState) {
|
502 |
+
const builder = new RangeSetBuilder<Decoration>();
|
503 |
+
|
504 |
+
const lines = getVisibleLines(this.view, state);
|
505 |
+
const map = new IndentationMap(lines, state, this.unitWidth);
|
506 |
+
const { hideFirstIndent } = state.facet(indentationMarkerConfig);
|
507 |
+
|
508 |
+
for (const line of lines) {
|
509 |
+
const entry = map.get(line.number);
|
510 |
+
|
511 |
+
if (!entry?.level) {
|
512 |
+
continue;
|
513 |
+
}
|
514 |
+
|
515 |
+
const backgrounds = makeBackgroundCSS(entry, this.unitWidth, hideFirstIndent);
|
516 |
+
|
517 |
+
builder.add(
|
518 |
+
line.from,
|
519 |
+
line.from,
|
520 |
+
Decoration.line({
|
521 |
+
class: 'cm-indent-markers',
|
522 |
+
attributes: {
|
523 |
+
style: `--indent-markers: ${backgrounds}`
|
524 |
+
}
|
525 |
+
})
|
526 |
+
);
|
527 |
+
}
|
528 |
+
|
529 |
+
this.decorations = builder.finish();
|
530 |
+
}
|
531 |
+
}
|
532 |
+
|
533 |
+
export function indentationMarkers(config: IndentationMarkerConfiguration = {}): Extension {
|
534 |
+
return [
|
535 |
+
indentationMarkerConfig.of(config),
|
536 |
+
indentTheme,
|
537 |
+
ViewPlugin.fromClass(IndentMarkersClass, {
|
538 |
+
decorations: (v) => v.decorations
|
539 |
+
})
|
540 |
+
];
|
541 |
+
}
|
src/lib/CodeMirrorSearch/CodeMirrorSearch.svelte
ADDED
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!-- references are:
|
2 |
+
https://github.com/codemirror/search
|
3 |
+
https://codemirror.net/docs/ref/#search
|
4 |
+
-->
|
5 |
+
<script lang="ts">
|
6 |
+
import type { EditorView } from '@codemirror/view';
|
7 |
+
|
8 |
+
import { onMount, createEventDispatcher } from 'svelte';
|
9 |
+
import {
|
10 |
+
SearchQuery,
|
11 |
+
findPrevious,
|
12 |
+
findNext,
|
13 |
+
setSearchQuery,
|
14 |
+
replaceNext,
|
15 |
+
replaceAll
|
16 |
+
} from '@codemirror/search';
|
17 |
+
|
18 |
+
import IconCaretV2 from '../Icons/IconCaretV2.svelte';
|
19 |
+
import IconArrowLeft from '../Icons/IconArrowLeft.svelte';
|
20 |
+
import IconCross from '../Icons/IconCross.svelte';
|
21 |
+
import IconReplace from '../Icons/IconReplace.svelte';
|
22 |
+
import IconReplaceAll from '../Icons/IconReplaceAll.svelte';
|
23 |
+
|
24 |
+
export let view: EditorView;
|
25 |
+
|
26 |
+
let el: HTMLDivElement;
|
27 |
+
let searchTxtEl: HTMLInputElement;
|
28 |
+
let searchTxt = '';
|
29 |
+
let replaceTxt = '';
|
30 |
+
let isCaseSensitive = false;
|
31 |
+
let isRegexp = false;
|
32 |
+
let isWholeWord = false;
|
33 |
+
let isReplacePanelOpen = false;
|
34 |
+
|
35 |
+
const dispatch = createEventDispatcher<{ close: void }>();
|
36 |
+
|
37 |
+
$: query = new SearchQuery({
|
38 |
+
search: searchTxt,
|
39 |
+
caseSensitive: isCaseSensitive,
|
40 |
+
wholeWord: isWholeWord,
|
41 |
+
regexp: isRegexp,
|
42 |
+
replace: replaceTxt
|
43 |
+
});
|
44 |
+
|
45 |
+
$: query, search();
|
46 |
+
|
47 |
+
// positionSearchPanel is no longer needed since we use CSS for top-right positioning.
|
48 |
+
|
49 |
+
function destroyDefaultPanel() {
|
50 |
+
// hack to prevent default CodeMirror search box appearing
|
51 |
+
// when you use {findNext, etc...} from @codemirror/search, it tries to automatically create default CodeMirror search box
|
52 |
+
const el = document.querySelector('.codemirror-wrapper .cm-search');
|
53 |
+
el?.parentElement?.removeChild(el);
|
54 |
+
}
|
55 |
+
|
56 |
+
function getSelectedText(editorView: EditorView) {
|
57 |
+
const state = editorView.state;
|
58 |
+
const selection = state.selection;
|
59 |
+
const selectedText = selection.ranges
|
60 |
+
.map((range) => state.doc.sliceString(range.from, range.to))
|
61 |
+
.join('\n');
|
62 |
+
return selectedText;
|
63 |
+
}
|
64 |
+
|
65 |
+
function search() {
|
66 |
+
destroyDefaultPanel();
|
67 |
+
view?.dispatch({ effects: setSearchQuery.of(query) });
|
68 |
+
if (searchTxt && view) {
|
69 |
+
findPrevious(view);
|
70 |
+
findNext(view);
|
71 |
+
} else {
|
72 |
+
reset();
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
function reset() {
|
77 |
+
// send and empty query
|
78 |
+
view?.dispatch({
|
79 |
+
effects: setSearchQuery.of(
|
80 |
+
new SearchQuery({
|
81 |
+
search: ''
|
82 |
+
})
|
83 |
+
)
|
84 |
+
});
|
85 |
+
}
|
86 |
+
|
87 |
+
function onKeyDownWindow(e: KeyboardEvent) {
|
88 |
+
const { ctrlKey, metaKey, key, shiftKey } = e;
|
89 |
+
const cmdKey = metaKey || ctrlKey;
|
90 |
+
const isOpenShortcut = key === 'f3' || (cmdKey && key === 'f');
|
91 |
+
const isNextOrPrevShortcut = cmdKey && key === 'g';
|
92 |
+
const isCloseShortcut = key === 'Escape' || key === 'Esc';
|
93 |
+
if (isOpenShortcut) {
|
94 |
+
e.preventDefault();
|
95 |
+
searchTxt = getSelectedText(view);
|
96 |
+
searchTxtEl.focus();
|
97 |
+
} else if (isNextOrPrevShortcut) {
|
98 |
+
e.preventDefault();
|
99 |
+
shiftKey ? findPrevious(view) : findNext(view);
|
100 |
+
} else if (isCloseShortcut) {
|
101 |
+
dispatch('close');
|
102 |
+
}
|
103 |
+
}
|
104 |
+
|
105 |
+
function onKeyDownEl(e: KeyboardEvent) {
|
106 |
+
const { key } = e;
|
107 |
+
const isNextShortcut = key === 'Enter';
|
108 |
+
if (isNextShortcut) {
|
109 |
+
e.preventDefault();
|
110 |
+
findNext(view);
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
onMount(() => {
|
115 |
+
searchTxt = getSelectedText(view);
|
116 |
+
searchTxtEl.focus();
|
117 |
+
|
118 |
+
// Switch from absolute to fixed and preserve position
|
119 |
+
if (el) {
|
120 |
+
const rect = el.getBoundingClientRect();
|
121 |
+
el.style.position = 'fixed';
|
122 |
+
el.style.top = rect.top + 'px';
|
123 |
+
el.style.left = rect.left + 'px';
|
124 |
+
el.style.right = 'auto'; // Remove right if present
|
125 |
+
el.classList.remove('absolute');
|
126 |
+
el.classList.add('fixed');
|
127 |
+
}
|
128 |
+
|
129 |
+
return reset;
|
130 |
+
});
|
131 |
+
</script>
|
132 |
+
|
133 |
+
<svelte:window on:keydown={onKeyDownWindow} />
|
134 |
+
|
135 |
+
<div
|
136 |
+
bind:this={el}
|
137 |
+
class="absolute top-0 right-0 z-20 rounded-sm border border-gray-500 bg-white dark:bg-gray-900 dark:text-white"
|
138 |
+
on:keydown={onKeyDownEl}
|
139 |
+
>
|
140 |
+
<div class="flex border-b border-gray-500">
|
141 |
+
<button
|
142 |
+
type="button"
|
143 |
+
class="border-r border-gray-500 px-0.5"
|
144 |
+
on:click={() => (isReplacePanelOpen = !isReplacePanelOpen)}
|
145 |
+
>
|
146 |
+
<IconCaretV2 classNames="h-full {isReplacePanelOpen ? '' : '-rotate-90'}" />
|
147 |
+
</button>
|
148 |
+
<div class="my-1">
|
149 |
+
<div class="flex items-center">
|
150 |
+
<div class="flex w-[250px] items-center bg-gray-100 dark:bg-gray-800">
|
151 |
+
<input
|
152 |
+
type="text"
|
153 |
+
class="w-full border-0 bg-transparent py-1 pr-[4.3rem] pl-2 text-sm"
|
154 |
+
bind:value={searchTxt}
|
155 |
+
bind:this={searchTxtEl}
|
156 |
+
/>
|
157 |
+
<!-- buttons: case sensetive, full word, regex -->
|
158 |
+
<div
|
159 |
+
class="-ml-[4.3rem] flex w-16 items-center justify-between gap-0.5 font-mono select-none"
|
160 |
+
>
|
161 |
+
<button
|
162 |
+
type="button"
|
163 |
+
title="Match Case"
|
164 |
+
class="rounded-sm px-0.5 text-sm {isCaseSensitive ? 'bg-black text-white' : ''}"
|
165 |
+
on:click={() => (isCaseSensitive = !isCaseSensitive)}
|
166 |
+
>
|
167 |
+
Aa
|
168 |
+
</button>
|
169 |
+
<button
|
170 |
+
type="button"
|
171 |
+
title="Match Whole Word"
|
172 |
+
class="rounded-sm px-0.5 text-sm underline {isWholeWord ? 'bg-black text-white' : ''}"
|
173 |
+
on:click={() => (isWholeWord = !isWholeWord)}
|
174 |
+
>
|
175 |
+
ab
|
176 |
+
</button>
|
177 |
+
<button
|
178 |
+
type="button"
|
179 |
+
title="Use Regular Expression"
|
180 |
+
class="rounded-sm px-0.5 text-sm {isRegexp ? 'bg-black text-white' : ''}"
|
181 |
+
on:click={() => (isRegexp = !isRegexp)}
|
182 |
+
>
|
183 |
+
re
|
184 |
+
</button>
|
185 |
+
</div>
|
186 |
+
</div>
|
187 |
+
<!-- buttons: find next, find previous, close search panel -->
|
188 |
+
<div class="mx-2 flex items-center gap-0.5 select-none">
|
189 |
+
<button type="button" title="Next Match" on:click={() => findNext(view)}>
|
190 |
+
<IconArrowLeft classNames="-rotate-90" />
|
191 |
+
</button>
|
192 |
+
<button type="button" title="Previous Match" on:click={() => findPrevious(view)}>
|
193 |
+
<IconArrowLeft classNames="rotate-90" />
|
194 |
+
</button>
|
195 |
+
<button type="button" title="Close" on:click={() => dispatch('close')}>
|
196 |
+
<IconCross />
|
197 |
+
</button>
|
198 |
+
</div>
|
199 |
+
</div>
|
200 |
+
{#if isReplacePanelOpen}
|
201 |
+
<div class="mt-1 flex items-center">
|
202 |
+
<input
|
203 |
+
type="text"
|
204 |
+
bind:value={replaceTxt}
|
205 |
+
class="w-[250px] border-0 bg-gray-100 py-1 pl-2 text-sm dark:bg-gray-800"
|
206 |
+
/>
|
207 |
+
<div class="ml-1 flex items-center gap-0.5 select-none">
|
208 |
+
<button type="button" title="Replace" on:click={() => replaceNext(view)}>
|
209 |
+
<IconReplace classNames="text-base" />
|
210 |
+
</button>
|
211 |
+
<button type="button" title="Replace All" on:click={() => replaceAll(view)}>
|
212 |
+
<IconReplaceAll classNames="text-base" />
|
213 |
+
</button>
|
214 |
+
</div>
|
215 |
+
</div>
|
216 |
+
{/if}
|
217 |
+
</div>
|
218 |
+
</div>
|
219 |
+
</div>
|
src/lib/CopyButton/CopyButton.svelte
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import type { SvelteComponent } from 'svelte';
|
3 |
+
|
4 |
+
import { onDestroy } from 'svelte';
|
5 |
+
|
6 |
+
import IconCopy from '../Icons/IconCopy.svelte';
|
7 |
+
import { tooltip } from '../utils/tooltip';
|
8 |
+
|
9 |
+
export const hydrate = true;
|
10 |
+
|
11 |
+
export let classNames = '';
|
12 |
+
export let label = '';
|
13 |
+
export let noIcon = false;
|
14 |
+
export let icon: typeof SvelteComponent | undefined = undefined;
|
15 |
+
export let style: 'blank' | 'button' | 'button-clear' | 'text' = 'text';
|
16 |
+
export let title = '';
|
17 |
+
export let value: string;
|
18 |
+
export let successType: 'tooltip' | 'text' = 'tooltip';
|
19 |
+
export let successText: string = 'Copied';
|
20 |
+
|
21 |
+
let isSuccess = false;
|
22 |
+
let timeout: any;
|
23 |
+
|
24 |
+
onDestroy(() => {
|
25 |
+
if (timeout) {
|
26 |
+
clearTimeout(timeout);
|
27 |
+
}
|
28 |
+
});
|
29 |
+
|
30 |
+
function handleClick() {
|
31 |
+
copyToClipboard(value);
|
32 |
+
isSuccess = true;
|
33 |
+
if (timeout) {
|
34 |
+
clearTimeout(timeout);
|
35 |
+
}
|
36 |
+
timeout = setTimeout(() => {
|
37 |
+
isSuccess = false;
|
38 |
+
}, 1000);
|
39 |
+
}
|
40 |
+
|
41 |
+
function copyToClipboard(value: string): void {
|
42 |
+
const textArea = document.createElement('textarea');
|
43 |
+
document.body.appendChild(textArea);
|
44 |
+
textArea.value = value;
|
45 |
+
textArea.select();
|
46 |
+
document.execCommand('copy');
|
47 |
+
document.body.removeChild(textArea);
|
48 |
+
}
|
49 |
+
</script>
|
50 |
+
|
51 |
+
{#key isSuccess}
|
52 |
+
<button
|
53 |
+
class="border-gray-500 {classNames}
|
54 |
+
{style !== 'blank' ? 'inline-flex cursor-pointer items-center text-sm focus:outline-hidden' : ''}
|
55 |
+
{['button', 'button-clear'].includes(style) ? 'bg-white dark:bg-gray-900' : ''}
|
56 |
+
{style === 'text' ? 'mx-0.5' : ''}
|
57 |
+
{style === 'button' ? 'btn' : ''}
|
58 |
+
{style === 'button-clear' ? 'rounded-md border p-1 shadow-xs' : ''}
|
59 |
+
{!isSuccess && ['button-clear', 'text'].includes(style) ? 'text-gray-600' : ''}
|
60 |
+
{isSuccess && style !== 'blank' ? 'text-green-500' : ''}
|
61 |
+
"
|
62 |
+
on:click|preventDefault|stopPropagation={handleClick}
|
63 |
+
title={title || label || 'Copy to clipboard'}
|
64 |
+
type="button"
|
65 |
+
use:tooltip={{
|
66 |
+
content: successText,
|
67 |
+
disabled: successType !== 'tooltip' || !isSuccess,
|
68 |
+
showOn: 'always',
|
69 |
+
opts: { placement: 'bottom' }
|
70 |
+
}}
|
71 |
+
>
|
72 |
+
{#if !noIcon}
|
73 |
+
<svelte:component this={icon ?? IconCopy} />
|
74 |
+
{/if}
|
75 |
+
{#if label}
|
76 |
+
{#if isSuccess && successType === 'text'}
|
77 |
+
<span class="ml-1.5">{successText}</span>
|
78 |
+
{:else}
|
79 |
+
<span class="ml-1.5 {style === 'text' ? 'underline' : ''}">
|
80 |
+
{label}
|
81 |
+
</span>
|
82 |
+
{/if}
|
83 |
+
{/if}
|
84 |
+
</button>
|
85 |
+
{/key}
|
src/lib/Icons/IconArrowLeft.svelte
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = '';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
xmlns="http://www.w3.org/2000/svg"
|
8 |
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
9 |
+
aria-hidden="true"
|
10 |
+
focusable="false"
|
11 |
+
role="img"
|
12 |
+
width="1em"
|
13 |
+
height="1em"
|
14 |
+
preserveAspectRatio="xMidYMid meet"
|
15 |
+
viewBox="0 0 32 32"
|
16 |
+
>
|
17 |
+
<path d="M14 26l1.41-1.41L7.83 17H28v-2H7.83l7.58-7.59L14 6L4 16l10 10z" fill="currentColor" />
|
18 |
+
</svg>
|
src/lib/Icons/IconCaretV2.svelte
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = '';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
width="1em"
|
8 |
+
height="1em"
|
9 |
+
viewBox="0 0 12 7"
|
10 |
+
fill="none"
|
11 |
+
xmlns="http://www.w3.org/2000/svg"
|
12 |
+
>
|
13 |
+
<path d="M1 1L6 6L11 1" stroke="currentColor" />
|
14 |
+
</svg>
|
src/lib/Icons/IconCodeGeneration.svelte
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = '';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
xmlns="http://www.w3.org/2000/svg"
|
8 |
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
9 |
+
aria-hidden="true"
|
10 |
+
focusable="false"
|
11 |
+
role="img"
|
12 |
+
width="1em"
|
13 |
+
height="1em"
|
14 |
+
preserveAspectRatio="xMidYMid meet"
|
15 |
+
viewBox="0 0 32 32"
|
16 |
+
>
|
17 |
+
<path
|
18 |
+
fill-rule="evenodd"
|
19 |
+
clip-rule="evenodd"
|
20 |
+
d="M24.4 16.83c-.4-1.74-2.9-1.73-3.28.02a5.58 5.58 0 0 1-4.43 4.4c-1.82.31-1.82 2.92 0 3.24a5.55 5.55 0 0 1 4.43 4.4c.36 1.68 2.9 1.7 3.27.01a5.6 5.6 0 0 1 4.46-4.4c1.75-.31 1.75-2.95 0-3.26a5.68 5.68 0 0 1-4.46-4.4Z"
|
21 |
+
fill="currentColor"
|
22 |
+
fill-opacity=".5"
|
23 |
+
/><path
|
24 |
+
fill-rule="evenodd"
|
25 |
+
clip-rule="evenodd"
|
26 |
+
d="M7.06 7.94a1 1 0 0 1 1.42 0l3.44 3.44a1 1 0 0 1 0 1.42l-3.44 3.44a1 1 0 0 1-1.42-1.42L9.8 12.1 7.06 9.36a1 1 0 0 1 0-1.42Zm5.73 7.59a1 1 0 0 1 1-1h4.3a1 1 0 1 1 0 2h-4.3a1 1 0 0 1-1-1Z"
|
27 |
+
fill="currentColor"
|
28 |
+
fill-opacity=".8"
|
29 |
+
/><path
|
30 |
+
d="M5.83 3.84h14.2a2 2 0 0 1 2 2v7.73c.66-.1 1.36-.02 2 .23V5.84a4 4 0 0 0-4-4H5.82a4 4 0 0 0-4 4v13.32a4 4 0 0 0 4 4h7.35c-.06-.69.07-1.39.39-2H5.83a2 2 0 0 1-2-2V5.84c0-1.1.9-2 2-2Z"
|
31 |
+
fill="currentColor"
|
32 |
+
fill-opacity=".8"
|
33 |
+
/>
|
34 |
+
</svg>
|
src/lib/Icons/IconCopy.svelte
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = '';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
xmlns="http://www.w3.org/2000/svg"
|
8 |
+
aria-hidden="true"
|
9 |
+
fill="currentColor"
|
10 |
+
focusable="false"
|
11 |
+
role="img"
|
12 |
+
width="1em"
|
13 |
+
height="1em"
|
14 |
+
preserveAspectRatio="xMidYMid meet"
|
15 |
+
viewBox="0 0 32 32"
|
16 |
+
>
|
17 |
+
<path
|
18 |
+
d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z"
|
19 |
+
transform="translate(0)"
|
20 |
+
/>
|
21 |
+
<path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)" /><rect
|
22 |
+
fill="none"
|
23 |
+
width="32"
|
24 |
+
height="32"
|
25 |
+
/>
|
26 |
+
</svg>
|
src/lib/Icons/IconCross.svelte
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = '';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
xmlns="http://www.w3.org/2000/svg"
|
8 |
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
9 |
+
aria-hidden="true"
|
10 |
+
focusable="false"
|
11 |
+
role="img"
|
12 |
+
width="1.1em"
|
13 |
+
height="1.1em"
|
14 |
+
preserveAspectRatio="xMidYMid meet"
|
15 |
+
viewBox="0 0 32 32"
|
16 |
+
>
|
17 |
+
<path
|
18 |
+
d="M24 9.4L22.6 8L16 14.6L9.4 8L8 9.4l6.6 6.6L8 22.6L9.4 24l6.6-6.6l6.6 6.6l1.4-1.4l-6.6-6.6L24 9.4z"
|
19 |
+
fill="currentColor"
|
20 |
+
/>
|
21 |
+
</svg>
|
src/lib/Icons/IconLineWrap.svelte
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = '';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
width="1em"
|
8 |
+
height="1em"
|
9 |
+
viewBox="0 0 12 11"
|
10 |
+
fill="none"
|
11 |
+
xmlns="http://www.w3.org/2000/svg"
|
12 |
+
>
|
13 |
+
<path
|
14 |
+
d="M0.75 1.25H11.25M0.75 5H9C9.75 5 11.25 5.375 11.25 6.875C11.25 8.375 9.99975 8.75 9.375 8.75H6M6 8.75L7.5 7.25M6 8.75L7.5 10.25M0.75 8.75H3.75"
|
15 |
+
stroke="currentColor"
|
16 |
+
stroke-width="1.125"
|
17 |
+
stroke-linecap="round"
|
18 |
+
stroke-linejoin="round"
|
19 |
+
/>
|
20 |
+
</svg>
|
src/lib/Icons/IconReplace.svelte
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = '';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
viewBox="0 0 16 16"
|
8 |
+
height="1em"
|
9 |
+
width="1em"
|
10 |
+
preserveAspectRatio="xMidYMid meet"
|
11 |
+
><path
|
12 |
+
fill="currentColor"
|
13 |
+
fill-rule="evenodd"
|
14 |
+
d="m3.221 3.739l2.261 2.269L7.7 3.784l-.7-.7l-1.012 1.007l-.008-1.6a.523.523 0 0 1 .5-.526H8V1H6.48A1.482 1.482 0 0 0 5 2.489V4.1L3.927 3.033l-.706.706zm6.67 1.794h.01c.183.311.451.467.806.467c.393 0 .706-.168.94-.503c.236-.335.353-.78.353-1.333c0-.511-.1-.913-.301-1.207c-.201-.295-.488-.442-.86-.442c-.405 0-.718.194-.938.581h-.01V1H9v4.919h.89v-.386zm-.015-1.061v-.34c0-.248.058-.448.175-.601a.54.54 0 0 1 .445-.23a.49.49 0 0 1 .436.233c.104.154.155.368.155.643c0 .33-.056.587-.169.768a.524.524 0 0 1-.47.27a.495.495 0 0 1-.411-.211a.853.853 0 0 1-.16-.532zM9 12.769c-.256.154-.625.231-1.108.231c-.563 0-1.02-.178-1.369-.533c-.349-.355-.523-.813-.523-1.374c0-.648.186-1.158.56-1.53c.374-.376.875-.563 1.5-.563c.433 0 .746.06.94.179v.998a1.26 1.26 0 0 0-.792-.276c-.325 0-.583.1-.774.298c-.19.196-.283.468-.283.816c0 .338.09.603.272.797c.182.191.431.287.749.287c.282 0 .558-.092.828-.276v.946zM4 7L3 8v6l1 1h7l1-1V8l-1-1H4zm0 1h7v6H4V8z"
|
15 |
+
clip-rule="evenodd"
|
16 |
+
/></svg
|
17 |
+
>
|
src/lib/Icons/IconReplaceAll.svelte
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = '';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
viewBox="0 0 16 16"
|
8 |
+
height="1em"
|
9 |
+
width="1em"
|
10 |
+
preserveAspectRatio="xMidYMid meet"
|
11 |
+
><path
|
12 |
+
fill="currentColor"
|
13 |
+
fill-rule="evenodd"
|
14 |
+
d="M11.6 2.677c.147-.31.356-.465.626-.465c.248 0 .44.118.573.353c.134.236.201.557.201.966c0 .443-.078.798-.235 1.067c-.156.268-.365.402-.627.402c-.237 0-.416-.125-.537-.374h-.008v.31H11V1h.593v1.677h.008zm-.016 1.1a.78.78 0 0 0 .107.426c.071.113.163.169.274.169c.136 0 .24-.072.314-.216c.075-.145.113-.35.113-.615c0-.22-.035-.39-.104-.514c-.067-.124-.164-.187-.29-.187c-.12 0-.219.062-.297.185a.886.886 0 0 0-.117.48v.272zM4.12 7.695L2 5.568l.662-.662l1.006 1v-1.51A1.39 1.39 0 0 1 5.055 3H7.4v.905H5.055a.49.49 0 0 0-.468.493l.007 1.5l.949-.944l.656.656l-2.08 2.085zM9.356 4.93H10V3.22C10 2.408 9.685 2 9.056 2c-.135 0-.285.024-.45.073a1.444 1.444 0 0 0-.388.167v.665c.237-.203.487-.304.75-.304c.261 0 .392.156.392.469l-.6.103c-.506.086-.76.406-.76.961c0 .263.061.473.183.631A.61.61 0 0 0 8.69 5c.29 0 .509-.16.657-.48h.009v.41zm.004-1.355v.193a.75.75 0 0 1-.12.436a.368.368 0 0 1-.313.17a.276.276 0 0 1-.22-.095a.38.38 0 0 1-.08-.248c0-.222.11-.351.332-.389l.4-.067zM7 12.93h-.644v-.41h-.009c-.148.32-.367.48-.657.48a.61.61 0 0 1-.507-.235c-.122-.158-.183-.368-.183-.63c0-.556.254-.876.76-.962l.6-.103c0-.313-.13-.47-.392-.47c-.263 0-.513.102-.75.305v-.665c.095-.063.224-.119.388-.167c.165-.049.315-.073.45-.073c.63 0 .944.407.944 1.22v1.71zm-.64-1.162v-.193l-.4.068c-.222.037-.333.166-.333.388c0 .1.027.183.08.248a.276.276 0 0 0 .22.095a.368.368 0 0 0 .312-.17c.08-.116.12-.26.12-.436zM9.262 13c.321 0 .568-.058.738-.173v-.71a.9.9 0 0 1-.552.207a.619.619 0 0 1-.5-.215c-.12-.145-.181-.345-.181-.598c0-.26.063-.464.189-.612a.644.644 0 0 1 .516-.223c.194 0 .37.069.528.207v-.749c-.129-.09-.338-.134-.626-.134c-.417 0-.751.14-1.001.422c-.249.28-.373.662-.373 1.148c0 .42.116.764.349 1.03c.232.267.537.4.913.4zM2 9l1-1h9l1 1v5l-1 1H3l-1-1V9zm1 0v5h9V9H3zm3-2l1-1h7l1 1v5l-1 1V7H6z"
|
15 |
+
clip-rule="evenodd"
|
16 |
+
/></svg
|
17 |
+
>
|
src/lib/Icons/IconRestart.svelte
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = '';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
xmlns="http://www.w3.org/2000/svg"
|
8 |
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
9 |
+
aria-hidden="true"
|
10 |
+
role="img"
|
11 |
+
width="1em"
|
12 |
+
height="1em"
|
13 |
+
preserveAspectRatio="xMidYMid meet"
|
14 |
+
viewBox="0 0 12 12"
|
15 |
+
>
|
16 |
+
<path
|
17 |
+
d="M9.21866 6.82043C9.21866 7.4695 9.02989 8.10399 8.67622 8.64367C8.32255 9.18334 7.81986 9.60397 7.23173 9.85236C6.6436 10.1007 5.99643 10.1657 5.37207 10.0391C4.74771 9.91248 4.1742 9.59993 3.72406 9.14097C3.27393 8.68201 2.96738 8.09726 2.84319 7.46067C2.71899 6.82407 2.78273 6.16423 3.02635 5.56457C3.26996 4.96491 3.6825 4.45237 4.21181 4.09177C4.74112 3.73117 5.36341 3.5387 6 3.5387H7.98978L6.83621 4.7152L7.28746 5.17957L9.21866 3.21053L7.28746 1.24149L6.83621 1.70552L7.99074 2.88235H6C5.23609 2.88235 4.48934 3.11332 3.85417 3.54604C3.219 3.97876 2.72395 4.5938 2.43162 5.31339C2.13928 6.03298 2.06279 6.8248 2.21182 7.58871C2.36086 8.35263 2.72871 9.05433 3.26888 9.60508C3.80904 10.1558 4.49726 10.5309 5.24649 10.6828C5.99572 10.8348 6.77231 10.7568 7.47807 10.4587C8.18383 10.1607 8.78706 9.65593 9.21146 9.00831C9.63587 8.3607 9.86239 7.59931 9.86239 6.82043H9.21866Z"
|
18 |
+
fill="currentColor"
|
19 |
+
stroke="currentColor"
|
20 |
+
stroke-width="0.5"
|
21 |
+
/>
|
22 |
+
</svg>
|
src/lib/Icons/IconSpin.svelte
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = '';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
xmlns="http://www.w3.org/2000/svg"
|
8 |
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
9 |
+
aria-hidden="true"
|
10 |
+
fill="none"
|
11 |
+
focusable="false"
|
12 |
+
role="img"
|
13 |
+
width="1em"
|
14 |
+
height="1em"
|
15 |
+
preserveAspectRatio="xMidYMid meet"
|
16 |
+
viewBox="0 0 12 12"
|
17 |
+
>
|
18 |
+
<path
|
19 |
+
class="opacity-75"
|
20 |
+
fill-rule="evenodd"
|
21 |
+
clip-rule="evenodd"
|
22 |
+
d="M6 0C2.6862 0 0 2.6862 0 6H1.8C1.8 4.88609 2.2425 3.8178 3.03015 3.03015C3.8178 2.2425 4.88609 1.8 6 1.8V0ZM12 6C12 9.3138 9.3138 12 6 12V10.2C7.11391 10.2 8.1822 9.7575 8.96985 8.96985C9.7575 8.1822 10.2 7.11391 10.2 6H12Z"
|
23 |
+
fill="currentColor"
|
24 |
+
/>
|
25 |
+
<path
|
26 |
+
class="opacity-25"
|
27 |
+
fill-rule="evenodd"
|
28 |
+
clip-rule="evenodd"
|
29 |
+
d="M3.03015 8.96985C3.8178 9.7575 4.88609 10.2 6 10.2V12C2.6862 12 0 9.3138 0 6H1.8C1.8 7.11391 2.2425 8.1822 3.03015 8.96985ZM7.60727 2.11971C7.0977 1.90864 6.55155 1.8 6 1.8V0C9.3138 0 12 2.6862 12 6H10.2C10.2 5.44845 10.0914 4.9023 9.88029 4.39273C9.66922 3.88316 9.35985 3.42016 8.96985 3.03015C8.57984 2.64015 8.11684 2.33078 7.60727 2.11971Z"
|
30 |
+
fill="currentColor"
|
31 |
+
/>
|
32 |
+
</svg>
|
src/lib/JsonEditor/JsonEditor.svelte
ADDED
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import CodeMirror from '$lib/CodeMirror/CodeMirror.svelte';
|
3 |
+
import { EditorView, lineNumbers } from '@codemirror/view';
|
4 |
+
import CopyButton from '$lib/CopyButton/CopyButton.svelte';
|
5 |
+
import { javascript } from '@codemirror/lang-javascript';
|
6 |
+
import { linter, lintGutter, type Diagnostic } from '@codemirror/lint';
|
7 |
+
import LineWrapButton from '$lib/LineWrapButton/LineWrapButton.svelte';
|
8 |
+
import JSON5 from 'json5';
|
9 |
+
import type { FormattedChatTemplate } from '../ChatTemplateViewer/types';
|
10 |
+
import { getExampleHelloWorld } from '$lib/example-inputs/helloWorld';
|
11 |
+
import { onMount } from 'svelte';
|
12 |
+
import { getExampleToolUsage } from '$lib/example-inputs/toolUsage';
|
13 |
+
import { foldGutter } from '@codemirror/language';
|
14 |
+
import { getExampleReasoning } from '$lib/example-inputs/reasoning';
|
15 |
+
import { tooltip } from '$lib/utils/tooltip';
|
16 |
+
import IconRestart from '$lib/Icons/IconRestart.svelte';
|
17 |
+
import { createEventDispatcher } from 'svelte';
|
18 |
+
import { page } from '$app/stores';
|
19 |
+
import { transformInput } from '$lib/utils/transformInput';
|
20 |
+
|
21 |
+
export let content: Record<string, unknown> = {};
|
22 |
+
export let error = '';
|
23 |
+
export let selectedTemplate: FormattedChatTemplate | undefined = undefined;
|
24 |
+
export let selectedExampleInputId = '';
|
25 |
+
|
26 |
+
const dispatch = createEventDispatcher<{ exampleChange: void }>();
|
27 |
+
|
28 |
+
let value = JSON5.stringify(content, null, 2);
|
29 |
+
let wrapLines = true;
|
30 |
+
let exampleInputs: { id: string; label: string; content: Record<string, unknown> }[] = [];
|
31 |
+
let exampleValue = '';
|
32 |
+
|
33 |
+
async function handleUpdateEditor(e: CustomEvent<string>) {
|
34 |
+
const currentCode = e.detail;
|
35 |
+
try {
|
36 |
+
content = JSON5.parse(currentCode);
|
37 |
+
value = currentCode;
|
38 |
+
error = '';
|
39 |
+
} catch (e) {
|
40 |
+
console.error(e);
|
41 |
+
error = 'Error in input JSON';
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
function jsonLinter() {
|
46 |
+
return (view: EditorView): Diagnostic[] => {
|
47 |
+
const diagnostics: Diagnostic[] = [];
|
48 |
+
const text = view.state.doc.toString();
|
49 |
+
|
50 |
+
try {
|
51 |
+
// Attempt to parse the JSON5
|
52 |
+
JSON5.parse(text);
|
53 |
+
} catch (e) {
|
54 |
+
let pos = 0;
|
55 |
+
let errorMessage = '';
|
56 |
+
|
57 |
+
if (e && typeof e === 'object') {
|
58 |
+
errorMessage = e.message || '';
|
59 |
+
// Prefer JSON5 error properties if available
|
60 |
+
const line = e.lineNumber;
|
61 |
+
const column = e.columnNumber;
|
62 |
+
if (typeof line === 'number' && typeof column === 'number') {
|
63 |
+
// Convert line/column to character offset
|
64 |
+
let runningPos = 0;
|
65 |
+
let currentLine = 1;
|
66 |
+
for (let i = 0; i < text.length; i++) {
|
67 |
+
if (currentLine === line && runningPos === column - 1) {
|
68 |
+
pos = i;
|
69 |
+
break;
|
70 |
+
}
|
71 |
+
if (text[i] === '\n') {
|
72 |
+
currentLine++;
|
73 |
+
runningPos = 0;
|
74 |
+
} else {
|
75 |
+
runningPos++;
|
76 |
+
}
|
77 |
+
}
|
78 |
+
} else {
|
79 |
+
// Fallback: try to extract from message
|
80 |
+
const match = /at position (\d+)/.exec(errorMessage);
|
81 |
+
if (match) {
|
82 |
+
pos = parseInt(match[1], 10);
|
83 |
+
} else {
|
84 |
+
const lineMatch = /line (\d+) column (\d+)/.exec(errorMessage);
|
85 |
+
if (lineMatch) {
|
86 |
+
const l = parseInt(lineMatch[1], 10);
|
87 |
+
const c = parseInt(lineMatch[2], 10);
|
88 |
+
let runningPos = 0;
|
89 |
+
let currentLine = 1;
|
90 |
+
for (let i = 0; i < text.length; i++) {
|
91 |
+
if (currentLine === l && runningPos === c - 1) {
|
92 |
+
pos = i;
|
93 |
+
break;
|
94 |
+
}
|
95 |
+
if (text[i] === '\n') {
|
96 |
+
currentLine++;
|
97 |
+
runningPos = 0;
|
98 |
+
} else {
|
99 |
+
runningPos++;
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
}
|
104 |
+
}
|
105 |
+
}
|
106 |
+
diagnostics.push({
|
107 |
+
from: Math.max(0, pos - 1),
|
108 |
+
to: Math.min(text.length, pos + 1),
|
109 |
+
severity: 'error',
|
110 |
+
message: `JSON Error: ${errorMessage}`
|
111 |
+
});
|
112 |
+
}
|
113 |
+
|
114 |
+
return diagnostics;
|
115 |
+
};
|
116 |
+
}
|
117 |
+
|
118 |
+
function handleExampleInputChange(e: Event) {
|
119 |
+
const target = e.target as HTMLSelectElement;
|
120 |
+
const selectedId = target.value;
|
121 |
+
const selectedExampleInput = exampleInputs.find(
|
122 |
+
(exampleInput) => exampleInput.id === selectedId
|
123 |
+
);
|
124 |
+
if (selectedExampleInput) {
|
125 |
+
selectedExampleInputId = selectedId;
|
126 |
+
content = selectedExampleInput.content;
|
127 |
+
value = JSON5.stringify(content, null, 2);
|
128 |
+
exampleValue = value;
|
129 |
+
dispatch('exampleChange');
|
130 |
+
}
|
131 |
+
}
|
132 |
+
|
133 |
+
onMount(() => {
|
134 |
+
if (selectedTemplate) {
|
135 |
+
const exampleHelloWorld = getExampleHelloWorld(selectedTemplate.template);
|
136 |
+
if (exampleHelloWorld) {
|
137 |
+
exampleInputs = [
|
138 |
+
...exampleInputs,
|
139 |
+
{
|
140 |
+
id: 'hello-world',
|
141 |
+
label: 'hello world example',
|
142 |
+
content: transformInput(exampleHelloWorld, selectedTemplate.template)
|
143 |
+
}
|
144 |
+
];
|
145 |
+
}
|
146 |
+
|
147 |
+
const exampleReasoning = getExampleReasoning(selectedTemplate.template);
|
148 |
+
if (exampleReasoning) {
|
149 |
+
exampleInputs = [
|
150 |
+
...exampleInputs,
|
151 |
+
{
|
152 |
+
id: 'reasoning',
|
153 |
+
label: 'reasoning example',
|
154 |
+
content: transformInput(exampleReasoning, selectedTemplate.template)
|
155 |
+
}
|
156 |
+
];
|
157 |
+
}
|
158 |
+
|
159 |
+
const exampleToolUsage = getExampleToolUsage(selectedTemplate.template);
|
160 |
+
if (exampleToolUsage) {
|
161 |
+
exampleInputs = [
|
162 |
+
...exampleInputs,
|
163 |
+
{
|
164 |
+
id: 'tool-usage',
|
165 |
+
label: 'tool usage example',
|
166 |
+
content: transformInput(exampleToolUsage, selectedTemplate.template)
|
167 |
+
}
|
168 |
+
];
|
169 |
+
}
|
170 |
+
|
171 |
+
const exampleFromQuery = $page.url.searchParams.get('example');
|
172 |
+
if (exampleFromQuery) {
|
173 |
+
const exampleInput = exampleInputs.find(
|
174 |
+
(exampleInput) => exampleInput.id === exampleFromQuery
|
175 |
+
);
|
176 |
+
if (exampleInput) {
|
177 |
+
content = exampleInput.content;
|
178 |
+
value = JSON5.stringify(content, null, 2);
|
179 |
+
exampleValue = value;
|
180 |
+
selectedExampleInputId = exampleInput.id;
|
181 |
+
return;
|
182 |
+
}
|
183 |
+
}
|
184 |
+
|
185 |
+
content = exampleInputs.at(-1)?.content ?? {};
|
186 |
+
selectedExampleInputId = exampleInputs.at(-1)?.id ?? '';
|
187 |
+
value = JSON5.stringify(content, null, 2);
|
188 |
+
exampleValue = value;
|
189 |
+
}
|
190 |
+
});
|
191 |
+
</script>
|
192 |
+
|
193 |
+
<div class="h-full overflow-scroll bg-white dark:bg-gray-900">
|
194 |
+
<div class="sticky top-0 z-10 bg-white dark:bg-gray-900">
|
195 |
+
<div
|
196 |
+
class="text-semibold flex items-center gap-x-2 border-b border-gray-500 bg-linear-to-r from-orange-200 to-white px-3 py-1.5 text-lg dark:from-orange-700 dark:to-orange-900 dark:text-gray-200"
|
197 |
+
>
|
198 |
+
JSON Input
|
199 |
+
|
200 |
+
{#if exampleInputs.length > 1}
|
201 |
+
<select
|
202 |
+
class="ml-auto rounded border px-1 py-0.5 text-sm"
|
203 |
+
on:change={handleExampleInputChange}
|
204 |
+
>
|
205 |
+
{#each exampleInputs as exampleInput}
|
206 |
+
<option value={exampleInput.id} selected={exampleInput.id === selectedExampleInputId}
|
207 |
+
>{exampleInput.label}</option
|
208 |
+
>
|
209 |
+
{/each}
|
210 |
+
</select>
|
211 |
+
{/if}
|
212 |
+
</div>
|
213 |
+
<div class="flex items-center border-b px-3 py-2">
|
214 |
+
<div class="ml-auto flex items-center gap-x-2">
|
215 |
+
<!-- reset button -->
|
216 |
+
{#if exampleValue && exampleValue !== value}
|
217 |
+
<button
|
218 |
+
class="relative inline-flex h-6! cursor-pointer items-center justify-center rounded-md border border-gray-500 bg-white p-0! px-1.5! text-sm shadow-xs focus:outline-hidden dark:bg-gray-900 dark:text-white [&_svg]:translate-x-px! [&_svg]:translate-y-px! [&_svg]:text-base!"
|
219 |
+
type="button"
|
220 |
+
on:click={() => {
|
221 |
+
value = exampleValue;
|
222 |
+
}}
|
223 |
+
use:tooltip={'Reset example to original'}
|
224 |
+
><IconRestart classNames="dark:text-gray-200!" />
|
225 |
+
<span class="ml-1 text-sm select-none dark:text-gray-200!"> Reset </span>
|
226 |
+
</button>
|
227 |
+
{/if}
|
228 |
+
|
229 |
+
<CopyButton
|
230 |
+
label="Copy"
|
231 |
+
{value}
|
232 |
+
style="button-clear"
|
233 |
+
classNames="h-6! [&_svg]:text-[0.7rem]! px-1.5! text-black! dark:text-gray-200!"
|
234 |
+
/>
|
235 |
+
|
236 |
+
<LineWrapButton
|
237 |
+
style="button-clear"
|
238 |
+
bind:wrapLines
|
239 |
+
classNames="[&_svg]:text-xs! size-6! p-0!"
|
240 |
+
/>
|
241 |
+
</div>
|
242 |
+
</div>
|
243 |
+
</div>
|
244 |
+
<CodeMirror
|
245 |
+
{value}
|
246 |
+
on:change={handleUpdateEditor}
|
247 |
+
extensions={[
|
248 |
+
lineNumbers(),
|
249 |
+
javascript({ jsx: false, typescript: false }),
|
250 |
+
linter(jsonLinter()),
|
251 |
+
lintGutter(),
|
252 |
+
foldGutter(),
|
253 |
+
...[wrapLines ? [EditorView.lineWrapping] : []]
|
254 |
+
]}
|
255 |
+
/>
|
256 |
+
</div>
|
src/lib/LineWrapButton/LineWrapButton.svelte
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { tooltip } from '$lib/utils/tooltip';
|
3 |
+
import IconLineWrap from '../Icons/IconLineWrap.svelte';
|
4 |
+
|
5 |
+
export const hydrate = true;
|
6 |
+
|
7 |
+
export let classNames = '';
|
8 |
+
export let wrapLines = false;
|
9 |
+
export let style: 'blank' | 'button' | 'button-clear' | 'text' = 'text';
|
10 |
+
|
11 |
+
function toggleWrapLines() {
|
12 |
+
wrapLines = !wrapLines;
|
13 |
+
}
|
14 |
+
|
15 |
+
function onKeyDown(e: KeyboardEvent) {
|
16 |
+
const { key, code, altKey } = e;
|
17 |
+
// support QWERTY & AZERTY (key "Â" for mac os AZERTY)
|
18 |
+
if (((key === 'z' || code === 'KeyZ') && altKey) || key === 'Â') {
|
19 |
+
e.preventDefault();
|
20 |
+
toggleWrapLines();
|
21 |
+
}
|
22 |
+
}
|
23 |
+
</script>
|
24 |
+
|
25 |
+
<svelte:window on:keydown={onKeyDown} />
|
26 |
+
|
27 |
+
{#key wrapLines}
|
28 |
+
<button
|
29 |
+
class="border-gray-500 {classNames}
|
30 |
+
{style !== 'blank'
|
31 |
+
? 'inline-flex cursor-pointer items-center justify-center text-sm focus:outline-hidden'
|
32 |
+
: ''}
|
33 |
+
{['button', 'button-clear'].includes(style) ? 'bg-white dark:bg-gray-900 dark:text-white' : ''}
|
34 |
+
{style === 'text' ? 'mx-0.5' : ''}
|
35 |
+
{style === 'button' ? 'btn' : ''}
|
36 |
+
{style === 'button-clear' ? 'rounded-md border p-1 shadow-xs' : ''}
|
37 |
+
"
|
38 |
+
type="button"
|
39 |
+
on:click={toggleWrapLines}
|
40 |
+
use:tooltip={wrapLines ? 'Unwrap lines' : 'Wrap lines'}
|
41 |
+
><IconLineWrap classNames={wrapLines ? 'opacity-100' : 'opacity-40'} /></button
|
42 |
+
>
|
43 |
+
{/key}
|
src/lib/OutputViewer/OutputViewer.svelte
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import CodeMirror from '$lib/CodeMirror/CodeMirror.svelte';
|
3 |
+
import CopyButton from '$lib/CopyButton/CopyButton.svelte';
|
4 |
+
import LineWrapButton from '$lib/LineWrapButton/LineWrapButton.svelte';
|
5 |
+
import { EditorView } from '@codemirror/view';
|
6 |
+
|
7 |
+
export let content = '';
|
8 |
+
export let error;
|
9 |
+
|
10 |
+
let wrapLines = true;
|
11 |
+
</script>
|
12 |
+
|
13 |
+
<div class="h-full overflow-scroll bg-white dark:bg-gray-900">
|
14 |
+
<div class="sticky top-0 z-10 bg-white dark:bg-gray-900">
|
15 |
+
<div
|
16 |
+
class="text-semibold flex items-center gap-x-2 border-b border-gray-500 bg-linear-to-r from-purple-200 to-white px-3 py-1.5 text-lg dark:from-purple-700 dark:to-purple-900 dark:text-gray-200"
|
17 |
+
>
|
18 |
+
Rendered Output
|
19 |
+
</div>
|
20 |
+
{#if error}
|
21 |
+
<div class="alert alert-error">
|
22 |
+
{error}
|
23 |
+
</div>
|
24 |
+
{:else}
|
25 |
+
<div class="flex items-center border-b px-3 py-2">
|
26 |
+
<div class="ml-auto flex items-center gap-x-2">
|
27 |
+
<CopyButton
|
28 |
+
label="Copy"
|
29 |
+
value={content}
|
30 |
+
style="button-clear"
|
31 |
+
classNames="h-6! [&_svg]:text-[0.7rem]! px-1.5! text-black! dark:text-gray-200!"
|
32 |
+
/>
|
33 |
+
|
34 |
+
<LineWrapButton
|
35 |
+
style="button-clear"
|
36 |
+
bind:wrapLines
|
37 |
+
classNames="[&_svg]:text-xs! size-6! p-0!"
|
38 |
+
/>
|
39 |
+
</div>
|
40 |
+
</div>
|
41 |
+
{/if}
|
42 |
+
</div>
|
43 |
+
{#if !error}
|
44 |
+
<CodeMirror
|
45 |
+
value={content}
|
46 |
+
readonly
|
47 |
+
extensions={[wrapLines ? [EditorView.lineWrapping] : []]}
|
48 |
+
/>
|
49 |
+
{/if}
|
50 |
+
</div>
|
src/lib/example-inputs/helloWorld.ts
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Template } from '@huggingface/jinja';
|
2 |
+
import { transformInput } from '$lib/utils/transformInput';
|
3 |
+
|
4 |
+
const variations = {
|
5 |
+
variation1_with_system_prompt: {
|
6 |
+
description: 'Variation with system prompt',
|
7 |
+
example: {
|
8 |
+
messages: [
|
9 |
+
{
|
10 |
+
role: 'system',
|
11 |
+
content: 'You are a helpful assistant.'
|
12 |
+
},
|
13 |
+
{
|
14 |
+
role: 'user',
|
15 |
+
content: 'Hello, how are you?'
|
16 |
+
},
|
17 |
+
{
|
18 |
+
role: 'assistant',
|
19 |
+
content: "I'm doing great. How can I help you today?"
|
20 |
+
},
|
21 |
+
{
|
22 |
+
role: 'user',
|
23 |
+
content: 'Can you tell me a joke?'
|
24 |
+
}
|
25 |
+
],
|
26 |
+
add_generation_prompt: true
|
27 |
+
}
|
28 |
+
},
|
29 |
+
variation2_without_system_prompt: {
|
30 |
+
description: 'Variation without system prompt',
|
31 |
+
example: {
|
32 |
+
messages: [
|
33 |
+
{
|
34 |
+
role: 'user',
|
35 |
+
content: 'Hello, how are you?'
|
36 |
+
},
|
37 |
+
{
|
38 |
+
role: 'assistant',
|
39 |
+
content: "I'm doing great. How can I help you today?"
|
40 |
+
},
|
41 |
+
{
|
42 |
+
role: 'user',
|
43 |
+
content: 'Can you tell me a joke?'
|
44 |
+
}
|
45 |
+
],
|
46 |
+
add_generation_prompt: true
|
47 |
+
}
|
48 |
+
}
|
49 |
+
};
|
50 |
+
|
51 |
+
export function getExampleHelloWorld(templateStr: string): Record<string, unknown> | undefined {
|
52 |
+
const template = new Template(templateStr);
|
53 |
+
const variationSystemPrompt = variations.variation1_with_system_prompt.example;
|
54 |
+
const variationSystemPromptRendered = template.render(
|
55 |
+
transformInput(variationSystemPrompt, templateStr)
|
56 |
+
);
|
57 |
+
if (variationSystemPromptRendered.includes('You are a helpful assistant.')) {
|
58 |
+
return variations.variation1_with_system_prompt.example;
|
59 |
+
}
|
60 |
+
return variations.variation2_without_system_prompt.example;
|
61 |
+
}
|
src/lib/example-inputs/reasoning.ts
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { transformInput } from '$lib/utils/transformInput';
|
2 |
+
import { Template } from '@huggingface/jinja';
|
3 |
+
|
4 |
+
const variations = {
|
5 |
+
variation1_with_system_prompt: {
|
6 |
+
description: 'Variation with system prompt',
|
7 |
+
example: {
|
8 |
+
messages: [
|
9 |
+
{
|
10 |
+
role: 'system',
|
11 |
+
content: 'You are a helpful assistant.'
|
12 |
+
},
|
13 |
+
{
|
14 |
+
role: 'user',
|
15 |
+
content: 'What is the capital of France?'
|
16 |
+
},
|
17 |
+
{
|
18 |
+
role: 'assistant',
|
19 |
+
content:
|
20 |
+
'<think>The user is asking for the capital of France. This is a factual question. I know this information.</think>The capital of France is Paris.'
|
21 |
+
},
|
22 |
+
{
|
23 |
+
role: 'user',
|
24 |
+
content: 'What about Chile?'
|
25 |
+
}
|
26 |
+
],
|
27 |
+
add_generation_prompt: true
|
28 |
+
}
|
29 |
+
},
|
30 |
+
variation2_without_system_prompt: {
|
31 |
+
description: 'Variation without system prompt',
|
32 |
+
example: {
|
33 |
+
messages: [
|
34 |
+
{
|
35 |
+
role: 'user',
|
36 |
+
content: 'What is the capital of France?'
|
37 |
+
},
|
38 |
+
{
|
39 |
+
role: 'assistant',
|
40 |
+
content:
|
41 |
+
'<think>The user is asking for the capital of France. This is a factual question. I know this information.</think>The capital of France is Paris.'
|
42 |
+
},
|
43 |
+
{
|
44 |
+
role: 'user',
|
45 |
+
content: 'What about Chile?'
|
46 |
+
}
|
47 |
+
],
|
48 |
+
add_generation_prompt: true
|
49 |
+
}
|
50 |
+
}
|
51 |
+
};
|
52 |
+
|
53 |
+
export function getExampleReasoning(templateStr: string): Record<string, unknown> | undefined {
|
54 |
+
if (!templateStr.includes('think>')) {
|
55 |
+
return;
|
56 |
+
}
|
57 |
+
const template = new Template(templateStr);
|
58 |
+
const variationSystemPrompt = variations.variation1_with_system_prompt.example;
|
59 |
+
const variationSystemPromptRendered = template.render(
|
60 |
+
transformInput(variationSystemPrompt, templateStr)
|
61 |
+
);
|
62 |
+
if (variationSystemPromptRendered.includes('You are a helpful assistant.')) {
|
63 |
+
return variations.variation1_with_system_prompt.example;
|
64 |
+
}
|
65 |
+
return variations.variation2_without_system_prompt.example;
|
66 |
+
}
|
src/lib/example-inputs/toolUsage.ts
ADDED
@@ -0,0 +1,396 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { transformInput } from '$lib/utils/transformInput';
|
2 |
+
import { Template } from '@huggingface/jinja';
|
3 |
+
|
4 |
+
const variations = {
|
5 |
+
variation1_qwen_xml_style: {
|
6 |
+
description:
|
7 |
+
"This variation reflects how Qwen-like models might structure tool definitions in the system message using XML-like tags and how tool responses are often wrapped. The assistant's tool invocation uses a standard `tool_calls` array which the template would then format into the model's expected string.",
|
8 |
+
example: {
|
9 |
+
messages: [
|
10 |
+
{
|
11 |
+
role: 'system',
|
12 |
+
content:
|
13 |
+
'You are a helpful assistant that can use tools to get information for the user.\n\n# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>\n{"name": "get_weather", "description": "Get current weather information for a location", "parameters": {"type": "object", "properties": {"location": {"type": "string", "description": "The city and state, e.g. San Francisco, CA"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "The unit of temperature to use"}}, "required": ["location"]}}\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags.'
|
14 |
+
},
|
15 |
+
{
|
16 |
+
role: 'user',
|
17 |
+
content: "What's the weather like in New York?"
|
18 |
+
},
|
19 |
+
{
|
20 |
+
role: 'assistant',
|
21 |
+
content:
|
22 |
+
"<think>\nThe user is asking about the weather in New York. I should use the weather tool to get this information.\n</think>\nI'll check the current weather in New York for you.",
|
23 |
+
tool_calls: [
|
24 |
+
{
|
25 |
+
function: {
|
26 |
+
name: 'get_weather',
|
27 |
+
arguments: {
|
28 |
+
location: 'New York',
|
29 |
+
unit: 'celsius'
|
30 |
+
}
|
31 |
+
}
|
32 |
+
}
|
33 |
+
]
|
34 |
+
},
|
35 |
+
{
|
36 |
+
role: 'user',
|
37 |
+
content:
|
38 |
+
'<tool_response>\n{"temperature": 22, "condition": "Sunny", "humidity": 45, "wind_speed": 10}\n</tool_response>'
|
39 |
+
},
|
40 |
+
{
|
41 |
+
role: 'assistant',
|
42 |
+
content:
|
43 |
+
"The weather in New York is currently sunny with a temperature of 22°C. The humidity is at 45% with a wind speed of 10 km/h. It's a great day to be outside!"
|
44 |
+
},
|
45 |
+
{
|
46 |
+
role: 'user',
|
47 |
+
content: 'Thanks! What about Boston?'
|
48 |
+
}
|
49 |
+
],
|
50 |
+
tools: [
|
51 |
+
{
|
52 |
+
name: 'get_weather',
|
53 |
+
description: 'Get current weather information for a location',
|
54 |
+
parameters: {
|
55 |
+
type: 'object',
|
56 |
+
properties: {
|
57 |
+
location: {
|
58 |
+
type: 'string',
|
59 |
+
description: 'The city and state, e.g. San Francisco, CA'
|
60 |
+
},
|
61 |
+
unit: {
|
62 |
+
type: 'string',
|
63 |
+
enum: ['celsius', 'fahrenheit'],
|
64 |
+
description: 'The unit of temperature to use'
|
65 |
+
}
|
66 |
+
},
|
67 |
+
required: ['location']
|
68 |
+
}
|
69 |
+
}
|
70 |
+
],
|
71 |
+
add_generation_prompt: true
|
72 |
+
}
|
73 |
+
},
|
74 |
+
variation3_deepseek_special_tags_style: {
|
75 |
+
description:
|
76 |
+
'This variation reflects DeepSeek-like models using specialized tags for tool calls. The `tool_calls` array in the assistant message would contain arguments as a JSON string, which the template then formats with specific tags and markdown.',
|
77 |
+
example: {
|
78 |
+
messages: [
|
79 |
+
{
|
80 |
+
role: 'system',
|
81 |
+
content: 'You are a helpful assistant.'
|
82 |
+
},
|
83 |
+
{
|
84 |
+
role: 'user',
|
85 |
+
content: "What's the weather like in New York?"
|
86 |
+
},
|
87 |
+
{
|
88 |
+
role: 'assistant',
|
89 |
+
content:
|
90 |
+
"<think>\nThe user is asking about the weather in New York. I should use the weather tool to get this information.\n</think>\nI'll check the current weather in New York for you.",
|
91 |
+
tool_calls: [
|
92 |
+
{
|
93 |
+
type: 'function',
|
94 |
+
function: {
|
95 |
+
name: 'get_weather',
|
96 |
+
arguments: '{"location": "New York", "unit": "celsius"}'
|
97 |
+
}
|
98 |
+
}
|
99 |
+
]
|
100 |
+
},
|
101 |
+
{
|
102 |
+
role: 'tool',
|
103 |
+
content: '{"temperature": 22, "condition": "Sunny", "humidity": 45, "wind_speed": 10}'
|
104 |
+
},
|
105 |
+
{
|
106 |
+
role: 'assistant',
|
107 |
+
content:
|
108 |
+
"The weather in New York is currently sunny with a temperature of 22°C. The humidity is at 45% with a wind speed of 10 km/h. It's a great day to be outside!"
|
109 |
+
},
|
110 |
+
{
|
111 |
+
role: 'user',
|
112 |
+
content: 'Thanks! What about Boston?'
|
113 |
+
}
|
114 |
+
],
|
115 |
+
tools: [
|
116 |
+
{
|
117 |
+
name: 'get_weather',
|
118 |
+
description: 'Get current weather information for a location',
|
119 |
+
parameters: {
|
120 |
+
type: 'object',
|
121 |
+
properties: {
|
122 |
+
location: {
|
123 |
+
type: 'string',
|
124 |
+
description: 'The city and state, e.g. San Francisco, CA'
|
125 |
+
},
|
126 |
+
unit: {
|
127 |
+
type: 'string',
|
128 |
+
enum: ['celsius', 'fahrenheit'],
|
129 |
+
description: 'The unit of temperature to use'
|
130 |
+
}
|
131 |
+
},
|
132 |
+
required: ['location']
|
133 |
+
}
|
134 |
+
}
|
135 |
+
],
|
136 |
+
add_generation_prompt: true
|
137 |
+
}
|
138 |
+
},
|
139 |
+
variation4_mistral_tags_style: {
|
140 |
+
description:
|
141 |
+
"This variation demonstrates the Mistral-like approach using `[AVAILABLE_TOOLS]` (implicitly handled by the template from the 'tools' array), `[TOOL_CALLS]` with IDs, and `[TOOL_RESULTS]`.",
|
142 |
+
example: {
|
143 |
+
messages: [
|
144 |
+
{
|
145 |
+
role: 'system',
|
146 |
+
content: 'You are a helpful assistant that can use tools to get information for the user.'
|
147 |
+
},
|
148 |
+
{
|
149 |
+
role: 'user',
|
150 |
+
content: "What's the weather like in New York?"
|
151 |
+
},
|
152 |
+
{
|
153 |
+
role: 'assistant',
|
154 |
+
content:
|
155 |
+
"<think>\nThe user is asking about the weather in New York. I should use the weather tool to get this information.\n</think>\nI'll check the current weather in New York for you.",
|
156 |
+
tool_calls: [
|
157 |
+
{
|
158 |
+
id: 'call_weather_nyc_001',
|
159 |
+
function: {
|
160 |
+
name: 'get_weather',
|
161 |
+
arguments: {
|
162 |
+
location: 'New York',
|
163 |
+
unit: 'celsius'
|
164 |
+
}
|
165 |
+
}
|
166 |
+
}
|
167 |
+
]
|
168 |
+
},
|
169 |
+
{
|
170 |
+
role: 'tool',
|
171 |
+
tool_call_id: 'call_weather_nyc_001',
|
172 |
+
content: '{"temperature": 22, "condition": "Sunny", "humidity": 45, "wind_speed": 10}'
|
173 |
+
},
|
174 |
+
{
|
175 |
+
role: 'assistant',
|
176 |
+
content:
|
177 |
+
"The weather in New York is currently sunny with a temperature of 22°C. The humidity is at 45% with a wind speed of 10 km/h. It's a great day to be outside!"
|
178 |
+
},
|
179 |
+
{
|
180 |
+
role: 'user',
|
181 |
+
content: 'Thanks! What about Boston?'
|
182 |
+
}
|
183 |
+
],
|
184 |
+
tools: [
|
185 |
+
{
|
186 |
+
name: 'get_weather',
|
187 |
+
description: 'Get current weather information for a location',
|
188 |
+
parameters: {
|
189 |
+
type: 'object',
|
190 |
+
properties: {
|
191 |
+
location: {
|
192 |
+
type: 'string',
|
193 |
+
description: 'The city and state, e.g. San Francisco, CA'
|
194 |
+
},
|
195 |
+
unit: {
|
196 |
+
type: 'string',
|
197 |
+
enum: ['celsius', 'fahrenheit'],
|
198 |
+
description: 'The unit of temperature to use'
|
199 |
+
}
|
200 |
+
},
|
201 |
+
required: ['location']
|
202 |
+
}
|
203 |
+
}
|
204 |
+
],
|
205 |
+
add_generation_prompt: true
|
206 |
+
}
|
207 |
+
},
|
208 |
+
variation5_generic_openai_anthropic_style: {
|
209 |
+
description:
|
210 |
+
'This is the generic style, often compatible with OpenAI and Anthropic models, similar to your provided example. It serves as a baseline.',
|
211 |
+
example: {
|
212 |
+
messages: [
|
213 |
+
{
|
214 |
+
role: 'system',
|
215 |
+
content: 'You are a helpful assistant that can use tools to get information for the user.'
|
216 |
+
},
|
217 |
+
{
|
218 |
+
role: 'user',
|
219 |
+
content: "What's the weather like in New York?"
|
220 |
+
},
|
221 |
+
{
|
222 |
+
role: 'assistant',
|
223 |
+
content:
|
224 |
+
"<think>\nThe user is asking about the weather in New York. I should use the weather tool to get this information.\n</think>\nI'll check the current weather in New York for you.",
|
225 |
+
tool_calls: [
|
226 |
+
{
|
227 |
+
function: {
|
228 |
+
name: 'get_weather',
|
229 |
+
arguments: {
|
230 |
+
location: 'New York',
|
231 |
+
unit: 'celsius'
|
232 |
+
}
|
233 |
+
}
|
234 |
+
}
|
235 |
+
]
|
236 |
+
},
|
237 |
+
{
|
238 |
+
role: 'tool',
|
239 |
+
content: '{"temperature": 22, "condition": "Sunny", "humidity": 45, "wind_speed": 10}'
|
240 |
+
},
|
241 |
+
{
|
242 |
+
role: 'assistant',
|
243 |
+
content:
|
244 |
+
"The weather in New York is currently sunny with a temperature of 22°C. The humidity is at 45% with a wind speed of 10 km/h. It's a great day to be outside!"
|
245 |
+
},
|
246 |
+
{
|
247 |
+
role: 'user',
|
248 |
+
content: 'Thanks! What about Boston?'
|
249 |
+
}
|
250 |
+
],
|
251 |
+
tools: [
|
252 |
+
{
|
253 |
+
name: 'get_weather',
|
254 |
+
description: 'Get current weather information for a location',
|
255 |
+
parameters: {
|
256 |
+
type: 'object',
|
257 |
+
properties: {
|
258 |
+
location: {
|
259 |
+
type: 'string',
|
260 |
+
description: 'The city and state, e.g. San Francisco, CA'
|
261 |
+
},
|
262 |
+
unit: {
|
263 |
+
type: 'string',
|
264 |
+
enum: ['celsius', 'fahrenheit'],
|
265 |
+
description: 'The unit of temperature to use'
|
266 |
+
}
|
267 |
+
},
|
268 |
+
required: ['location']
|
269 |
+
}
|
270 |
+
}
|
271 |
+
],
|
272 |
+
add_generation_prompt: true
|
273 |
+
}
|
274 |
+
},
|
275 |
+
variation6_granite_style: {
|
276 |
+
description:
|
277 |
+
"This variation reflects Granite-like models where the tool call might be embedded directly in the assistant's content string, prefixed by a special tag like `<|tool_call|>`. The `available_tools` would be passed to the template engine.",
|
278 |
+
example: {
|
279 |
+
messages: [
|
280 |
+
{
|
281 |
+
role: 'system',
|
282 |
+
content:
|
283 |
+
"You are Granite, developed by IBM. You are a helpful assistant with access to the following tools. When a tool is required to answer the user's query, respond only with <|tool_call|> followed by a JSON list of tools used."
|
284 |
+
},
|
285 |
+
{
|
286 |
+
role: 'user',
|
287 |
+
content: "What's the weather like in New York?"
|
288 |
+
},
|
289 |
+
{
|
290 |
+
role: 'assistant',
|
291 |
+
content:
|
292 |
+
'<think>\nThe user is asking about the weather in New York. I should use the weather tool to get this information.\n</think>\nI\'ll check the current weather in New York for you.\n<|tool_call|>[{"name": "get_weather", "arguments": {"location": "New York", "unit": "celsius"}}]'
|
293 |
+
},
|
294 |
+
{
|
295 |
+
role: 'tool',
|
296 |
+
content: '{"temperature": 22, "condition": "Sunny", "humidity": 45, "wind_speed": 10}'
|
297 |
+
},
|
298 |
+
{
|
299 |
+
role: 'assistant',
|
300 |
+
content:
|
301 |
+
"The weather in New York is currently sunny with a temperature of 22°C. The humidity is at 45% with a wind speed of 10 km/h. It's a great day to be outside!"
|
302 |
+
},
|
303 |
+
{
|
304 |
+
role: 'user',
|
305 |
+
content: 'Thanks! What about Boston?'
|
306 |
+
}
|
307 |
+
],
|
308 |
+
tools: [
|
309 |
+
{
|
310 |
+
name: 'get_weather',
|
311 |
+
description: 'Get current weather information for a location',
|
312 |
+
parameters: {
|
313 |
+
type: 'object',
|
314 |
+
properties: {
|
315 |
+
location: {
|
316 |
+
type: 'string',
|
317 |
+
description: 'The city and state, e.g. San Francisco, CA'
|
318 |
+
},
|
319 |
+
unit: {
|
320 |
+
type: 'string',
|
321 |
+
enum: ['celsius', 'fahrenheit'],
|
322 |
+
description: 'The unit of temperature to use'
|
323 |
+
}
|
324 |
+
},
|
325 |
+
required: ['location']
|
326 |
+
}
|
327 |
+
}
|
328 |
+
],
|
329 |
+
add_generation_prompt: true
|
330 |
+
}
|
331 |
+
},
|
332 |
+
variation2_llama3_style: {
|
333 |
+
description:
|
334 |
+
"This variation shows how Llama-3.1-like models might handle tool definitions passed within the first user message. The assistant's invocation uses a standard `tool_calls` array.",
|
335 |
+
example: {
|
336 |
+
messages: [
|
337 |
+
{
|
338 |
+
role: 'system',
|
339 |
+
content:
|
340 |
+
'Environment: ipython\nCutting Knowledge Date: December 2023\nToday Date: 2025-05-14\n\nYou are a helpful assistant.'
|
341 |
+
},
|
342 |
+
{
|
343 |
+
role: 'user',
|
344 |
+
content:
|
345 |
+
'Given the following functions, please respond with a JSON for a function call with its proper arguments that best answers the given prompt.\n\nRespond in the format {"name": function name, "parameters": dictionary of argument name and its value}.\nDo not use variables.\n\n[\n {\n "name": "get_weather",\n "description": "Get current weather information for a location",\n "parameters": {\n "type": "object",\n "properties": {\n "location": {\n "type": "string",\n "description": "The city and state, e.g. San Francisco, CA"\n },\n "unit": {\n "type": "string",\n "enum": ["celsius", "fahrenheit"],\n "description": "The unit of temperature to use"\n }\n },\n "required": ["location"]\n }\n }\n]\n\nWhat\'s the weather like in New York?'
|
346 |
+
},
|
347 |
+
{
|
348 |
+
role: 'assistant',
|
349 |
+
content:
|
350 |
+
"<think>\nThe user is asking about the weather in New York. I should use the weather tool to get this information.\n</think>\nI'll check the current weather in New York for you.",
|
351 |
+
tool_calls: [
|
352 |
+
{
|
353 |
+
function: {
|
354 |
+
name: 'get_weather',
|
355 |
+
arguments: {
|
356 |
+
location: 'New York',
|
357 |
+
unit: 'celsius'
|
358 |
+
}
|
359 |
+
}
|
360 |
+
}
|
361 |
+
]
|
362 |
+
},
|
363 |
+
{
|
364 |
+
role: 'tool',
|
365 |
+
content: '{"temperature": 22, "condition": "Sunny", "humidity": 45, "wind_speed": 10}'
|
366 |
+
},
|
367 |
+
{
|
368 |
+
role: 'assistant',
|
369 |
+
content:
|
370 |
+
"The weather in New York is currently sunny with a temperature of 22°C. The humidity is at 45% with a wind speed of 10 km/h. It's a great day to be outside!"
|
371 |
+
},
|
372 |
+
{
|
373 |
+
role: 'user',
|
374 |
+
content: 'Thanks! What about Boston?'
|
375 |
+
}
|
376 |
+
],
|
377 |
+
tools: null,
|
378 |
+
add_generation_prompt: true
|
379 |
+
}
|
380 |
+
}
|
381 |
+
};
|
382 |
+
|
383 |
+
export function getExampleToolUsage(templateStr: string): Record<string, unknown> | undefined {
|
384 |
+
const template = new Template(templateStr);
|
385 |
+
for (const variation of Object.values(variations)) {
|
386 |
+
try {
|
387 |
+
const variationRendered = template.render(transformInput(variation.example, templateStr));
|
388 |
+
if (variationRendered.includes('get_weather')) {
|
389 |
+
return variation.example;
|
390 |
+
}
|
391 |
+
} catch (e) {
|
392 |
+
console.error(e);
|
393 |
+
}
|
394 |
+
}
|
395 |
+
return undefined;
|
396 |
+
}
|
src/lib/utils/tooltip.ts
ADDED
@@ -0,0 +1,463 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import type { ActionReturn } from 'svelte/action';
|
2 |
+
|
3 |
+
/// placement of the floating element around the anchor element
|
4 |
+
export type Placement =
|
5 |
+
| 'left'
|
6 |
+
| 'right'
|
7 |
+
| 'top'
|
8 |
+
| 'bottom'
|
9 |
+
| 'auto'
|
10 |
+
| 'prefer-left'
|
11 |
+
| 'prefer-right'
|
12 |
+
| 'prefer-top'
|
13 |
+
| 'prefer-bottom';
|
14 |
+
|
15 |
+
/// placement axis
|
16 |
+
export type Axis = 'x' | 'y';
|
17 |
+
|
18 |
+
/// alignment of the floating element against the anchor element
|
19 |
+
export type Alignment = 'start' | 'center' | 'end' | 'screen' | 'auto' | 'prefer-center';
|
20 |
+
|
21 |
+
export interface AbsolutePosition {
|
22 |
+
left: string;
|
23 |
+
top: string;
|
24 |
+
right: string;
|
25 |
+
bottom: string;
|
26 |
+
}
|
27 |
+
|
28 |
+
export interface PositionOptions {
|
29 |
+
placement?: Placement; /// preferred placement of the floating element
|
30 |
+
alignment?: Alignment; /// preferred alignment of the floating element
|
31 |
+
hitZoneXMargin?: number; /// page x margin before hitting the edge of the screen
|
32 |
+
hitZoneYMargin?: number; /// page y margin before hitting the edge of the screen
|
33 |
+
shift?: boolean; /// shift the floating element to center it against the anchor element
|
34 |
+
arrowPadding?: number; /// padding for the arrow
|
35 |
+
arrowSize?: number; /// size of the arrow
|
36 |
+
minMargin?: number; /// minimum margin around the floating element in "screen" alignment
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Compute the best placement for the floating element based on
|
41 |
+
* the anchor element position, the page size and a preferred placement.
|
42 |
+
*/
|
43 |
+
export function computePlacement(
|
44 |
+
anchorBBox: DOMRect,
|
45 |
+
floatBBox: DOMRect,
|
46 |
+
preferredPlacement: Placement = 'auto',
|
47 |
+
pageWidth: number,
|
48 |
+
pageHeight: number,
|
49 |
+
opts: {
|
50 |
+
hitZoneXMargin: number;
|
51 |
+
hitZoneYMargin: number;
|
52 |
+
} = {
|
53 |
+
hitZoneXMargin: 0,
|
54 |
+
hitZoneYMargin: 0
|
55 |
+
}
|
56 |
+
): Placement {
|
57 |
+
let computedPlacement = preferredPlacement === 'auto' ? 'bottom' : preferredPlacement;
|
58 |
+
if (pageHeight > 0 && pageWidth > 0) {
|
59 |
+
if (preferredPlacement === 'auto') {
|
60 |
+
/// check if the anchor is closer to the top or bottom of the page
|
61 |
+
computedPlacement = anchorBBox.top > pageHeight / 2 ? 'top' : 'bottom';
|
62 |
+
} else if (
|
63 |
+
preferredPlacement === 'prefer-top' ||
|
64 |
+
floatBBox.width + opts.hitZoneXMargin >= pageWidth
|
65 |
+
) {
|
66 |
+
/// check if the toast has enough space to be placed above the anchor
|
67 |
+
computedPlacement =
|
68 |
+
anchorBBox.top > floatBBox.height + opts.hitZoneYMargin ? 'top' : 'bottom';
|
69 |
+
} else if (preferredPlacement === 'prefer-bottom') {
|
70 |
+
/// check if the toast has enough space to be placed below the anchor
|
71 |
+
computedPlacement =
|
72 |
+
anchorBBox.top + anchorBBox.height + floatBBox.height + opts.hitZoneYMargin > pageHeight
|
73 |
+
? 'top'
|
74 |
+
: 'bottom';
|
75 |
+
} else if (preferredPlacement === 'prefer-left') {
|
76 |
+
/// check if the toast has enough space to be placed on the left of the anchor
|
77 |
+
computedPlacement =
|
78 |
+
anchorBBox.left > floatBBox.width + opts.hitZoneXMargin ? 'left' : 'right';
|
79 |
+
} else if (preferredPlacement === 'prefer-right') {
|
80 |
+
/// check if the toast has enough space to be placed on the right of the anchor
|
81 |
+
computedPlacement =
|
82 |
+
anchorBBox.left + anchorBBox.width + floatBBox.width + opts.hitZoneXMargin > pageWidth
|
83 |
+
? 'left'
|
84 |
+
: 'right';
|
85 |
+
}
|
86 |
+
}
|
87 |
+
return computedPlacement;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Compute the best alignment for the floating element based on
|
92 |
+
* the anchor element position, the page size, the placement axis and a preferred alignment.
|
93 |
+
*/
|
94 |
+
export function computeAlignment(
|
95 |
+
anchorBBox: DOMRect,
|
96 |
+
floatingBBox: DOMRect,
|
97 |
+
preferredAlignment: Alignment = 'auto',
|
98 |
+
axis: Axis = 'y',
|
99 |
+
pageWidth: number,
|
100 |
+
pageHeight: number,
|
101 |
+
opts: {
|
102 |
+
hitZoneXMargin: number;
|
103 |
+
hitZoneYMargin: number;
|
104 |
+
} = {
|
105 |
+
hitZoneXMargin: 0,
|
106 |
+
hitZoneYMargin: 0
|
107 |
+
}
|
108 |
+
): Alignment {
|
109 |
+
let computedAlignment =
|
110 |
+
preferredAlignment === 'auto' ? (axis === 'y' ? 'center' : 'start') : preferredAlignment;
|
111 |
+
if (['prefer-center', 'auto'].includes(preferredAlignment) && pageWidth > 0) {
|
112 |
+
if (floatingBBox.width + opts.hitZoneXMargin * 2 >= pageWidth) {
|
113 |
+
/// on mobile, large popovers should be centered and screen wide
|
114 |
+
computedAlignment = 'screen';
|
115 |
+
} else if (
|
116 |
+
axis === 'y' &&
|
117 |
+
anchorBBox.left + floatingBBox.width > pageWidth - opts.hitZoneXMargin &&
|
118 |
+
anchorBBox.left - floatingBBox.width - opts.hitZoneXMargin < 0
|
119 |
+
) {
|
120 |
+
/// if the floating element is too wide we center it
|
121 |
+
computedAlignment = 'center';
|
122 |
+
} else if (
|
123 |
+
axis === 'y' &&
|
124 |
+
anchorBBox.left + floatingBBox.width > pageWidth - opts.hitZoneXMargin
|
125 |
+
) {
|
126 |
+
/// align at the end of the anchor when the right edge of the page is too close
|
127 |
+
computedAlignment = 'end';
|
128 |
+
} else if (axis === 'y' && anchorBBox.left - floatingBBox.width - opts.hitZoneXMargin < 0) {
|
129 |
+
/// align at the start of the anchor when the left edge of the page is too close
|
130 |
+
computedAlignment = 'start';
|
131 |
+
} else if (
|
132 |
+
axis === 'x' &&
|
133 |
+
anchorBBox.top + floatingBBox.height > pageHeight - opts.hitZoneYMargin
|
134 |
+
) {
|
135 |
+
/// when placed horizontally, align at the end of the anchor when the top edge of the page is too close
|
136 |
+
computedAlignment = 'end';
|
137 |
+
} else {
|
138 |
+
/// start by default except if prefer-center is set
|
139 |
+
computedAlignment = preferredAlignment === 'prefer-center' ? 'center' : 'start';
|
140 |
+
}
|
141 |
+
}
|
142 |
+
return computedAlignment;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Compute the position of the floating element
|
147 |
+
*/
|
148 |
+
export function computePosition(
|
149 |
+
anchorBBox: DOMRect,
|
150 |
+
floatBBox: DOMRect,
|
151 |
+
{
|
152 |
+
placement = 'auto',
|
153 |
+
alignment = 'auto',
|
154 |
+
hitZoneXMargin = 0,
|
155 |
+
hitZoneYMargin = 0,
|
156 |
+
shift = false,
|
157 |
+
arrowPadding = 10,
|
158 |
+
arrowSize = 8,
|
159 |
+
minMargin = 20
|
160 |
+
}: PositionOptions = {}
|
161 |
+
): { float: AbsolutePosition; arrow: AbsolutePosition } {
|
162 |
+
const axis: Axis = ['auto', 'top', 'bottom', 'prefer-top', 'prefer-bottom'].includes(placement)
|
163 |
+
? 'y'
|
164 |
+
: 'x';
|
165 |
+
|
166 |
+
const computedAlignment = computeAlignment(
|
167 |
+
anchorBBox,
|
168 |
+
floatBBox,
|
169 |
+
alignment,
|
170 |
+
axis,
|
171 |
+
window.innerWidth,
|
172 |
+
window.innerHeight,
|
173 |
+
{ hitZoneXMargin, hitZoneYMargin }
|
174 |
+
);
|
175 |
+
const computedPlacement = computePlacement(
|
176 |
+
anchorBBox,
|
177 |
+
floatBBox,
|
178 |
+
placement,
|
179 |
+
window.innerWidth,
|
180 |
+
window.innerHeight,
|
181 |
+
{
|
182 |
+
hitZoneXMargin,
|
183 |
+
hitZoneYMargin
|
184 |
+
}
|
185 |
+
);
|
186 |
+
|
187 |
+
/// position of the anchor element
|
188 |
+
const left = anchorBBox.left + window.scrollX;
|
189 |
+
const width = anchorBBox.width;
|
190 |
+
const height = anchorBBox.height;
|
191 |
+
|
192 |
+
const halfArrowSize = arrowSize / 2;
|
193 |
+
|
194 |
+
let floatingLeft: string = '';
|
195 |
+
let floatingTop: string = '';
|
196 |
+
let floatingRight: string = '';
|
197 |
+
let floatingBottom: string = '';
|
198 |
+
let arrowLeft: string = '';
|
199 |
+
let arrowTop: string = '';
|
200 |
+
let arrowRight: string = '';
|
201 |
+
let arrowBottom: string = '';
|
202 |
+
|
203 |
+
switch (computedPlacement) {
|
204 |
+
case 'top': {
|
205 |
+
floatingBottom = `calc(100% + ${arrowSize}px)`;
|
206 |
+
arrowTop = `calc(100% - ${halfArrowSize}px)`;
|
207 |
+
break;
|
208 |
+
}
|
209 |
+
case 'bottom': {
|
210 |
+
floatingTop = `calc(100% + ${arrowSize}px)`;
|
211 |
+
arrowBottom = `calc(100% - ${halfArrowSize}px)`;
|
212 |
+
break;
|
213 |
+
}
|
214 |
+
case 'left': {
|
215 |
+
console.log('left');
|
216 |
+
floatingRight = `calc(100% + ${arrowSize}px)`;
|
217 |
+
arrowRight = `-${halfArrowSize}px`;
|
218 |
+
break;
|
219 |
+
}
|
220 |
+
case 'right': {
|
221 |
+
floatingLeft = `calc(100% + ${arrowSize}px)`;
|
222 |
+
arrowLeft = `-${halfArrowSize}px`;
|
223 |
+
break;
|
224 |
+
}
|
225 |
+
default: {
|
226 |
+
break;
|
227 |
+
}
|
228 |
+
}
|
229 |
+
if (axis === 'y') {
|
230 |
+
/// shift the floating element so the arrow is exactly at the middle of the anchor
|
231 |
+
const shiftFloating = shift ? width / 2 - halfArrowSize - arrowPadding : 0;
|
232 |
+
switch (computedAlignment) {
|
233 |
+
case 'start': {
|
234 |
+
floatingLeft = `${shiftFloating}px`;
|
235 |
+
floatingRight = 'auto';
|
236 |
+
arrowLeft = `${arrowPadding}px`;
|
237 |
+
break;
|
238 |
+
}
|
239 |
+
case 'center': {
|
240 |
+
floatingLeft = `-${floatBBox.width / 2 - width / 2}px`;
|
241 |
+
floatingRight = 'auto';
|
242 |
+
arrowLeft = `calc(50% - ${halfArrowSize}px)`;
|
243 |
+
break;
|
244 |
+
}
|
245 |
+
case 'end': {
|
246 |
+
floatingLeft = 'auto';
|
247 |
+
floatingRight = `${shiftFloating}px`;
|
248 |
+
arrowRight = `${arrowPadding}px`;
|
249 |
+
break;
|
250 |
+
}
|
251 |
+
case 'screen': {
|
252 |
+
floatingLeft = `${minMargin - left}px`;
|
253 |
+
floatingRight = 'auto';
|
254 |
+
arrowLeft = `${left - minMargin + width / 2 - halfArrowSize}px`;
|
255 |
+
break;
|
256 |
+
}
|
257 |
+
default:
|
258 |
+
break;
|
259 |
+
}
|
260 |
+
} else {
|
261 |
+
/// shift the floating element so the arrow is exactly at the middle of the anchor
|
262 |
+
const popoverShift = shift ? height / 2 - halfArrowSize - arrowPadding : 0;
|
263 |
+
switch (computedAlignment) {
|
264 |
+
case 'start': {
|
265 |
+
floatingTop = `${popoverShift}px`;
|
266 |
+
arrowTop = `${floatBBox.height < arrowPadding * 2 ? floatBBox.height / 2 : arrowPadding}px`;
|
267 |
+
break;
|
268 |
+
}
|
269 |
+
case 'center': {
|
270 |
+
floatingTop = `-${floatBBox.height / 2 - height / 2}px`;
|
271 |
+
arrowTop = `calc(50% - ${halfArrowSize}px)`;
|
272 |
+
break;
|
273 |
+
}
|
274 |
+
case 'end': {
|
275 |
+
floatingBottom = `${popoverShift}px`;
|
276 |
+
arrowBottom = `${floatBBox.height < arrowPadding * 2 ? floatBBox.height / 2 : arrowPadding}px`;
|
277 |
+
break;
|
278 |
+
}
|
279 |
+
case 'screen': {
|
280 |
+
floatingLeft = `${minMargin - left}px`;
|
281 |
+
floatingRight = `auto`;
|
282 |
+
arrowLeft = `${left - minMargin + width / 2 - halfArrowSize}px`;
|
283 |
+
break;
|
284 |
+
}
|
285 |
+
default:
|
286 |
+
break;
|
287 |
+
}
|
288 |
+
}
|
289 |
+
return {
|
290 |
+
arrow: { left: arrowLeft, top: arrowTop, right: arrowRight, bottom: arrowBottom },
|
291 |
+
float: { left: floatingLeft, top: floatingTop, right: floatingRight, bottom: floatingBottom }
|
292 |
+
};
|
293 |
+
}
|
294 |
+
|
295 |
+
const defaultOptions: PositionOptions = {
|
296 |
+
placement: 'prefer-top',
|
297 |
+
alignment: 'prefer-center',
|
298 |
+
hitZoneXMargin: 20,
|
299 |
+
hitZoneYMargin: 20,
|
300 |
+
shift: true,
|
301 |
+
arrowPadding: 10,
|
302 |
+
arrowSize: 8,
|
303 |
+
minMargin: 10
|
304 |
+
};
|
305 |
+
|
306 |
+
/**
|
307 |
+
* Tooltip svelte action,
|
308 |
+
*
|
309 |
+
* use it with the tooltip text content as a string.
|
310 |
+
* ```html
|
311 |
+
* <div use:tooltip={"Tooltip content"} />
|
312 |
+
* ```
|
313 |
+
* or with the tooltip options as an object.
|
314 |
+
* ```html
|
315 |
+
* <div use:tooltip={{ content: "Tooltip content", opts: { placement: "left", alignment: "end" } }} />
|
316 |
+
* ```
|
317 |
+
*/
|
318 |
+
export function tooltip(
|
319 |
+
node: HTMLElement,
|
320 |
+
parameter:
|
321 |
+
| string
|
322 |
+
| {
|
323 |
+
content: string | undefined;
|
324 |
+
opts?: PositionOptions;
|
325 |
+
showOn?: 'click' | 'hover' | 'hoverTouch' | 'always';
|
326 |
+
disabled?: boolean;
|
327 |
+
}
|
328 |
+
| undefined
|
329 |
+
): ActionReturn {
|
330 |
+
/// if the tooltip is disabled, or without content, we do nothing
|
331 |
+
if (
|
332 |
+
parameter === undefined ||
|
333 |
+
(typeof parameter === 'string' && !parameter) ||
|
334 |
+
(typeof parameter === 'object' && (!parameter.content || parameter.disabled === true))
|
335 |
+
) {
|
336 |
+
return {};
|
337 |
+
}
|
338 |
+
|
339 |
+
let content: string;
|
340 |
+
const opts: PositionOptions = { ...defaultOptions };
|
341 |
+
let showOn = 'hover';
|
342 |
+
if (typeof parameter === 'string') {
|
343 |
+
content = parameter;
|
344 |
+
} else {
|
345 |
+
content = parameter.content as string;
|
346 |
+
Object.assign(opts, parameter.opts);
|
347 |
+
showOn = parameter.showOn ?? 'hover';
|
348 |
+
}
|
349 |
+
|
350 |
+
/// create elements
|
351 |
+
const tooltipMask = document.createElement('div');
|
352 |
+
tooltipMask.className = 'tooltip-mask hidden';
|
353 |
+
|
354 |
+
const tooltipElt = document.createElement('div');
|
355 |
+
tooltipElt.className = 'tooltip';
|
356 |
+
tooltipElt.setAttribute('role', 'tooltip');
|
357 |
+
|
358 |
+
const arrowElt = document.createElement('div');
|
359 |
+
arrowElt.className = 'tooltip-arrow';
|
360 |
+
|
361 |
+
tooltipElt.appendChild(arrowElt);
|
362 |
+
tooltipElt.appendChild(document.createTextNode(content));
|
363 |
+
tooltipMask.appendChild(tooltipElt);
|
364 |
+
document.body.appendChild(tooltipMask);
|
365 |
+
|
366 |
+
function updateElementPosition(
|
367 |
+
element: HTMLElement,
|
368 |
+
position: AbsolutePosition | { top: string; left?: string; width?: string; height?: string }
|
369 |
+
) {
|
370 |
+
for (const [key, value] of Object.entries(position)) {
|
371 |
+
element.style[key] = value;
|
372 |
+
}
|
373 |
+
}
|
374 |
+
|
375 |
+
function updatePositions() {
|
376 |
+
updateElementPosition(tooltipMask, {
|
377 |
+
top: `${node.getBoundingClientRect().top + window.scrollY}px`,
|
378 |
+
left: `${node.getBoundingClientRect().left + window.scrollX}px`,
|
379 |
+
width: `${node.getBoundingClientRect().width}px`,
|
380 |
+
height: `${node.getBoundingClientRect().height}px`
|
381 |
+
});
|
382 |
+
|
383 |
+
const positions = computePosition(
|
384 |
+
node.getBoundingClientRect(),
|
385 |
+
tooltipElt.getBoundingClientRect(),
|
386 |
+
opts
|
387 |
+
);
|
388 |
+
|
389 |
+
updateElementPosition(tooltipElt, positions.float);
|
390 |
+
updateElementPosition(arrowElt, positions.arrow);
|
391 |
+
}
|
392 |
+
|
393 |
+
function show() {
|
394 |
+
tooltipMask.classList.remove('hidden');
|
395 |
+
updatePositions();
|
396 |
+
|
397 |
+
// eslint-disable-next-line github/prefer-observers
|
398 |
+
document.addEventListener('scroll', updatePositions);
|
399 |
+
// eslint-disable-next-line github/prefer-observers
|
400 |
+
document.addEventListener('resize', updatePositions);
|
401 |
+
}
|
402 |
+
|
403 |
+
function hide() {
|
404 |
+
tooltipMask.classList.add('hidden');
|
405 |
+
document.removeEventListener('scroll', updatePositions);
|
406 |
+
document.removeEventListener('resize', updatePositions);
|
407 |
+
}
|
408 |
+
|
409 |
+
switch (showOn) {
|
410 |
+
case 'click': {
|
411 |
+
node.addEventListener('click', show);
|
412 |
+
break;
|
413 |
+
}
|
414 |
+
case 'hoverTouch': {
|
415 |
+
node.addEventListener('mouseenter', show);
|
416 |
+
node.addEventListener('mouseleave', hide);
|
417 |
+
node.addEventListener('touchstart', show);
|
418 |
+
node.addEventListener('touchend', hide);
|
419 |
+
break;
|
420 |
+
}
|
421 |
+
case 'hover': {
|
422 |
+
node.addEventListener('mouseenter', show);
|
423 |
+
node.addEventListener('mouseleave', hide);
|
424 |
+
break;
|
425 |
+
}
|
426 |
+
case 'always': {
|
427 |
+
show();
|
428 |
+
break;
|
429 |
+
}
|
430 |
+
}
|
431 |
+
|
432 |
+
return {
|
433 |
+
destroy() {
|
434 |
+
document.removeEventListener('scroll', updatePositions);
|
435 |
+
document.removeEventListener('resize', updatePositions);
|
436 |
+
|
437 |
+
switch (showOn) {
|
438 |
+
case 'click': {
|
439 |
+
node.removeEventListener('click', show);
|
440 |
+
break;
|
441 |
+
}
|
442 |
+
case 'hoverTouch': {
|
443 |
+
node.removeEventListener('mouseenter', show);
|
444 |
+
node.removeEventListener('mouseleave', hide);
|
445 |
+
node.removeEventListener('touchstart', show);
|
446 |
+
node.removeEventListener('touchend', hide);
|
447 |
+
break;
|
448 |
+
}
|
449 |
+
case 'hover': {
|
450 |
+
node.removeEventListener('mouseenter', show);
|
451 |
+
node.removeEventListener('mouseleave', hide);
|
452 |
+
break;
|
453 |
+
}
|
454 |
+
}
|
455 |
+
if (showOn === 'always') {
|
456 |
+
tooltipElt.style.opacity = '0';
|
457 |
+
setTimeout(() => document.body.removeChild(tooltipMask), 150);
|
458 |
+
} else {
|
459 |
+
document.body.removeChild(tooltipMask);
|
460 |
+
}
|
461 |
+
}
|
462 |
+
};
|
463 |
+
}
|
src/lib/utils/transformInput.ts
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export function transformInput(input: Record<string, unknown>, templateStr: string) {
|
2 |
+
// handle cohere special case
|
3 |
+
if (templateStr.includes('safety_mode')) {
|
4 |
+
input.safety_mode = '';
|
5 |
+
}
|
6 |
+
return input;
|
7 |
+
}
|
src/routes/+page.svelte
CHANGED
@@ -1,2 +1,289 @@
|
|
1 |
-
<
|
2 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import JsonEditor from '$lib/JsonEditor/JsonEditor.svelte';
|
3 |
+
import ChatTemplateViewer from '$lib/ChatTemplateViewer/ChatTemplateViewer.svelte';
|
4 |
+
import OutputViewer from '$lib/OutputViewer/OutputViewer.svelte';
|
5 |
+
import type { ChatTemplate, FormattedChatTemplate } from '$lib/ChatTemplateViewer/types';
|
6 |
+
import { onMount } from 'svelte';
|
7 |
+
import { Template } from '@huggingface/jinja';
|
8 |
+
import { goto } from '$app/navigation';
|
9 |
+
import { page } from '$app/stores';
|
10 |
+
|
11 |
+
let modelId = $page.url.searchParams.get('modelId') ?? 'Qwen/Qwen3-235B-A22B';
|
12 |
+
let formattedTemplates: FormattedChatTemplate[] = [];
|
13 |
+
let selectedTemplate: FormattedChatTemplate | undefined = undefined;
|
14 |
+
let showFormattedTemplate = true;
|
15 |
+
let selectedExampleInputId = '';
|
16 |
+
|
17 |
+
let leftWidth = 50; // percent
|
18 |
+
let isDraggingVertical = false;
|
19 |
+
|
20 |
+
let topHeight = 50; // percent (for right pane)
|
21 |
+
let isDraggingHorizontal = false;
|
22 |
+
|
23 |
+
let error = '';
|
24 |
+
let output = '';
|
25 |
+
|
26 |
+
let input = {
|
27 |
+
messages: [
|
28 |
+
{
|
29 |
+
role: 'user',
|
30 |
+
content: 'Hello, how are you?'
|
31 |
+
},
|
32 |
+
{
|
33 |
+
role: 'assistant',
|
34 |
+
content: "I'm doing great. How can I help you today?"
|
35 |
+
},
|
36 |
+
{
|
37 |
+
role: 'user',
|
38 |
+
content: 'Can you tell me a joke?'
|
39 |
+
}
|
40 |
+
],
|
41 |
+
add_generation_prompt: true
|
42 |
+
};
|
43 |
+
|
44 |
+
$: {
|
45 |
+
try {
|
46 |
+
if (!input.messages) {
|
47 |
+
error = "Invalid JSON: missing 'messages' key";
|
48 |
+
}
|
49 |
+
|
50 |
+
if (selectedTemplate) {
|
51 |
+
const template = new Template(
|
52 |
+
showFormattedTemplate ? selectedTemplate.formattedTemplate : selectedTemplate.template
|
53 |
+
);
|
54 |
+
output = template.render(input);
|
55 |
+
error = '';
|
56 |
+
}
|
57 |
+
} catch (e) {
|
58 |
+
error = e instanceof Error ? e.message : 'Unknown error';
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
function startDragVertical(e: MouseEvent) {
|
63 |
+
isDraggingVertical = true;
|
64 |
+
document.body.style.cursor = 'col-resize';
|
65 |
+
}
|
66 |
+
|
67 |
+
function stopDragVertical() {
|
68 |
+
isDraggingVertical = false;
|
69 |
+
document.body.style.cursor = '';
|
70 |
+
}
|
71 |
+
|
72 |
+
function onDragVertical(e: MouseEvent) {
|
73 |
+
if (!isDraggingVertical) return;
|
74 |
+
const playground = document.getElementById('playground-container');
|
75 |
+
if (!playground) return;
|
76 |
+
const rect = playground.getBoundingClientRect();
|
77 |
+
const offsetX = e.clientX - rect.left;
|
78 |
+
let percent = (offsetX / rect.width) * 100;
|
79 |
+
if (percent < 10) percent = 10;
|
80 |
+
if (percent > 90) percent = 90;
|
81 |
+
leftWidth = percent;
|
82 |
+
}
|
83 |
+
|
84 |
+
function startDragHorizontal(e: MouseEvent) {
|
85 |
+
isDraggingHorizontal = true;
|
86 |
+
document.body.style.cursor = 'row-resize';
|
87 |
+
}
|
88 |
+
|
89 |
+
function stopDragHorizontal() {
|
90 |
+
isDraggingHorizontal = false;
|
91 |
+
document.body.style.cursor = '';
|
92 |
+
}
|
93 |
+
|
94 |
+
function onDragHorizontal(e: MouseEvent) {
|
95 |
+
if (!isDraggingHorizontal) return;
|
96 |
+
const rightPane = document.getElementById('right-pane');
|
97 |
+
if (!rightPane) return;
|
98 |
+
const rect = rightPane.getBoundingClientRect();
|
99 |
+
const offsetY = e.clientY - rect.top;
|
100 |
+
let percent = (offsetY / rect.height) * 100;
|
101 |
+
if (percent < 10) percent = 10;
|
102 |
+
if (percent > 90) percent = 90;
|
103 |
+
topHeight = percent;
|
104 |
+
}
|
105 |
+
|
106 |
+
async function getChatTemplate(modelId: string) {
|
107 |
+
try {
|
108 |
+
const res = await fetch('https://huggingface.co/api/models/' + modelId);
|
109 |
+
|
110 |
+
if (!res.ok) {
|
111 |
+
alert(`Failed to fetch model "${modelId}": ${res.status} ${res.statusText}`);
|
112 |
+
return;
|
113 |
+
}
|
114 |
+
|
115 |
+
const model = await res.json();
|
116 |
+
|
117 |
+
let chatTemplate: ChatTemplate | undefined = undefined;
|
118 |
+
|
119 |
+
if (model.config?.chat_template_jinja) {
|
120 |
+
// model.config.chat_template_jinja & optional model.config.additional_chat_templates
|
121 |
+
chatTemplate = model.config.chat_template_jinja;
|
122 |
+
if (model.config?.additional_chat_templates) {
|
123 |
+
chatTemplate = [
|
124 |
+
{
|
125 |
+
name: 'default',
|
126 |
+
template: model.config.chat_template_jinja
|
127 |
+
},
|
128 |
+
...(model.config?.additional_chat_templates
|
129 |
+
? Object.keys(model.config.additional_chat_templates).map((name) => ({
|
130 |
+
name,
|
131 |
+
template: model.config?.additional_chat_templates?.[name] ?? ''
|
132 |
+
}))
|
133 |
+
: [])
|
134 |
+
];
|
135 |
+
}
|
136 |
+
} else if (model.config?.processor_config?.chat_template) {
|
137 |
+
// for backward compatibility VLM
|
138 |
+
chatTemplate = model.config.processor_config.chat_template;
|
139 |
+
} else if (model.config?.tokenizer_config?.chat_template) {
|
140 |
+
// for backward compatibility
|
141 |
+
chatTemplate = model.config.tokenizer_config.chat_template;
|
142 |
+
} else if (model.gguf?.chat_template) {
|
143 |
+
// for GGUF models
|
144 |
+
chatTemplate = model.gguf.chat_template;
|
145 |
+
}
|
146 |
+
|
147 |
+
const formattedTemplates: FormattedChatTemplate[] = (
|
148 |
+
typeof chatTemplate === 'string'
|
149 |
+
? [{ name: 'default', template: chatTemplate }]
|
150 |
+
: chatTemplate
|
151 |
+
) // Convert string template to array for unified handling
|
152 |
+
.map(({ name, template }) => ({
|
153 |
+
name,
|
154 |
+
template,
|
155 |
+
formattedTemplate: (() => {
|
156 |
+
try {
|
157 |
+
return new Template(template).format();
|
158 |
+
} catch (error) {
|
159 |
+
console.error(`Error formatting chat template ${name}:`, error);
|
160 |
+
return template; // Return the original template in case of an error
|
161 |
+
}
|
162 |
+
})()
|
163 |
+
})) // add formatted template attribute
|
164 |
+
.map(({ name, template, formattedTemplate }) => ({
|
165 |
+
name,
|
166 |
+
template,
|
167 |
+
formattedTemplate,
|
168 |
+
templateUnedited: template,
|
169 |
+
formattedTemplateUnedited: formattedTemplate
|
170 |
+
}));
|
171 |
+
|
172 |
+
let selectedTemplate =
|
173 |
+
formattedTemplates.find(({ name }) => name === $page.url.searchParams.get('template')) ??
|
174 |
+
formattedTemplates[0];
|
175 |
+
|
176 |
+
return { formattedTemplates, selectedTemplate, model };
|
177 |
+
} catch (error) {
|
178 |
+
console.error(error);
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
async function handleModelIdChange(newModelId: string, opts?: { replaceState?: boolean }) {
|
183 |
+
const modelTemplate = await getChatTemplate(newModelId);
|
184 |
+
if (modelTemplate) {
|
185 |
+
modelId = newModelId;
|
186 |
+
formattedTemplates = modelTemplate.formattedTemplates;
|
187 |
+
selectedTemplate = modelTemplate.selectedTemplate;
|
188 |
+
const model = modelTemplate.model;
|
189 |
+
input = {
|
190 |
+
...input,
|
191 |
+
bos_token: model?.config?.tokenizer_config?.bos_token?.content ?? model?.gguf?.bos_token,
|
192 |
+
eos_token: model?.config?.tokenizer_config?.eos_token?.content ?? model?.gguf?.eos_token,
|
193 |
+
pad_token: model?.config?.tokenizer_config?.pad_token?.content ?? model?.gguf?.pad_token,
|
194 |
+
unk_token: model?.config?.tokenizer_config?.unk_token?.content ?? model?.gguf?.unk_token
|
195 |
+
};
|
196 |
+
|
197 |
+
if (opts?.replaceState) {
|
198 |
+
updateParams();
|
199 |
+
}
|
200 |
+
}
|
201 |
+
}
|
202 |
+
|
203 |
+
function updateParams() {
|
204 |
+
let searchParams = '?modelId=' + modelId;
|
205 |
+
if (selectedTemplate && selectedTemplate.name !== 'default') {
|
206 |
+
searchParams += '&template=' + selectedTemplate.name;
|
207 |
+
}
|
208 |
+
if (selectedExampleInputId) {
|
209 |
+
searchParams += '&example=' + selectedExampleInputId;
|
210 |
+
}
|
211 |
+
|
212 |
+
goto(searchParams, { replaceState: true });
|
213 |
+
|
214 |
+
// post message to parent
|
215 |
+
const parentOrigin = 'https://huggingface.co';
|
216 |
+
window.parent.postMessage({ queryString: searchParams }, parentOrigin);
|
217 |
+
}
|
218 |
+
|
219 |
+
onMount(async () => {
|
220 |
+
await handleModelIdChange(modelId);
|
221 |
+
});
|
222 |
+
</script>
|
223 |
+
|
224 |
+
<svelte:window
|
225 |
+
on:mousemove={onDragVertical}
|
226 |
+
on:mouseup={stopDragVertical}
|
227 |
+
on:mousemove={onDragHorizontal}
|
228 |
+
on:mouseup={stopDragHorizontal}
|
229 |
+
/>
|
230 |
+
|
231 |
+
<div
|
232 |
+
id="playground-container"
|
233 |
+
class="relative flex h-screen w-full overflow-hidden border bg-white shadow select-none dark:bg-gray-950"
|
234 |
+
>
|
235 |
+
<div class="overflow-auto" style="width: {leftWidth}%">
|
236 |
+
{#if formattedTemplates.length}
|
237 |
+
<ChatTemplateViewer
|
238 |
+
{modelId}
|
239 |
+
{formattedTemplates}
|
240 |
+
bind:selectedTemplate
|
241 |
+
bind:showFormattedTemplate
|
242 |
+
on:modelIdChange={(e) => handleModelIdChange(e.detail, { replaceState: true })}
|
243 |
+
on:templateChange={(e) => updateParams()}
|
244 |
+
/>
|
245 |
+
{/if}
|
246 |
+
</div>
|
247 |
+
|
248 |
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
249 |
+
<div
|
250 |
+
class="hidden h-full w-1 cursor-col-resize items-center justify-center bg-gray-100 select-none hover:bg-blue-200 active:bg-blue-200 sm:flex dark:bg-gray-700 dark:hover:bg-blue-900 dark:active:bg-blue-900"
|
251 |
+
style="left: calc({leftWidth}% - 4px); z-index:10;"
|
252 |
+
on:mousedown={startDragVertical}
|
253 |
+
>
|
254 |
+
<div class="h-12 w-[0.05rem] rounded-full bg-gray-400"></div>
|
255 |
+
</div>
|
256 |
+
|
257 |
+
<div
|
258 |
+
id="right-pane"
|
259 |
+
class="relative flex h-full flex-col bg-gray-100"
|
260 |
+
style="width: {100 - leftWidth}%"
|
261 |
+
>
|
262 |
+
{#key `${modelId}-${selectedTemplate?.name}`}
|
263 |
+
<div class="w-full" style="height: {topHeight}%">
|
264 |
+
<!-- Right top pane -->
|
265 |
+
<JsonEditor
|
266 |
+
bind:error
|
267 |
+
bind:content={input}
|
268 |
+
bind:selectedTemplate
|
269 |
+
bind:selectedExampleInputId
|
270 |
+
on:exampleChange={(e) => updateParams()}
|
271 |
+
/>
|
272 |
+
</div>
|
273 |
+
{/key}
|
274 |
+
|
275 |
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
276 |
+
<div
|
277 |
+
class="hidden h-1 w-full cursor-row-resize items-center justify-center bg-gray-100 select-none hover:bg-blue-200 active:bg-blue-200 sm:flex dark:bg-gray-700 dark:hover:bg-blue-900 dark:active:bg-blue-900"
|
278 |
+
style="top: calc({topHeight}% - 4px); z-index:10;"
|
279 |
+
on:mousedown={startDragHorizontal}
|
280 |
+
>
|
281 |
+
<div class="h-[0.05rem] w-12 rounded-full bg-gray-400"></div>
|
282 |
+
</div>
|
283 |
+
|
284 |
+
<div class="w-full" style="height: {100 - topHeight}%">
|
285 |
+
<!-- Right bottom pane -->
|
286 |
+
<OutputViewer content={output} {error} />
|
287 |
+
</div>
|
288 |
+
</div>
|
289 |
+
</div>
|
svelte.config.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import adapter from '@sveltejs/adapter-
|
2 |
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
3 |
|
4 |
/** @type {import('@sveltejs/kit').Config} */
|
|
|
1 |
+
import adapter from '@sveltejs/adapter-node';
|
2 |
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
3 |
|
4 |
/** @type {import('@sveltejs/kit').Config} */
|