From 6e2030d4d48175e3280524aebc23f5bc4e5d4398 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 11 Nov 2024 21:14:43 -0800 Subject: [PATCH] finish converting react to VS --- package-lock.json | 326 +++++++++ package.json | 2 + .../inlineDiffService/inlineDiffService.ts | 31 + .../contrib/void/browser/misc/build.js | 104 +-- .../contrib/void/browser/misc/extension.ts | 184 ----- .../void/browser/misc/sendLLMMessage.ts | 395 ---------- .../contrib/void/browser/misc/styles.css | 46 -- .../void/browser/misc/tailwind.config.js | 31 - .../contrib/void/browser/react/README.md | 2 +- .../browser/react/src/markdown/BlockCode.tsx | 41 ++ .../react/src/markdown/MarkdownRender.tsx | 221 ++++++ .../react/src/prompt/stringifyFiles.ts | 51 ++ .../{sidebar-tsx => prompt}/systemPrompts.ts | 12 +- .../browser/react/src/sidebar-tsx/Sidebar.tsx | 8 +- .../react/src/sidebar-tsx/SidebarChat.tsx | 690 +++++++++--------- .../react/src/sidebar-tsx/SidebarSettings.tsx | 11 +- .../src/sidebar-tsx/SidebarThreadSelector.tsx | 126 ++-- .../react/src/sidebar-tsx/shared_types.ts | 97 --- .../contrib/void/browser/react/src/styles.css | 40 + .../react/src/util/contextForServices.tsx | 36 +- .../browser/react/src/util/sendLLMMessage.ts | 395 ++++++++++ .../void/browser/react/tailwind.config.js | 24 +- .../contrib/void/browser/react/tsconfig.json | 11 + .../contrib/void/browser/registerConfig.ts | 91 ++- .../void/browser/registerInlineDiff.ts | 339 +++++++++ .../contrib/void/browser/registerSidebar.ts | 8 +- ...erThreadsHistory.ts => registerThreads.ts} | 110 +-- .../contrib/void/browser/void.contribution.ts | 2 +- 28 files changed, 2093 insertions(+), 1341 deletions(-) delete mode 100644 src/vs/workbench/contrib/void/browser/misc/extension.ts delete mode 100644 src/vs/workbench/contrib/void/browser/misc/sendLLMMessage.ts delete mode 100644 src/vs/workbench/contrib/void/browser/misc/styles.css delete mode 100644 src/vs/workbench/contrib/void/browser/misc/tailwind.config.js create mode 100644 src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx create mode 100644 src/vs/workbench/contrib/void/browser/react/src/markdown/MarkdownRender.tsx create mode 100644 src/vs/workbench/contrib/void/browser/react/src/prompt/stringifyFiles.ts rename src/vs/workbench/contrib/void/browser/react/src/{sidebar-tsx => prompt}/systemPrompts.ts (97%) delete mode 100644 src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/shared_types.ts create mode 100644 src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.ts create mode 100644 src/vs/workbench/contrib/void/browser/react/tsconfig.json create mode 100644 src/vs/workbench/contrib/void/browser/registerInlineDiff.ts rename src/vs/workbench/contrib/void/browser/{registerThreadsHistory.ts => registerThreads.ts} (66%) diff --git a/package-lock.json b/package-lock.json index 48f41e95..96102735 100644 --- a/package-lock.json +++ b/package-lock.json @@ -132,6 +132,7 @@ "istanbul-lib-source-maps": "^4.0.1", "istanbul-reports": "^3.1.5", "lazy.js": "^0.4.2", + "marked": "^15.0.0", "merge-options": "^1.0.1", "mime": "^1.4.1", "minimatch": "^3.0.4", @@ -153,6 +154,7 @@ "rcedit": "^1.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-syntax-highlighter": "^15.6.1", "rimraf": "^2.7.1", "scope-tailwind": "^1.0.1", "sinon": "^12.0.1", @@ -988,6 +990,19 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", @@ -2948,6 +2963,16 @@ "@types/vinyl": "*" } }, + "node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", @@ -3117,6 +3142,13 @@ "@types/node": "*" } }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/vinyl": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz", @@ -5675,6 +5707,39 @@ "node": ">=8" } }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -6156,6 +6221,17 @@ "node": ">=0.4.0" } }, + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/command-line-args": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", @@ -8274,6 +8350,20 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dev": true, + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -8766,6 +8856,15 @@ "dev": true, "license": "MIT" }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/formdata-node": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", @@ -11742,6 +11841,35 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -11751,6 +11879,23 @@ "he": "bin/he" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/highlightjs-vue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", + "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -12391,6 +12536,32 @@ "node": ">=0.10.0" } }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -12588,6 +12759,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-deflate": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-deflate/-/is-deflate-1.0.0.tgz", @@ -12704,6 +12886,17 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-interactive": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", @@ -13934,6 +14127,21 @@ "node": ">=8" } }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -14040,6 +14248,19 @@ "markdown-it": "bin/markdown-it.mjs" } }, + "node_modules/marked": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.0.tgz", + "integrity": "sha512-0mouKmBROJv/WSHJBPZZyYofUgawMChnD5je/g+aOBXsHDjb/IsnTQj7mnhQZu+qPJmRQ0ecX3mLGEUm3BgwYA==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", @@ -15971,6 +16192,25 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", @@ -17278,6 +17518,16 @@ "node": ">= 0.8" } }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -17302,6 +17552,20 @@ "node": ">=0.4.0" } }, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -17506,6 +17770,24 @@ "react": "^18.3.1" } }, + "node_modules/react-syntax-highlighter": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.1.tgz", + "integrity": "sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "highlightjs-vue": "^1.0.0", + "lowlight": "^1.17.0", + "prismjs": "^1.27.0", + "refractor": "^3.6.0" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -17706,6 +17988,39 @@ "node": ">= 0.10" } }, + "node_modules/refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/prismjs": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, + "license": "MIT" + }, "node_modules/regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -19161,6 +19476,17 @@ "deprecated": "See https://github.com/lydell/source-map-url#deprecated", "dev": true }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/sparkles": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", diff --git a/package.json b/package.json index 1cb61dfc..c363366e 100644 --- a/package.json +++ b/package.json @@ -194,6 +194,7 @@ "istanbul-lib-source-maps": "^4.0.1", "istanbul-reports": "^3.1.5", "lazy.js": "^0.4.2", + "marked": "^15.0.0", "merge-options": "^1.0.1", "mime": "^1.4.1", "minimatch": "^3.0.4", @@ -215,6 +216,7 @@ "rcedit": "^1.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-syntax-highlighter": "^15.6.1", "rimraf": "^2.7.1", "scope-tailwind": "^1.0.1", "sinon": "^12.0.1", diff --git a/src/vs/editor/browser/services/inlineDiffService/inlineDiffService.ts b/src/vs/editor/browser/services/inlineDiffService/inlineDiffService.ts index d49ed2b4..46608ccb 100644 --- a/src/vs/editor/browser/services/inlineDiffService/inlineDiffService.ts +++ b/src/vs/editor/browser/services/inlineDiffService/inlineDiffService.ts @@ -6,6 +6,37 @@ import { ICodeEditor, IViewZone } from '../../editorBrowser.js'; import { IRange } from '../../../common/core/range.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; + + + +// // an area that is currently being diffed +// type DiffArea = { +// diffareaid: number, +// startLine: number, +// endLine: number, +// originalStartLine: number, +// originalEndLine: number, +// sweepIndex: number | null // null iff not sweeping +// } + +// // the return type of diff creator +// type BaseDiff = { +// type: 'edit' | 'insertion' | 'deletion'; +// // repr: string; // representation of the diff in text +// originalRange: vscode.Range; +// originalCode: string; +// range: vscode.Range; +// code: string; +// } + +// // each diff on the user's screen +// type Diff = { +// diffid: number, +// lenses: vscode.CodeLens[], +// } & BaseDiff + + + export interface IInlineDiffService { readonly _serviceBrand: undefined; addDiff(editor: ICodeEditor, originalText: string, modifiedRange: IRange): void; diff --git a/src/vs/workbench/contrib/void/browser/misc/build.js b/src/vs/workbench/contrib/void/browser/misc/build.js index d1be3bb0..60699b7a 100644 --- a/src/vs/workbench/contrib/void/browser/misc/build.js +++ b/src/vs/workbench/contrib/void/browser/misc/build.js @@ -1,64 +1,66 @@ -const tailwindcss = require('tailwindcss') -const autoprefixer = require('autoprefixer') -const postcss = require('postcss') -const fs = require('fs') +// This is from the old repo -const convertTailwindToCSS = ({ from, to }) => { - console.log('converting ', from, ' --> ', to) +// const tailwindcss = require('tailwindcss') +// const autoprefixer = require('autoprefixer') +// const postcss = require('postcss') +// const fs = require('fs') - const original_css_contents = fs.readFileSync(from, 'utf8') +// const convertTailwindToCSS = ({ from, to }) => { +// console.log('converting ', from, ' --> ', to) - return postcss([ - tailwindcss, // this compiles tailwind of all the files specified in tailwind.config.json - autoprefixer, - ]) - .process(original_css_contents, { from, to }) - .then(processed_css_contents => { fs.writeFileSync(to, processed_css_contents.css) }) - .catch(error => { - console.error('Error in build-css:', error) - }) -} +// const original_css_contents = fs.readFileSync(from, 'utf8') + +// return postcss([ +// tailwindcss, // this compiles tailwind of all the files specified in tailwind.config.json +// autoprefixer, +// ]) +// .process(original_css_contents, { from, to }) +// .then(processed_css_contents => { fs.writeFileSync(to, processed_css_contents.css) }) +// .catch(error => { +// console.error('Error in build-css:', error) +// }) +// } -const esbuild = require('esbuild') +// const esbuild = require('esbuild') -const convertTSXtoJS = async ({ from, to }) => { - console.log('converting ', from, ' --> ', to) +// const convertTSXtoJS = async ({ from, to }) => { +// console.log('converting ', from, ' --> ', to) - return esbuild.build({ - entryPoints: [from], - bundle: true, - minify: true, - sourcemap: true, - outfile: to, - format: 'iife', // apparently iife is safe for browsers (safer than cjs) - platform: 'browser', - external: ['vscode'], - }).catch(() => process.exit(1)); -} +// return esbuild.build({ +// entryPoints: [from], +// bundle: true, +// minify: true, +// sourcemap: true, +// outfile: to, +// format: 'iife', // apparently iife is safe for browsers (safer than cjs) +// platform: 'browser', +// external: ['vscode'], +// }).catch(() => process.exit(1)); +// } -(async () => { - // convert tsx to js - await convertTSXtoJS({ - from: 'src/webviews/sidebar/index.tsx', - to: 'dist/webviews/sidebar/index.js', - }) +// (async () => { +// // convert tsx to js +// await convertTSXtoJS({ +// from: 'src/webviews/sidebar/index.tsx', +// to: 'dist/webviews/sidebar/index.js', +// }) - await convertTSXtoJS({ - from: 'src/webviews/ctrlk/index.tsx', - to: 'dist/webviews/ctrlk/index.js', - }) +// await convertTSXtoJS({ +// from: 'src/webviews/ctrlk/index.tsx', +// to: 'dist/webviews/ctrlk/index.js', +// }) - await convertTSXtoJS({ - from: 'src/webviews/diffline/index.tsx', - to: 'dist/webviews/diffline/index.js', - }) +// await convertTSXtoJS({ +// from: 'src/webviews/diffline/index.tsx', +// to: 'dist/webviews/diffline/index.js', +// }) - // convert tailwind to css - await convertTailwindToCSS({ - from: 'src/webviews/styles.css', - to: 'dist/webviews/styles.css', - }) +// // convert tailwind to css +// await convertTailwindToCSS({ +// from: 'src/webviews/styles.css', +// to: 'dist/webviews/styles.css', +// }) -})() +// })() diff --git a/src/vs/workbench/contrib/void/browser/misc/extension.ts b/src/vs/workbench/contrib/void/browser/misc/extension.ts deleted file mode 100644 index 813819f6..00000000 --- a/src/vs/workbench/contrib/void/browser/misc/extension.ts +++ /dev/null @@ -1,184 +0,0 @@ -// import * as vscode from 'vscode'; - -// import { v4 as uuidv4 } from 'uuid' -// import { AbortRef } from '../common/sendLLMMessage'; -// import { MessageToSidebar, MessageFromSidebar, DiffArea, ChatThreads } from '../common/shared_types'; -// import { getVoidConfigFromPartial } from '../webviews/common/contextForConfig'; -// import { DiffProvider } from '../../DiffProvider'; -// import { readFileContentOfUri } from './extensionLib/readFileContentOfUri'; -// import { SidebarWebviewProvider } from '../sidebar/SidebarWebviewProvider'; -// import { CtrlKWebviewProvider } from './providers/CtrlKWebviewProvider'; - -// const roundRangeToLines = (selection: vscode.Selection) => { -// let endLine = selection.end.character === 0 ? selection.end.line - 1 : selection.end.line // e.g. if the user triple clicks, it selects column=0, line=line -> column=0, line=line+1 -// return new vscode.Range(selection.start.line, 0, endLine, Number.MAX_SAFE_INTEGER) -// } - -// const getSelection = (editor: vscode.TextEditor) => { -// // get the range of the selection and the file the user is in -// const selectionRange = roundRangeToLines(editor.selection); -// const selectionStr = editor.document.getText(selectionRange).trim(); -// const filePath = editor.document.uri; -// return { selectionStr, filePath } -// } - -// export function activate(context: vscode.ExtensionContext) { - -// // 1. Mount the chat sidebar -// const sidebarWebviewProvider = new SidebarWebviewProvider(context); -// context.subscriptions.push( -// vscode.window.registerWebviewViewProvider(SidebarWebviewProvider.viewId, sidebarWebviewProvider, { webviewOptions: { retainContextWhenHidden: true } }) -// ); - -// // 1.5 -// const ctrlKWebviewProvider = new CtrlKWebviewProvider(context) - - -// // 2. ctrl+l -// context.subscriptions.push( -// vscode.commands.registerCommand('void.ctrl+l', () => { -// const editor = vscode.window.activeTextEditor -// if (!editor) return - -// // show the sidebar -// vscode.commands.executeCommand('workbench.view.extension.voidViewContainer'); -// // vscode.commands.executeCommand('vscode.moveViewToPanel', CustomViewProvider.viewId); // move to aux bar - -// const { selectionStr, filePath } = getSelection(editor) - -// // send message to the webview (Sidebar.tsx) -// sidebarWebviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+l', selection: { selectionStr, filePath } } satisfies MessageToSidebar)); -// }) -// ); - -// // 2.5: ctrl+k -// context.subscriptions.push( -// vscode.commands.registerCommand('void.ctrl+k', () => { -// console.log('CTRLK PRESSED') -// const editor = vscode.window.activeTextEditor -// if (!editor) return - -// const { selectionStr, filePath } = getSelection(editor) - -// // send message to the webview (Sidebar.tsx) -// // ctrlKWebviewProvider.onPressCtrlK() -// // sidebarWebviewProvider.webview.then(webview => webview.postMessage({ type: 'ctrl+k', selection: { selectionStr, filePath } } satisfies MessageToSidebar)); -// }) -// ); - -// // 3. Show an approve/reject codelens above each change -// const diffProvider = new DiffProvider(context); -// context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', diffProvider)); - -// // 4. Add approve/reject commands -// context.subscriptions.push(vscode.commands.registerCommand('void.acceptDiff', async (params) => { -// diffProvider.acceptDiff(params) -// })); -// context.subscriptions.push(vscode.commands.registerCommand('void.rejectDiff', async (params) => { -// diffProvider.rejectDiff(params) -// })); - -// // 5. Receive messages from sidebar -// sidebarWebviewProvider.webview.then( -// webview => { - -// // top navigation bar commands -// context.subscriptions.push(vscode.commands.registerCommand('void.startNewThread', async () => { -// webview.postMessage({ type: 'startNewThread' } satisfies MessageToSidebar) -// })) -// context.subscriptions.push(vscode.commands.registerCommand('void.toggleThreadSelector', async () => { -// webview.postMessage({ type: 'toggleThreadSelector' } satisfies MessageToSidebar) -// })) -// context.subscriptions.push(vscode.commands.registerCommand('void.toggleSettings', async () => { -// webview.postMessage({ type: 'toggleSettings' } satisfies MessageToSidebar) -// })); - -// // Receive messages in the extension from the sidebar webview (messages are sent using `postMessage`) -// webview.onDidReceiveMessage(async (m: MessageFromSidebar) => { - -// const abortApplyRef: AbortRef = { current: null } - -// if (m.type === 'requestFiles') { - -// // get contents of all file paths -// const files = await Promise.all( -// m.filepaths.map(async (filepath) => ({ filepath, content: await readFileContentOfUri(filepath) })) -// ) - -// // send contents to webview -// webview.postMessage({ type: 'files', files, } satisfies MessageToSidebar) - -// } -// else if (m.type === 'applyChanges') { - -// const editor = vscode.window.activeTextEditor -// if (!editor) { -// vscode.window.showInformationMessage('No active editor!') -// return -// } -// // create an area to show diffs -// const partialDiffArea: Omit = { -// startLine: 0, // in ctrl+L the start and end lines are the full document -// endLine: editor.document.lineCount, -// originalStartLine: 0, -// originalEndLine: editor.document.lineCount, -// sweepIndex: null, -// } -// const diffArea = diffProvider.createDiffArea(editor.document.uri, partialDiffArea, await readFileContentOfUri(editor.document.uri)) - -// const docUri = editor.document.uri -// const fileStr = await readFileContentOfUri(docUri) -// const voidConfig = getVoidConfigFromPartial(context.globalState.get('partialVoidConfig') ?? {}) - -// await diffProvider.startStreamingInDiffArea({ docUri, oldFileStr: fileStr, diffRepr: m.diffRepr, voidConfig, diffArea, abortRef: abortApplyRef }) -// } -// else if (m.type === 'getPartialVoidConfig') { -// const partialVoidConfig = context.globalState.get('partialVoidConfig') ?? {} -// webview.postMessage({ type: 'partialVoidConfig', partialVoidConfig } satisfies MessageToSidebar) -// } -// else if (m.type === 'persistPartialVoidConfig') { -// const partialVoidConfig = m.partialVoidConfig -// context.globalState.update('partialVoidConfig', partialVoidConfig) -// } -// else if (m.type === 'getAllThreads') { -// const threads: ChatThreads = context.workspaceState.get('allThreads') ?? {} -// webview.postMessage({ type: 'allThreads', threads } satisfies MessageToSidebar) -// } -// else if (m.type === 'persistThread') { -// const threads: ChatThreads = context.workspaceState.get('allThreads') ?? {} -// const updatedThreads: ChatThreads = { ...threads, [m.thread.id]: m.thread } -// context.workspaceState.update('allThreads', updatedThreads) -// } -// else if (m.type === 'getDeviceId') { -// let deviceId = context.globalState.get('void_deviceid') -// if (!deviceId || typeof deviceId !== 'string') { -// deviceId = uuidv4() -// context.globalState.update('void_deviceid', deviceId) -// } -// webview.postMessage({ type: 'deviceId', deviceId: deviceId as string } satisfies MessageToSidebar) -// } -// else { -// console.error('unrecognized command', m) -// } -// }) -// } -// ) - - - - -// // Gets called when user presses ctrl + k (mounts ctrl+k-style codelens) -// // TODO need to build this -// // const ctrlKCodeLensProvider = new CtrlKCodeLensProvider(); -// // context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', ctrlKCodeLensProvider)); -// // context.subscriptions.push( -// // vscode.commands.registerCommand('void.ctrl+k', () => { -// // const editor = vscode.window.activeTextEditor; -// // if (!editor) -// // return -// // ctrlKCodeLensProvider.addNewCodeLens(editor.document, editor.selection); -// // // vscode.commands.executeCommand('editor.action.showHover'); // apparently this refreshes the codelenses by having the internals call provideCodeLenses -// // }) -// // ) - -// } diff --git a/src/vs/workbench/contrib/void/browser/misc/sendLLMMessage.ts b/src/vs/workbench/contrib/void/browser/misc/sendLLMMessage.ts deleted file mode 100644 index 080e4c67..00000000 --- a/src/vs/workbench/contrib/void/browser/misc/sendLLMMessage.ts +++ /dev/null @@ -1,395 +0,0 @@ -// import Anthropic from '@anthropic-ai/sdk'; -// import OpenAI from 'openai'; -// import { Ollama } from 'ollama/browser' -// import { Content, GoogleGenerativeAI, GoogleGenerativeAIError, GoogleGenerativeAIFetchError } from '@google/generative-ai'; -// // import { VoidConfig } from '../webviews/common/contextForConfig' -// // import { captureEvent } from '../webviews/common/posthog'; -// // import { ChatMessage } from './shared_types'; - -// type VoidConfig = any - -// export type AbortRef = { current: (() => void) | null } - -// export type OnText = (newText: string, fullText: string) => void - -// export type OnFinalMessage = (input: string) => void - -// export type LLMMessageAnthropic = { -// role: 'user' | 'assistant'; -// content: string; -// } - -// export type LLMMessage = { -// role: 'system' | 'user' | 'assistant'; -// content: string; -// } - -// type SendLLMMessageFnTypeInternal = (params: { -// messages: LLMMessage[]; -// onText: OnText; -// onFinalMessage: OnFinalMessage; -// onError: (error: string) => void; -// voidConfig: VoidConfig; - -// _setAborter: (aborter: () => void) => void; -// }) => void - -// type SendLLMMessageFnTypeExternal = (params: { -// messages: LLMMessage[]; -// onText: OnText; -// onFinalMessage: (fullText: string) => void; -// onError: (error: string) => void; -// voidConfig: VoidConfig | null; -// abortRef: AbortRef; - -// logging: { -// loggingName: string, -// }; -// }) => void - -// const parseMaxTokensStr = (maxTokensStr: string) => { -// // parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN -// const int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr) -// if (Number.isNaN(int)) -// return undefined -// return int -// } - -// // Anthropic -// const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => { - -// const anthropic = new Anthropic({ apiKey: voidConfig.anthropic.apikey, dangerouslyAllowBrowser: true }); // defaults to process.env["ANTHROPIC_API_KEY"] - -// // find system messages and concatenate them -// const systemMessage = messages -// .filter(msg => msg.role === 'system') -// .map(msg => msg.content) -// .join('\n'); - -// // remove system messages for Anthropic -// const anthropicMessages = messages.filter(msg => msg.role !== 'system') as LLMMessageAnthropic[] - -// const stream = anthropic.messages.stream({ -// system: systemMessage, -// messages: anthropicMessages, -// model: voidConfig.anthropic.model, -// max_tokens: parseMaxTokensStr(voidConfig.default.maxTokens)!, // this might be undefined, but it will just throw an error for the user -// }); - - -// // when receive text -// stream.on('text', (newText, fullText) => { -// onText(newText, fullText) -// }) - -// // when we get the final message on this stream (or when error/fail) -// stream.on('finalMessage', (claude_response) => { -// // stringify the response's content -// const content = claude_response.content.map(c => c.type === 'text' ? c.text : c.type).join('\n'); -// onFinalMessage(content) -// }) - -// stream.on('error', (error) => { -// // the most common error will be invalid API key (401), so we handle this with a nice message -// if (error instanceof Anthropic.APIError && error.status === 401) { -// onError('Invalid API key.') -// } -// else { -// onError(error.message) -// } -// }) - -// // TODO need to test this to make sure it works, it might throw an error -// _setAborter(() => stream.controller.abort()) - -// }; - -// // Gemini -// const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => { - -// let fullText = '' - -// const genAI = new GoogleGenerativeAI(voidConfig.gemini.apikey); -// const model = genAI.getGenerativeModel({ model: voidConfig.gemini.model }); - -// // remove system messages that get sent to Gemini -// // str of all system messages -// const systemMessage = messages -// .filter(msg => msg.role === 'system') -// .map(msg => msg.content) -// .join('\n'); - -// // Convert messages to Gemini format -// const geminiMessages: Content[] = messages -// .filter(msg => msg.role !== 'system') -// .map((msg, i) => ({ -// parts: [{ text: msg.content }], -// role: msg.role === 'assistant' ? 'model' : 'user' -// })) - -// model.generateContentStream({ contents: geminiMessages, systemInstruction: systemMessage, }) -// .then(async response => { -// _setAborter(() => response.stream.return(fullText)) - -// for await (const chunk of response.stream) { -// const newText = chunk.text(); -// fullText += newText; -// onText(newText, fullText); -// } -// onFinalMessage(fullText); -// }) -// .catch((error) => { -// if (error instanceof GoogleGenerativeAIFetchError) { -// if (error.status === 400) { -// onError('Invalid API key.'); -// } -// else { -// onError(`${error.name}:\n${error.message}`); -// } -// } -// else { -// onError(error); -// } -// }) -// } - -// // OpenAI, OpenRouter, OpenAICompatible -// const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => { - -// let fullText = '' - -// let openai: OpenAI -// let options: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming - -// const maxTokens = parseMaxTokensStr(voidConfig.default.maxTokens) - -// if (voidConfig.default.whichApi === 'openAI') { -// openai = new OpenAI({ apiKey: voidConfig.openAI.apikey, dangerouslyAllowBrowser: true }); -// options = { model: voidConfig.openAI.model, messages: messages, stream: true, max_completion_tokens: maxTokens } -// } -// else if (voidConfig.default.whichApi === 'openRouter') { -// openai = new OpenAI({ -// baseURL: 'https://openrouter.ai/api/v1', apiKey: voidConfig.openRouter.apikey, dangerouslyAllowBrowser: true, -// defaultHeaders: { -// 'HTTP-Referer': 'https://voideditor.com', // Optional, for including your app on openrouter.ai rankings. -// 'X-Title': 'Void Editor', // Optional. Shows in rankings on openrouter.ai. -// }, -// }); -// options = { model: voidConfig.openRouter.model, messages: messages, stream: true, max_completion_tokens: maxTokens } -// } -// else if (voidConfig.default.whichApi === 'openAICompatible') { -// openai = new OpenAI({ baseURL: voidConfig.openAICompatible.endpoint, apiKey: voidConfig.openAICompatible.apikey, dangerouslyAllowBrowser: true }) -// options = { model: voidConfig.openAICompatible.model, messages: messages, stream: true, max_completion_tokens: maxTokens } -// } -// else { -// console.error(`sendOpenAIMsg: invalid whichApi: ${voidConfig.default.whichApi}`) -// throw new Error(`voidConfig.whichAPI was invalid: ${voidConfig.default.whichApi}`) -// } - -// openai.chat.completions -// .create(options) -// .then(async response => { -// _setAborter(() => response.controller.abort()) -// // when receive text -// for await (const chunk of response) { -// const newText = chunk.choices[0]?.delta?.content || ''; -// fullText += newText; -// onText(newText, fullText); -// } -// onFinalMessage(fullText); -// }) -// // when error/fail - this catches errors of both .create() and .then(for await) -// .catch(error => { -// if (error instanceof OpenAI.APIError) { -// if (error.status === 401) { -// onError('Invalid API key.'); -// } -// else { -// onError(`${error.name}:\n${error.message}`); -// } -// } -// else { -// onError(error); -// } -// }) - -// }; - -// // Ollama -// export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => { - -// let fullText = '' - -// const ollama = new Ollama({ host: voidConfig.ollama.endpoint }) - -// ollama.chat({ -// model: voidConfig.ollama.model, -// messages: messages, -// stream: true, -// options: { num_predict: parseMaxTokensStr(voidConfig.default.maxTokens) } // this is max_tokens -// }) -// .then(async stream => { -// _setAborter(() => stream.abort()) -// // iterate through the stream -// for await (const chunk of stream) { -// const newText = chunk.message.content; -// fullText += newText; -// onText(newText, fullText); -// } -// onFinalMessage(fullText); - -// }) -// // when error/fail -// .catch(error => { -// onError(error) -// }) - -// }; - -// // Greptile -// // https://docs.greptile.com/api-reference/query -// // https://docs.greptile.com/quickstart#sample-response-streamed - -// const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => { - -// let fullText = '' - -// fetch('https://api.greptile.com/v2/query', { -// method: 'POST', -// headers: { -// 'Authorization': `Bearer ${voidConfig.greptile.apikey}`, -// 'X-Github-Token': `${voidConfig.greptile.githubPAT}`, -// 'Content-Type': `application/json`, -// }, -// body: JSON.stringify({ -// messages, -// stream: true, -// repositories: [voidConfig.greptile.repoinfo], -// }), -// }) -// // this is {message}\n{message}\n{message}...\n -// .then(async response => { -// const text = await response.text() -// console.log('got greptile', text) -// return JSON.parse(`[${text.trim().split('\n').join(',')}]`) -// }) -// // TODO make this actually stream, right now it just sends one message at the end -// // TODO add _setAborter() when add streaming -// .then(async responseArr => { - -// for (const response of responseArr) { -// const type: string = response['type'] -// const message = response['message'] - -// // when receive text -// if (type === 'message') { -// fullText += message -// onText(message, fullText) -// } -// else if (type === 'sources') { -// const { filepath, linestart: _, lineend: _2 } = message as { filepath: string; linestart: number | null; lineend: number | null } -// fullText += filepath -// onText(filepath, fullText) -// } -// // type: 'status' with an empty 'message' means last message -// else if (type === 'status') { -// if (!message) { -// onFinalMessage(fullText) -// } -// } -// } - -// }) -// .catch(e => { -// onError(e) -// }); - -// } - - - - - -// export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ -// messages, -// onText: onText_, -// onFinalMessage: onFinalMessage_, -// onError: onError_, -// abortRef: abortRef_, -// voidConfig, -// logging: { loggingName } -// }) => { -// if (!voidConfig) return; - -// // trim message content (Anthropic and other providers give an error if there is trailing whitespace) -// messages = messages.map(m => ({ ...m, content: m.content.trim() })) - -// // only captures number of messages and message "shape", no actual code, instructions, prompts, etc -// const captureChatEvent = (eventId: string, extras?: object) => { -// // captureEvent(eventId, { -// // whichApi: voidConfig.default['whichApi'], -// // numMessages: messages?.length, -// // messagesShape: messages?.map(msg => ({ role: msg.role, length: msg.content.length })), -// // version: '2024-11-02', -// // ...extras, -// // }) -// } -// const submit_time = new Date() - -// let _fullTextSoFar = '' -// let _aborter: (() => void) | null = null -// let _setAborter = (fn: () => void) => { _aborter = fn } -// let _didAbort = false - -// const onText = (newText: string, fullText: string) => { -// if (_didAbort) return -// onText_(newText, fullText) -// _fullTextSoFar = fullText -// } - -// const onFinalMessage = (fullText: string) => { -// if (_didAbort) return -// captureChatEvent(`${loggingName} - Received Full Message`, { messageLength: fullText.length, duration: new Date().getMilliseconds() - submit_time.getMilliseconds() }) -// onFinalMessage_(fullText) -// } - -// const onError = (error: string) => { -// if (_didAbort) return -// captureChatEvent(`${loggingName} - Error`, { error }) -// onError_(error) -// } - -// const onAbort = () => { -// captureChatEvent(`${loggingName} - Abort`, { messageLengthSoFar: _fullTextSoFar.length }) -// _aborter?.() -// _didAbort = true -// } -// abortRef_.current = onAbort - -// captureChatEvent(`${loggingName} - Sending Message`, { messageLength: messages[messages.length - 1]?.content.length }) - -// switch (voidConfig.default.whichApi) { -// case 'anthropic': -// sendAnthropicMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, }); -// break; -// case 'openAI': -// case 'openRouter': -// case 'openAICompatible': -// sendOpenAIMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, }); -// break; -// case 'gemini': -// sendGeminiMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, }); -// break; -// case 'ollama': -// sendOllamaMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, }); -// break; -// case 'greptile': -// sendGreptileMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, }); -// break; -// default: -// onError(`Error: whichApi was ${voidConfig.default.whichApi}, which is not recognized!`) -// break; -// } - - -// } diff --git a/src/vs/workbench/contrib/void/browser/misc/styles.css b/src/vs/workbench/contrib/void/browser/misc/styles.css deleted file mode 100644 index 2b838c73..00000000 --- a/src/vs/workbench/contrib/void/browser/misc/styles.css +++ /dev/null @@ -1,46 +0,0 @@ -/* all the styles are shared right now between all webviews */ - -@tailwind base; -@tailwind components; -@tailwind utilities; - -html { - font-size: var(--vscode-font-size); -} - -.btn { - @apply cursor-pointer transition-colors; - - &.btn-primary { - @apply bg-vscode-button-bg text-vscode-button-fg; - - &:not(:disabled) { - @apply hover:bg-vscode-button-hoverBg; - } - } - - &.btn-sm { - @apply px-3 py-1 text-sm; - } - - &.btn-secondary { - @apply bg-vscode-button-secondary-bg text-vscode-button-secondary-fg; - - &:not(:disabled) { - @apply hover:bg-vscode-button-secondary-hoverBg; - } - } - - /* add transparency when disabled */ - &:disabled { - @apply opacity-75 cursor-not-allowed; - } -} - -.input { - @apply bg-vscode-input-bg text-vscode-input-fg border-vscode-input-border focus:outline-vscode-focus-border; -} - -.dropdown { - @apply bg-vscode-dropdown-bg text-vscode-dropdown-foreground border-vscode-dropdown-border focus:outline-vscode-focus-border; -} diff --git a/src/vs/workbench/contrib/void/browser/misc/tailwind.config.js b/src/vs/workbench/contrib/void/browser/misc/tailwind.config.js deleted file mode 100644 index b50e4207..00000000 --- a/src/vs/workbench/contrib/void/browser/misc/tailwind.config.js +++ /dev/null @@ -1,31 +0,0 @@ -/** @type {import('tailwindcss').Config} */ - -// inject user's vscode theme colors: https://code.visualstudio.com/api/extension-guides/webview#theming-webview-content -module.exports = { - content: ["./src/webviews/**/*.{html,js,ts,jsx,tsx}"], - theme: { - extend: { - colors: { - vscode: { - "sidebar-bg": "var(--vscode-sideBar-background)", - "editor-bg": "var(--vscode-editor-background)", - "editor-fg": "var(--vscode-editor-foreground)", - "input-bg": "var(--vscode-input-background)", - "input-fg": "var(--vscode-input-foreground)", - "input-border": "var(--vscode-input-border)", - "button-fg": "var(--vscode-button-foreground)", - "button-bg": "var(--vscode-button-background)", - "button-hoverBg": "var(--vscode-button-hoverBackground)", - "button-secondary-fg": "var(--vscode-button-secondaryForeground)", - "button-secondary-bg": "var(--vscode-button-secondaryBackground)", - "button-secondary-hoverBg": "var(--vscode-button-secondaryHoverBackground)", - "dropdown-bg": "var(--vscode-settings-dropdownBackground)", - "dropdown-foreground": "var(--vscode-settings-dropdownForeground)", - "dropdown-border": "var(--vscode-settings-dropdownBorder)", - "focus-border": "var(--vscode-focusBorder)", - }, - }, - }, - }, - plugins: [], -}; diff --git a/src/vs/workbench/contrib/void/browser/react/README.md b/src/vs/workbench/contrib/void/browser/react/README.md index 11b8d253..e6bfee13 100644 --- a/src/vs/workbench/contrib/void/browser/react/README.md +++ b/src/vs/workbench/contrib/void/browser/react/README.md @@ -3,7 +3,7 @@ Run `node build.js` to compile the React into `out/`. A couple things to remember: -- Make sure to add .js at the end of any external imports used in here. +- Make sure to add .js at the end of any external imports used in here, e.g. ../../../../../my_file.js. If you don't do this, you will get untraceable errors. - src/ needs to be shallow so the detection of externals works properly (see tsup.config.js). diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx new file mode 100644 index 00000000..6b34342d --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/BlockCode.tsx @@ -0,0 +1,41 @@ +import React, { ReactNode, useCallback, useEffect, useState } from "react" +import SyntaxHighlighter from "react-syntax-highlighter"; +import { atomOneDarkReasonable } from "react-syntax-highlighter/dist/esm/styles/hljs"; + + +export const BlockCode = ({ text, buttonsOnHover, language }: { text: string, buttonsOnHover?: ReactNode, language?: string }) => { + + const customStyle = { + ...atomOneDarkReasonable, + 'code[class*="language-"]': { + ...atomOneDarkReasonable['code[class*="language-"]'], + background: "none", + }, + } + + return (<> +
+ + {!toolbar ? null : ( +
+
{buttonsOnHover === null ? null : buttonsOnHover}
+
+ )} + +
+ + {text} + + +
+
+ + ) +} + diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/MarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/MarkdownRender.tsx new file mode 100644 index 00000000..2f141c48 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/MarkdownRender.tsx @@ -0,0 +1,221 @@ +import React, { JSX, useCallback, useEffect, useState } from "react" +import { marked, MarkedToken, Token, TokensList } from "marked" +import { BlockCode } from "./BlockCode.js" + + +enum CopyButtonState { + Copy = "Copy", + Copied = "Copied!", + Error = "Could not copy", +} + +const COPY_FEEDBACK_TIMEOUT = 1000 // amount of time to say 'Copied!' + +const CodeButtonsOnHover = ({ diffRepr: text }: { diffRepr: string }) => { + const [copyButtonState, setCopyButtonState] = useState(CopyButtonState.Copy) + + useEffect(() => { + if (copyButtonState !== CopyButtonState.Copy) { + setTimeout(() => { + setCopyButtonState(CopyButtonState.Copy) + }, COPY_FEEDBACK_TIMEOUT) + } + }, [copyButtonState]) + + const onCopy = useCallback(() => { + navigator.clipboard.writeText(text).then( + () => { + setCopyButtonState(CopyButtonState.Copied) + }, + () => { + setCopyButtonState(CopyButtonState.Error) + } + ) + }, [text]) + + return <> + + + +} + + +const RenderToken = ({ token, nested = false }: { token: Token | string, nested?: boolean }): JSX.Element => { + + // deal with built-in tokens first (assume marked token) + const t = token as MarkedToken + + if (t.type === "space") { + return {t.raw} + } + + if (t.type === "code") { + return } + /> + } + + if (t.type === "heading") { + const HeadingTag = `h${t.depth}` as keyof JSX.IntrinsicElements + return {t.text} + } + + if (t.type === "table") { + return ( + + + + {t.header.map((cell: any, index: number) => ( + + ))} + + + + {t.rows.map((row: any[], rowIndex: number) => ( + + {row.map((cell: any, cellIndex: number) => ( + + ))} + + ))} + +
+ {cell.raw} +
+ {cell.raw} +
+ ) + } + + if (t.type === "hr") { + return
+ } + + if (t.type === "blockquote") { + return
{t.text}
+ } + + if (t.type === "list") { + const ListTag = t.ordered ? "ol" : "ul" + return ( + + {t.items.map((item, index) => ( +
  • + {item.task && ( + + )} + +
  • + ))} +
    + ) + } + + if (t.type === "paragraph") { + const contents = <> + {t.tokens.map((token, index) => ( + + ))} + + if (nested) + return contents + return

    {contents}

    + } + + // don't actually render tags, just render strings of them + if (t.type === "html") { + return ( +
    +				{``}
    +				{t.raw}
    +				{``}
    +			
    + ) + } + + if (t.type === "text" || t.type === "escape") { + return {t.raw} + } + + if (t.type === "def") { + return <> // Definitions are typically not rendered + } + + if (t.type === "link") { + return ( + + {t.text} + + ) + } + + if (t.type === "image") { + return {t.text} + } + + if (t.type === "strong") { + return {t.text} + } + + if (t.type === "em") { + return {t.text} + } + + // inline code + if (t.type === "codespan") { + return ( + + {t.text} + + ) + } + + if (t.type === "br") { + return
    + } + + // strikethrough + if (t.type === "del") { + return {t.text} + } + + // default + return ( +
    + Unknown type: + {t.raw} +
    + ) +} + +export const MarkdownRender = ({ string, nested = false }: { string: string, nested?: boolean }) => { + const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer + return ( + <> + {tokens.map((token, index) => ( + + ))} + + ) +} + diff --git a/src/vs/workbench/contrib/void/browser/react/src/prompt/stringifyFiles.ts b/src/vs/workbench/contrib/void/browser/react/src/prompt/stringifyFiles.ts new file mode 100644 index 00000000..42d2fd39 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/prompt/stringifyFiles.ts @@ -0,0 +1,51 @@ + +import { ChatFile, ChatCodeSelection } from '../sidebar-tsx/SidebarChat.js'; + +export const filesStr = (fullFiles: ChatFile[]) => { + return fullFiles.map(({ filepath, content }) => + ` +${filepath.fsPath} +\`\`\` +${content} +\`\`\``).join('\n') +} + + +export const userInstructionsStr = (instructions: string, files: ChatFile[], selection: ChatCodeSelection | null) => { + let str = ''; + + if (files.length > 0) { + str += filesStr(files); + } + + if (selection) { + str += ` +I am currently selecting this code: +\t\`\`\`${selection.selectionStr}\`\`\` +`; + } + + if (files.length > 0 && selection) { + str += ` +Please edit the selected code or the entire file following these instructions: +`; + } else if (files.length > 0) { + str += ` +Please edit the file following these instructions: +`; + } else if (selection) { + str += ` +Please edit the selected code following these instructions: +`; + } + + str += ` +\t${instructions} +`; + if (files.length > 0) { + str += ` +\tIf you make a change, rewrite the entire file. +`; // TODO don't rewrite the whole file on prompt, instead rewrite it when click Apply + } + return str; +}; diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/systemPrompts.ts b/src/vs/workbench/contrib/void/browser/react/src/prompt/systemPrompts.ts similarity index 97% rename from src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/systemPrompts.ts rename to src/vs/workbench/contrib/void/browser/react/src/prompt/systemPrompts.ts index 7e443053..107a4d56 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/systemPrompts.ts +++ b/src/vs/workbench/contrib/void/browser/react/src/prompt/systemPrompts.ts @@ -1,5 +1,5 @@ -const generateDiffInstructions = ` +export const generateDiffInstructions = ` You are a coding assistant. You are given a list of relevant files \`files\`, a selection that the user is making \`selection\`, and instructions to follow \`instructions\`. Please edit the selected file following the user's instructions (or, if appropriate, answer their question instead). @@ -160,7 +160,7 @@ We should change all the buttons like the one selected into a div component. Her `; -const searchDiffChunkInstructions = ` +export const searchDiffChunkInstructions = ` You are a coding assistant that applies a diff to a file. You are given a diff \`diff\`, a list of files \`files\` to apply the diff to, and a selection \`selection\` that you are currently considering in the file. Determine whether you should modify ANY PART of the selection \`selection\` following the \`diff\`. Return \`true\` if you should modify any part of the selection, and \`false\` if you should not modify any part of it. @@ -269,7 +269,7 @@ OUTPUT ` -const writeFileWithDiffInstructions = ` +export const writeFileWithDiffInstructions = ` You are a coding assistant that applies a diff to a file. You are given the original file \`original_file\`, a diff \`diff\`, and a new file that you are applying the diff to \`new_file\`. Please finish writing the new file \`new_file\`, according to the diff \`diff\`. @@ -398,9 +398,3 @@ export default Sidebar;\`\`\` ` - -export { - generateDiffInstructions, - searchDiffChunkInstructions, - writeFileWithDiffInstructions, -}; \ No newline at end of file diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx index c1340fef..f1f6a073 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx @@ -2,19 +2,15 @@ import React, { useEffect, useState } from 'react' import { mountFnGenerator } from '../util/mountFnGenerator' import { SidebarSettings } from './SidebarSettings.js'; -import { useServices } from '../util/contextForServices.js'; -import { IVoidSidebarStateService, VoidSidebarState } from '../../../registerSidebar.js'; +import { useSidebarState } from '../util/contextForServices.js'; // import { SidebarThreadSelector } from './SidebarThreadSelector.js'; // import { SidebarChat } from './SidebarChat.js'; import '../styles.css' const Sidebar = () => { - // state should come from sidebarStateService - const { sidebarStateService } = useServices() - const [sidebarState, setSideBarState] = useState(sidebarStateService.state) + const [sidebarState, sidebarStateService] = useSidebarState() const { isHistoryOpen, currentTab: tab } = sidebarState - useEffect(() => { sidebarStateService.onDidChangeState(() => setSideBarState(sidebarStateService.state)) }, [sidebarStateService]) return
    diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 4558828b..f6c6ae85 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -1,355 +1,339 @@ -// import React, { FormEvent, useCallback, useRef, useState } from "react"; - - -// sidebarStateService.onDidFocusChat(() => {}) -// sidebarStateService.onDidBlurChat(() => {}) - - - -// import MarkdownRender from "../../sidebar/markdown/!MarkdownRender"; -// import BlockCode from "../../sidebar/markdown/!BlockCode"; -// import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation'; - - -// const filesStr = (fullFiles: File[]) => { -// return fullFiles.map(({ filepath, content }) => -// ` -// ${filepath.fsPath} -// \`\`\` -// ${content} -// \`\`\``).join('\n') -// } - -// const userInstructionsStr = (instructions: string, files: File[], selection: CodeSelection | null) => { -// let str = ''; - -// if (files.length > 0) { -// str += filesStr(files); -// } - -// if (selection) { -// str += ` -// I am currently selecting this code: -// \t\`\`\`${selection.selectionStr}\`\`\` -// `; -// } - -// if (files.length > 0 && selection) { -// str += ` -// Please edit the selected code or the entire file following these instructions: -// `; -// } else if (files.length > 0) { -// str += ` -// Please edit the file following these instructions: -// `; -// } else if (selection) { -// str += ` -// Please edit the selected code following these instructions: -// `; -// } - -// str += ` -// \t${instructions} -// `; -// if (files.length > 0) { -// str += ` -// \tIf you make a change, rewrite the entire file. -// `; // TODO don't rewrite the whole file on prompt, instead rewrite it when click Apply -// } -// return str; -// }; - - - - - -// const getBasename = (pathStr: string) => { -// // "unixify" path -// pathStr = pathStr.replace(/[/\\]+/g, "/") // replace any / or \ or \\ with / -// const parts = pathStr.split("/") // split on / -// return parts[parts.length - 1] -// } - -// export const SelectedFiles = ({ files, setFiles, }: { files: vscode.Uri[], setFiles: null | ((files: vscode.Uri[]) => void) }) => { -// return ( -// files.length !== 0 && ( -//
    -// {files.map((filename, i) => ( -// -// ))} -//
    -// ) -// ) -// } - - -// const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => { - -// const role = chatMessage.role -// const children = chatMessage.displayContent - -// if (!children) -// return null - -// let chatbubbleContents: React.ReactNode - -// if (role === 'user') { -// chatbubbleContents = <> -// -// {chatMessage.selection?.selectionStr && } -// {children} -// -// } -// else if (role === 'assistant') { -// chatbubbleContents = // sectionsHTML -// } - -// return
    -//
    -// {chatbubbleContents} -//
    -//
    -// } - - - -// export const SidebarChat = ({ chatInputRef }: { chatInputRef: React.RefObject }) => { - -// // // if they pressed the + to add a new chat -// // useOnVSCodeMessage('startNewThread', (m) => { -// // const allThreads = getAllThreads() -// // // find a thread with 0 messages and switch to it -// // for (let threadId in allThreads) { -// // if (allThreads[threadId].messages.length === 0) { -// // switchToThread(threadId) -// // return -// // } -// // } -// // // start a new thread -// // startNewThread() -// // }) - -// // // if user pressed ctrl+l, add their selection to the sidebar -// // useOnVSCodeMessage('ctrl+l', (m) => { -// // setSelection(m.selection) -// // const filepath = m.selection.filePath - -// // // add current file to the context if it's not already in the files array -// // if (!files.find(f => f.fsPath === filepath.fsPath)) -// // setFiles(files => [...files, filepath]) -// // }) - -// // state of current message -// const [selection, setSelection] = useState(null) // the code the user is selecting -// const [files, setFiles] = useState([]) // the names of the files in the chat -// const [instructions, setInstructions] = useState('') // the user's instructions - -// // state of chat -// const [messageStream, setMessageStream] = useState('') -// const [isLoading, setIsLoading] = useState(false) -// const abortFnRef = useRef<(() => void) | null>(null) - -// const [latestError, setLatestError] = useState('') - -// // higher level state -// const { getAllThreads, getCurrentThread, addMessageToHistory, startNewThread, switchToThread } = useThreads() - -// const { voidConfig } = useVoidConfig() - - - - - -// const isDisabled = !instructions - -// const formRef = useRef(null) -// const onSubmit = async (e: FormEvent) => { - -// e.preventDefault() -// if (isDisabled) return -// if (isLoading) return - -// setIsLoading(true) -// setInstructions(''); -// formRef.current?.reset(); // reset the form's text when clear instructions or unexpected behavior happens -// setSelection(null) -// setFiles([]) -// setLatestError('') - -// // request file content from vscode and await response -// getVSCodeAPI().postMessage({ type: 'requestFiles', filepaths: files }) -// const relevantFiles = await awaitVSCodeResponse('files') - -// // add system message to chat history -// const systemPromptElt: ChatMessage = { role: 'system', content: generateDiffInstructions } -// addMessageToHistory(systemPromptElt) - -// const userContent = userInstructionsStr(instructions, relevantFiles.files, selection) -// const newHistoryElt: ChatMessage = { role: 'user', content: userContent, displayContent: instructions, selection, files } -// addMessageToHistory(newHistoryElt) - -// // send message to LLM -// sendLLMMessage({ -// logging: { loggingName: 'Chat' }, -// messages: [...(getCurrentThread()?.messages ?? []).map(m => ({ role: m.role, content: m.content })),], -// onText: (newText, fullText) => setMessageStream(fullText), -// onFinalMessage: (content) => { - -// // add assistant's message to chat history, and clear selection -// const newHistoryElt: ChatMessage = { role: 'assistant', content, displayContent: content } -// addMessageToHistory(newHistoryElt) -// setMessageStream('') -// setIsLoading(false) -// }, -// onError: (error) => { -// // add assistant's message to chat history, and clear selection -// let content = messageStream; // just use the current content -// const newHistoryElt: ChatMessage = { role: 'assistant', content, displayContent: content, } -// addMessageToHistory(newHistoryElt) -// setMessageStream('') -// setIsLoading(false) - -// setLatestError(error) -// }, -// voidConfig, -// abortRef: abortFnRef, -// }) - - -// } - -// const onAbort = useCallback(() => { -// // abort claude -// abortFnRef.current?.() - -// // if messageStream was not empty, add it to the history -// const llmContent = messageStream || '(null)' -// const newHistoryElt: ChatMessage = { role: 'assistant', content: llmContent, displayContent: messageStream, } -// addMessageToHistory(newHistoryElt) - -// setMessageStream('') -// setIsLoading(false) - -// }, [messageStream, addMessageToHistory]) - - -// return <> -//
    -// {/* previous messages */} -// {getCurrentThread() !== null && getCurrentThread()?.messages.map((message, i) => -// -// )} -// {/* message stream */} -// -//
    -// {/* chatbar */} -//
    -// {/* selection */} -//
    -//
    -//
    -// {/* selection */} -// {(files.length || selection?.selectionStr) &&
    -// {/* selected files */} -// -// {/* selected code */} -// {!!selection?.selectionStr && ( -// setSelection(null)} -// className="btn btn-secondary btn-sm border border-vscode-input-border rounded" -// > -// Remove -// -// )} /> -// )} -//
    } - -//
    { if (e.key === 'Enter' && !e.shiftKey) onSubmit(e) }} - -// onSubmit={(e) => { -// console.log('submit!') -// onSubmit(e) -// }}> -// {/* input */} - -//