From 64dc3b9201069595037dbc551b3ce45eb2e0d434 Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Tue, 17 Jun 2025 10:06:39 -0500 Subject: [PATCH] initial --- package.json | 6 + pnpm-lock.yaml | 412 ++++++++++++++++++ src/app.css | 2 +- src/lib/components/ui/card/card.svelte | 2 +- src/lib/utils/markdown.ts | 46 ++ src/markdown.css | 174 ++++++++ src/routes/account/+layout.svelte | 2 +- src/routes/api/generate-message/+server.ts | 3 +- src/routes/chat/[id]/markdown-renderer.svelte | 13 + src/routes/chat/[id]/message.svelte | 17 +- src/routes/chat/model-picker.svelte | 3 +- 11 files changed, 671 insertions(+), 9 deletions(-) create mode 100644 src/lib/utils/markdown.ts create mode 100644 src/markdown.css create mode 100644 src/routes/chat/[id]/markdown-renderer.svelte diff --git a/package.json b/package.json index 0db5e77..4167b12 100644 --- a/package.json +++ b/package.json @@ -22,12 +22,16 @@ "@eslint/js": "^9.18.0", "@iconify/json": "^2.2.349", "@playwright/test": "^1.49.1", + "@shikijs/langs": "^3.6.0", + "@shikijs/markdown-it": "^3.6.0", + "@shikijs/themes": "^3.6.0", "@sveltejs/adapter-auto": "^6.0.0", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", "@tailwindcss/vite": "^4.0.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/svelte": "^5.2.4", + "@types/markdown-it": "^14.1.2", "@vercel/functions": "^2.2.0", "clsx": "^2.1.1", "concurrently": "^9.1.2", @@ -39,6 +43,7 @@ "eslint-plugin-svelte": "^3.0.0", "globals": "^16.0.0", "jsdom": "^26.0.0", + "markdown-it": "^14.1.0", "melt": "^0.35.0", "mode-watcher": "^1.0.8", "neverthrow": "^8.2.0", @@ -46,6 +51,7 @@ "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", "runed": "^0.28.0", + "shiki": "^3.6.0", "svelte": "^5.0.0", "svelte-check": "^4.0.0", "tailwind-merge": "^3.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a69b27..4183cd7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,6 +60,15 @@ importers: '@playwright/test': specifier: ^1.49.1 version: 1.53.0 + '@shikijs/langs': + specifier: ^3.6.0 + version: 3.6.0 + '@shikijs/markdown-it': + specifier: ^3.6.0 + version: 3.6.0(markdown-it-async@2.2.0) + '@shikijs/themes': + specifier: ^3.6.0 + version: 3.6.0 '@sveltejs/adapter-auto': specifier: ^6.0.0 version: 6.0.1(@sveltejs/kit@2.21.5(@sveltejs/vite-plugin-svelte@5.1.0(svelte@5.34.1)(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.34.1)(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(lightningcss@1.30.1))) @@ -78,6 +87,9 @@ importers: '@testing-library/svelte': specifier: ^5.2.4 version: 5.2.8(svelte@5.34.1)(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(lightningcss@1.30.1))(vitest@3.2.3(@types/node@24.0.1)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)) + '@types/markdown-it': + specifier: ^14.1.2 + version: 14.1.2 '@vercel/functions': specifier: ^2.2.0 version: 2.2.0 @@ -111,6 +123,9 @@ importers: jsdom: specifier: ^26.0.0 version: 26.1.0 + markdown-it: + specifier: ^14.1.0 + version: 14.1.0 melt: specifier: ^0.35.0 version: 0.35.0(@floating-ui/dom@1.7.1)(svelte@5.34.1) @@ -132,6 +147,9 @@ importers: runed: specifier: ^0.28.0 version: 0.28.0(svelte@5.34.1) + shiki: + specifier: ^3.6.0 + version: 3.6.0 svelte: specifier: ^5.0.0 version: 5.34.1 @@ -818,6 +836,35 @@ packages: cpu: [x64] os: [win32] + '@shikijs/core@3.6.0': + resolution: {integrity: sha512-9By7Xb3olEX0o6UeJyPLI1PE1scC4d3wcVepvtv2xbuN9/IThYN4Wcwh24rcFeASzPam11MCq8yQpwwzCgSBRw==} + + '@shikijs/engine-javascript@3.6.0': + resolution: {integrity: sha512-7YnLhZG/TU05IHMG14QaLvTW/9WiK8SEYafceccHUSXs2Qr5vJibUwsDfXDLmRi0zHdzsxrGKpSX6hnqe0k8nA==} + + '@shikijs/engine-oniguruma@3.6.0': + resolution: {integrity: sha512-nmOhIZ9yT3Grd+2plmW/d8+vZ2pcQmo/UnVwXMUXAKTXdi+LK0S08Ancrz5tQQPkxvjBalpMW2aKvwXfelauvA==} + + '@shikijs/langs@3.6.0': + resolution: {integrity: sha512-IdZkQJaLBu1LCYCwkr30hNuSDfllOT8RWYVZK1tD2J03DkiagYKRxj/pDSl8Didml3xxuyzUjgtioInwEQM/TA==} + + '@shikijs/markdown-it@3.6.0': + resolution: {integrity: sha512-OFJb0EY1GOfWEpeXyfav4mDXt4QjqURwhQLTYaBeWP4QjBUUYIdPri+Jf8Fgkds+i4I6WcmX47Wu9vAq8USZsA==} + peerDependencies: + markdown-it-async: ^2.2.0 + peerDependenciesMeta: + markdown-it-async: + optional: true + + '@shikijs/themes@3.6.0': + resolution: {integrity: sha512-Fq2j4nWr1DF4drvmhqKq8x5vVQ27VncF8XZMBuHuQMZvUSS3NBgpqfwz/FoGe36+W6PvniZ1yDlg2d4kmYDU6w==} + + '@shikijs/types@3.6.0': + resolution: {integrity: sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@simplewebauthn/browser@13.1.0': resolution: {integrity: sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg==} @@ -991,12 +1038,30 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/node@24.0.1': resolution: {integrity: sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@typescript-eslint/eslint-plugin@8.34.0': resolution: {integrity: sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1056,6 +1121,9 @@ packages: resolution: {integrity: sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@vercel/functions@2.2.0': resolution: {integrity: sha512-x1Zrc2jOclTSB9+Ic/XNMDinO0SG4ZS5YeV2Xz1m/tuJOM7QtPVU3Epw2czBao0dukefmC8HCNpyUL8ZchJ/Tg==} engines: {node: '>= 18'} @@ -1176,6 +1244,9 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -1188,6 +1259,12 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -1215,6 +1292,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1334,6 +1414,9 @@ packages: devalue@5.1.1: resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1355,6 +1438,10 @@ packages: resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + entities@6.0.1: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} @@ -1541,10 +1628,19 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -1749,6 +1845,9 @@ packages: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + local-pkg@1.1.1: resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} engines: {node: '>=14'} @@ -1779,6 +1878,19 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + markdown-it-async@2.2.0: + resolution: {integrity: sha512-sITME+kf799vMeO/ww/CjH6q+c05f6TLpn6VOmmWCGNqPJzSh+uFgZoMB9s0plNtW6afy63qglNAC3MhrhP/gg==} + + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + melt@0.35.0: resolution: {integrity: sha512-Y8/ImbAN83rf11AaqtQ6u9DXcZ2/Ozu6HM7/oV0BZUt0Dh8Q/j06TKyyBB3u65FRsZDJBuxiQHPklCW7dQcfLw==} peerDependencies: @@ -1789,6 +1901,21 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -1860,6 +1987,12 @@ packages: nwsapi@2.2.20: resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.3: + resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} + openai@5.3.0: resolution: {integrity: sha512-VIKmoF7y4oJCDOwP/oHXGzM69+x0dpGFmN9QmYO+uPbLFOmmnwO+x1GbsgUtI+6oraxomGZ566Y421oYVu191w==} hasBin: true @@ -2049,6 +2182,13 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -2080,6 +2220,15 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -2155,6 +2304,9 @@ packages: resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} engines: {node: '>= 0.4'} + shiki@3.6.0: + resolution: {integrity: sha512-tKn/Y0MGBTffQoklaATXmTqDU02zx8NYBGQ+F6gy87/YjKbizcLd+Cybh/0ZtOBX9r1NEnAy/GTRDKtOsc1L9w==} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -2166,6 +2318,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -2176,6 +2331,9 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2307,6 +2465,9 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -2332,6 +2493,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.6.1: resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} @@ -2341,6 +2505,21 @@ packages: undici-types@7.8.0: resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + unplugin-icons@22.1.0: resolution: {integrity: sha512-ect2ZNtk1Zgwb0NVHd0C1IDW/MV+Jk/xaq4t8o6rYdVS3+L660ZdD5kTSQZvsgdwCvquRw+/wYn75hsweRjoIA==} peerDependencies: @@ -2374,6 +2553,12 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite-node@3.2.3: resolution: {integrity: sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -2545,6 +2730,9 @@ packages: zod@3.25.64: resolution: {integrity: sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g==} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@adobe/css-tools@4.4.3': {} @@ -3017,6 +3205,46 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.43.0': optional: true + '@shikijs/core@3.6.0': + dependencies: + '@shikijs/types': 3.6.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.6.0': + dependencies: + '@shikijs/types': 3.6.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.3 + + '@shikijs/engine-oniguruma@3.6.0': + dependencies: + '@shikijs/types': 3.6.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.6.0': + dependencies: + '@shikijs/types': 3.6.0 + + '@shikijs/markdown-it@3.6.0(markdown-it-async@2.2.0)': + dependencies: + markdown-it: 14.1.0 + shiki: 3.6.0 + optionalDependencies: + markdown-it-async: 2.2.0 + + '@shikijs/themes@3.6.0': + dependencies: + '@shikijs/types': 3.6.0 + + '@shikijs/types@3.6.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + '@simplewebauthn/browser@13.1.0': {} '@simplewebauthn/server@13.1.1': @@ -3194,13 +3422,32 @@ snapshots: '@types/estree@1.0.8': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/json-schema@7.0.15': {} + '@types/linkify-it@5.0.0': {} + + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdurl@2.0.0': {} + '@types/node@24.0.1': dependencies: undici-types: 7.8.0 optional: true + '@types/unist@3.0.3': {} + '@typescript-eslint/eslint-plugin@8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -3293,6 +3540,8 @@ snapshots: '@typescript-eslint/types': 8.34.0 eslint-visitor-keys: 4.2.1 + '@ungap/structured-clone@1.3.0': {} + '@vercel/functions@2.2.0': {} '@vitest/expect@3.2.3': @@ -3421,6 +3670,8 @@ snapshots: callsites@3.1.0: {} + ccount@2.0.1: {} + chai@5.2.0: dependencies: assertion-error: 2.0.1 @@ -3439,6 +3690,10 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + check-error@2.1.1: {} chokidar@4.0.3: @@ -3461,6 +3716,8 @@ snapshots: color-name@1.1.4: {} + comma-separated-tokens@2.0.3: {} + concat-map@0.0.1: {} concurrently@9.1.2: @@ -3537,6 +3794,10 @@ snapshots: devalue@5.1.1: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + diff-sequences@29.6.3: {} dom-accessibility-api@0.5.16: {} @@ -3552,6 +3813,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.2.2 + entities@4.5.0: {} + entities@6.0.1: {} es-module-lexer@1.7.0: {} @@ -3791,10 +4054,30 @@ snapshots: has-flag@4.0.0: {} + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 + html-void-elements@3.0.0: {} + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.3 @@ -3980,6 +4263,10 @@ snapshots: lilconfig@2.1.0: {} + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + local-pkg@1.1.1: dependencies: mlly: 1.7.4 @@ -4006,6 +4293,35 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + markdown-it-async@2.2.0: + dependencies: + '@types/markdown-it': 14.1.2 + markdown-it: 14.1.0 + optional: true + + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdurl@2.0.0: {} + melt@0.35.0(@floating-ui/dom@1.7.1)(svelte@5.34.1): dependencies: '@floating-ui/dom': 1.7.1 @@ -4017,6 +4333,23 @@ snapshots: merge2@1.4.1: {} + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -4073,6 +4406,14 @@ snapshots: nwsapi@2.2.20: {} + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.3: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.0.1 + regex-recursion: 6.0.2 + openai@5.3.0(ws@8.18.2)(zod@3.25.64): optionalDependencies: ws: 8.18.2 @@ -4194,6 +4535,10 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + property-information@7.1.0: {} + + punycode.js@2.3.1: {} + punycode@2.3.1: {} pvtsutils@1.3.6: @@ -4217,6 +4562,16 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + require-directory@2.1.1: {} resolve-from@4.0.0: {} @@ -4298,6 +4653,17 @@ snapshots: shell-quote@1.8.3: {} + shiki@3.6.0: + dependencies: + '@shikijs/core': 3.6.0 + '@shikijs/engine-javascript': 3.6.0 + '@shikijs/engine-oniguruma': 3.6.0 + '@shikijs/langs': 3.6.0 + '@shikijs/themes': 3.6.0 + '@shikijs/types': 3.6.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + siginfo@2.0.0: {} sirv@3.0.1: @@ -4308,6 +4674,8 @@ snapshots: source-map-js@1.2.1: {} + space-separated-tokens@2.0.2: {} + stackback@0.0.2: {} std-env@3.9.0: {} @@ -4318,6 +4686,11 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -4454,6 +4827,8 @@ snapshots: tree-kill@1.2.2: {} + trim-lines@3.0.1: {} + ts-api-utils@2.1.0(typescript@5.8.3): dependencies: typescript: 5.8.3 @@ -4476,6 +4851,8 @@ snapshots: typescript@5.8.3: {} + uc.micro@2.1.0: {} + ufo@1.6.1: {} uncrypto@0.1.3: {} @@ -4483,6 +4860,29 @@ snapshots: undici-types@7.8.0: optional: true + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + unplugin-icons@22.1.0(svelte@5.34.1): dependencies: '@antfu/install-pkg': 1.1.0 @@ -4507,6 +4907,16 @@ snapshots: util-deprecate@1.0.2: {} + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.2 + vite-node@3.2.3(@types/node@24.0.1)(jiti@2.4.2)(lightningcss@1.30.1): dependencies: cac: 6.7.14 @@ -4653,3 +5063,5 @@ snapshots: zimmerframe@1.1.2: {} zod@3.25.64: {} + + zwitch@2.0.4: {} diff --git a/src/app.css b/src/app.css index 5035921..40442ae 100644 --- a/src/app.css +++ b/src/app.css @@ -14,7 +14,7 @@ --card-foreground: oklch(0.3257 0.1161 325.0372); --popover: oklch(1 0 0); --popover-foreground: oklch(0.3257 0.1161 325.0372); - --primary: oklch(0.5316 0.1409 355.1999); + --primary: oklch(0.5797 0.1194 237.7893); --heading: oklch(0.5797 0.1194 237.7893); --primary-foreground: oklch(1 0 0); --secondary: oklch(0.8696 0.0675 334.8991); diff --git a/src/lib/components/ui/card/card.svelte b/src/lib/components/ui/card/card.svelte index c3512cf..8ae1ff1 100644 --- a/src/lib/components/ui/card/card.svelte +++ b/src/lib/components/ui/card/card.svelte @@ -5,6 +5,6 @@ let { class: className, children, ...restProps }: HTMLAttributes = $props(); -
+
{@render children?.()}
diff --git a/src/lib/utils/markdown.ts b/src/lib/utils/markdown.ts new file mode 100644 index 0000000..083e20e --- /dev/null +++ b/src/lib/utils/markdown.ts @@ -0,0 +1,46 @@ +import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'; +import { fromHighlighter } from '@shikijs/markdown-it/core'; +import MarkdownIt from 'markdown-it'; +import { createHighlighterCore } from 'shiki/core'; +import type { Getter } from 'runed'; + +const bundledLanguages = { + bash: () => import('@shikijs/langs/bash'), + diff: () => import('@shikijs/langs/diff'), + javascript: () => import('@shikijs/langs/javascript'), + json: () => import('@shikijs/langs/json'), + svelte: () => import('@shikijs/langs/svelte'), + typescript: () => import('@shikijs/langs/typescript'), +}; + +/** A preloaded highlighter instance. */ +export const highlighter = createHighlighterCore({ + themes: [ + import('@shikijs/themes/github-light-default'), + import('@shikijs/themes/github-dark-default'), + ], + langs: Object.entries(bundledLanguages).map(([_, lang]) => lang), + engine: createJavaScriptRegexEngine(), +}); + +const md = MarkdownIt(); + +highlighter.then((h) => { + md.use( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fromHighlighter(h as any, { + themes: { + light: 'github-light-default', + dark: 'github-dark-default', + }, + }) + ); +}); + +export class Markdown { + constructor(readonly code: Getter) {} + + get current() { + return md.render(this.code()); + } +} diff --git a/src/markdown.css b/src/markdown.css new file mode 100644 index 0000000..eef3521 --- /dev/null +++ b/src/markdown.css @@ -0,0 +1,174 @@ +@import 'tailwindcss'; + +@reference './app.css'; + +html.dark .shiki, +html.dark .shiki span { + color: var(--shiki-dark) !important; + background-color: var(--shiki-dark-bg) !important; + /* Optional, if you also want font styles */ + font-style: var(--shiki-dark-font-style) !important; + font-weight: var(--shiki-dark-font-weight) !important; + text-decoration: var(--shiki-dark-text-decoration) !important; +} + +.prose { + @apply text-foreground max-w-[65ch]; + + & h1:not(.not-prose h1) { + @apply scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl; + } + + & h2:not(.not-prose h2) { + @apply mt-12 scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0; + } + + & h3:not(.not-prose h3) { + @apply mt-12 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0; + } + + & h4:not(.not-prose h4) { + @apply mt-12 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0; + } + + & h5:not(.not-prose h5) { + @apply mt-12 scroll-m-20 text-lg font-semibold tracking-tight first:mt-0; + } + + & h6:not(.not-prose h6) { + @apply mt-12 scroll-m-20 text-base font-semibold tracking-tight first:mt-0; + } + + & h1:not(.not-prose h1), + & h2:not(.not-prose h2), + & h3:not(.not-prose h3), + & h4:not(.not-prose h4), + & h5:not(.not-prose h5), + & h6:not(.not-prose h6) { + & + p { + @apply mt-0; + } + } + + & p:not(.not-prose p) { + @apply leading-[175%] [&:not(:first-child)]:mt-6; + } + + & li:not(.not-prose li) { + @apply leading-[175%]; + } + + & a:not(.not-prose a) { + @apply text-primary font-medium underline underline-offset-4; + } + + & blockquote:not(.not-prose blockquote) { + @apply mt-6 border-l-2 pl-6 italic; + } + + & table:not(.not-prose table) { + @apply my-6 w-full overflow-y-auto; + + & thead tr { + @apply even:bg-muted m-0 border-t p-0; + } + + & th { + @apply border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right; + } + + & tbody tr { + @apply even:bg-muted m-0 border-t p-0; + } + + & td { + @apply border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right; + } + } + + & ul:not(.not-prose ul) { + @apply my-6 ml-6 list-disc [&>li]:mt-2; + + & p { + @apply my-0 inline; + } + } + + & ol:not(.not-prose ol) { + @apply my-6 ml-6 list-decimal [&>li]:mt-2; + + & p { + @apply my-0 inline; + } + } + + & pre:not(.not-prose pre) { + @apply bg-muted my-6 overflow-y-auto rounded-md p-4 text-sm; + } + + & code:not(pre code):not(.not-prose code) { + @apply bg-muted relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold; + } + + & .lead:not(.not-prose .lead) { + @apply text-muted-foreground text-xl; + } + + & .large:not(.not-prose .large) { + @apply text-lg font-semibold; + } + + & .small:not(.not-prose .small) { + @apply text-sm leading-none font-medium; + } + + & .muted:not(.not-prose .muted) { + @apply text-muted-foreground text-sm; + } + + & img:not(.not-prose img), + & picture:not(.not-prose picture), + & video:not(.not-prose video) { + @apply my-6; + } + + & picture > img:not(.not-prose picture > img) { + @apply my-0; + } + + & kbd:not(.not-prose kbd) { + @apply bg-muted rounded-md px-1.5 py-0.5 text-xs font-semibold; + } + + & hr { + @apply my-10; + } + + & dl:not(.not-prose dl) { + @apply my-6; + + & dt { + @apply mt-6 font-semibold tracking-tight first:mt-0; + } + } + + & details:not(.not-prose details) { + @apply mt-6; + + & summary { + @apply mt-6 cursor-pointer font-semibold tracking-tight; + } + + & p { + @apply [&:first-of-type]:mt-2; + } + } + + & mark:not(.not-prose mark) { + @apply bg-yellow-300; + } + + & small:not(.not-prose small) { + @apply text-xs leading-none; + } +} diff --git a/src/routes/account/+layout.svelte b/src/routes/account/+layout.svelte index 535f4e8..d937661 100644 --- a/src/routes/account/+layout.svelte +++ b/src/routes/account/+layout.svelte @@ -83,7 +83,7 @@
{#each navigation as tab (tab)} syntax. Please follow these rules. + content: `Respond in markdown format. The user may have mentioned one or more rules to follow with the @ syntax. Please follow these rules. Rules to follow: ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, }; @@ -251,7 +251,6 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`, openai.chat.completions.create({ model: model.model_id, messages: [...messages.map((m) => ({ role: m.role, content: m.content })), systemMessage], - max_tokens: 1000, temperature: 0.7, stream: true, }), diff --git a/src/routes/chat/[id]/markdown-renderer.svelte b/src/routes/chat/[id]/markdown-renderer.svelte new file mode 100644 index 0000000..24b9eea --- /dev/null +++ b/src/routes/chat/[id]/markdown-renderer.svelte @@ -0,0 +1,13 @@ + + +{@html markdown.current} diff --git a/src/routes/chat/[id]/message.svelte b/src/routes/chat/[id]/message.svelte index cc63f50..4097a60 100644 --- a/src/routes/chat/[id]/message.svelte +++ b/src/routes/chat/[id]/message.svelte @@ -3,12 +3,14 @@ import { tv } from 'tailwind-variants'; import type { Doc } from '$lib/backend/convex/_generated/dataModel'; import { CopyButton } from '$lib/components/ui/copy-button'; + import '../../../markdown.css'; + import MarkdownRenderer from './markdown-renderer.svelte'; const style = tv({ - base: 'rounded-lg p-2', + base: 'prose rounded-lg p-2', variants: { role: { - user: 'bg-primary text-primary-foreground self-end', + user: 'bg-primary !text-primary-foreground self-end', assistant: 'text-foreground', }, }, @@ -24,7 +26,16 @@ {#if message.role !== 'system' && !(message.role === 'assistant' && message.content.length === 0)}
- {message.content} + + + + {#snippet failed(error)} +
+ Error rendering markdown: +
{error.message}
+
+ {/snippet} +
- {#each enabledArr as model (model._id)} {/each}