mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 22:40:26 +01:00
Better code splitting and removed final instances of react-dnd
This commit is contained in:
148
package-lock.json
generated
148
package-lock.json
generated
@@ -1969,24 +1969,6 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-dnd/asap": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz",
|
||||
"integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-dnd/invariant": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz",
|
||||
"integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-dnd/shallowequal": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz",
|
||||
"integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@replit/codemirror-emacs": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-emacs/-/codemirror-emacs-6.1.0.tgz",
|
||||
@@ -2854,6 +2836,7 @@
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/container-queries/-/container-queries-0.1.1.tgz",
|
||||
"integrity": "sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=3.2.0"
|
||||
@@ -2873,9 +2856,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/history": {
|
||||
"version": "1.121.34",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.121.34.tgz",
|
||||
"integrity": "sha512-YL8dGi5ZU+xvtav2boRlw4zrRghkY6hvdcmHhA0RGSJ/CBgzv+cbADW9eYJLx74XMZvIQ1pp6VMbrpXnnM5gHA==",
|
||||
"version": "1.133.3",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.133.3.tgz",
|
||||
"integrity": "sha512-zFQnGdX0S4g5xRuS+95iiEXM+qlGvYG7ksmOKx7LaMv60lDWa0imR8/24WwXXvBWJT1KnwVdZcjvhCwz9IiJCw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -2886,9 +2869,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.83.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.0.tgz",
|
||||
"integrity": "sha512-0M8dA+amXUkyz5cVUm/B+zSk3xkQAcuXuz5/Q/LveT4ots2rBpPTZOzd7yJa2Utsf8D2Upl5KyjhHRY+9lB/XA==",
|
||||
"version": "5.90.5",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.5.tgz",
|
||||
"integrity": "sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2896,12 +2879,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.83.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.83.0.tgz",
|
||||
"integrity": "sha512-/XGYhZ3foc5H0VM2jLSD/NyBRIOK4q9kfeml4+0x2DlL6xVuAcVEW+hTlTapAmejObg0i3eNqhkr2dT+eciwoQ==",
|
||||
"version": "5.90.5",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.5.tgz",
|
||||
"integrity": "sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.83.0"
|
||||
"@tanstack/query-core": "5.90.5"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2912,14 +2895,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-router": {
|
||||
"version": "1.127.3",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.127.3.tgz",
|
||||
"integrity": "sha512-QprmWHJrGbEKXJiP7WZ+dilTJRc7nMbsFCUnfAUw8PsOYanhgvBkBwAU05YEo8WTIZ9atCc1R90hyzqbiBFkdA==",
|
||||
"version": "1.133.13",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.133.13.tgz",
|
||||
"integrity": "sha512-mVAj70mPOH/a60Hjlha3gHEWLFuE4kHeKau/AL5Xp6e5GtNk1JTRwN4sJ9QlSyLcClOUUtGfED1FoLj0D2W0Eg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/history": "1.121.34",
|
||||
"@tanstack/history": "1.133.3",
|
||||
"@tanstack/react-store": "^0.7.0",
|
||||
"@tanstack/router-core": "1.127.3",
|
||||
"@tanstack/router-core": "1.133.13",
|
||||
"isbot": "^5.1.22",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
"tiny-warning": "^1.0.3"
|
||||
@@ -2972,14 +2955,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/router-core": {
|
||||
"version": "1.127.3",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.127.3.tgz",
|
||||
"integrity": "sha512-08JlfwsMIDkMyCQsRviMVBn0cVUzlNzkll4pZgf6QRSO1RASBsci5hMojcsdH0d/yXLH0FBJ6fINbj0ctBm63Q==",
|
||||
"version": "1.133.13",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.133.13.tgz",
|
||||
"integrity": "sha512-zZptdlS/wSkqozb07Y3zX5gas2OapJdjEG6/Id0e/twNefVdR4EY2TK/mgvyhHtKIpCxIcnZz/3opypgeQi9bg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/history": "1.121.34",
|
||||
"@tanstack/history": "1.133.3",
|
||||
"@tanstack/store": "^0.7.0",
|
||||
"cookie-es": "^1.2.2",
|
||||
"cookie-es": "^2.0.0",
|
||||
"seroval": "^1.3.2",
|
||||
"seroval-plugins": "^1.3.2",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
@@ -5915,9 +5898,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cookie-es": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz",
|
||||
"integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-2.0.0.tgz",
|
||||
"integrity": "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/copy-descriptor": {
|
||||
@@ -6707,17 +6690,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dnd-core": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz",
|
||||
"integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-dnd/asap": "^5.0.1",
|
||||
"@react-dnd/invariant": "^4.0.1",
|
||||
"redux": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/doctrine": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
||||
@@ -9273,15 +9245,6 @@
|
||||
"@babel/runtime": "^7.7.6"
|
||||
}
|
||||
},
|
||||
"node_modules/hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hosted-git-info": {
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
@@ -14421,46 +14384,6 @@
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dnd": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz",
|
||||
"integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-dnd/invariant": "^4.0.1",
|
||||
"@react-dnd/shallowequal": "^4.0.1",
|
||||
"dnd-core": "^16.0.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"hoist-non-react-statics": "^3.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/hoist-non-react-statics": ">= 3.3.1",
|
||||
"@types/node": ">= 12",
|
||||
"@types/react": ">= 16",
|
||||
"react": ">= 16.14"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/hoist-non-react-statics": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-dnd-touch-backend": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd-touch-backend/-/react-dnd-touch-backend-16.0.1.tgz",
|
||||
"integrity": "sha512-NonoCABzzjyWGZuDxSG77dbgMZ2Wad7eQiCd/ECtsR2/NBLTjGksPUx9UPezZ1nQ/L7iD130Tz3RUshL/ClKLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-dnd/invariant": "^4.0.1",
|
||||
"dnd-core": "^16.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
@@ -14479,6 +14402,7 @@
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-markdown": {
|
||||
@@ -14847,15 +14771,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/redux": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect.getprototypeof": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
||||
@@ -16907,6 +16822,7 @@
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz",
|
||||
"integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
@@ -18864,7 +18780,7 @@
|
||||
},
|
||||
"packages/plugin-runtime-types": {
|
||||
"name": "@yaakapp/api",
|
||||
"version": "0.6.6",
|
||||
"version": "0.7.0",
|
||||
"dependencies": {
|
||||
"@types/node": "^24.0.13"
|
||||
},
|
||||
@@ -19136,10 +19052,9 @@
|
||||
"@replit/codemirror-emacs": "^6.1.0",
|
||||
"@replit/codemirror-vim": "^6.3.0",
|
||||
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tanstack/react-query": "^5.76.1",
|
||||
"@tanstack/react-router": "^1.120.3",
|
||||
"@tanstack/react-virtual": "^3.13.8",
|
||||
"@tanstack/react-query": "^5.90.5",
|
||||
"@tanstack/react-router": "^1.133.13",
|
||||
"@tanstack/react-virtual": "^3.13.12",
|
||||
"@tauri-apps/api": "^2.8.0",
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.3.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.4.0",
|
||||
@@ -19169,8 +19084,6 @@
|
||||
"parse-color": "^1.0.0",
|
||||
"react": "^19.1.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-touch-backend": "^16.0.1",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-pdf": "^10.0.1",
|
||||
@@ -19187,6 +19100,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lezer/generator": "^1.8.0",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
|
||||
"@tanstack/router-plugin": "^1.127.5",
|
||||
"@types/node": "^24.0.13",
|
||||
|
||||
@@ -21,7 +21,7 @@ import { resolvedModelName } from '../lib/resolvedModelName';
|
||||
import { Banner } from './core/Banner';
|
||||
import { Checkbox } from './core/Checkbox';
|
||||
import { DetailsBanner } from './core/DetailsBanner';
|
||||
import { Editor } from './core/Editor/Editor';
|
||||
import { Editor } from './core/Editor/LazyEditor';
|
||||
import { IconButton } from './core/IconButton';
|
||||
import { Input } from './core/Input';
|
||||
import { Label } from './core/Label';
|
||||
|
||||
@@ -17,7 +17,7 @@ import { showDialog } from '../lib/dialog';
|
||||
import { pluralizeCount } from '../lib/pluralize';
|
||||
import { Button } from './core/Button';
|
||||
import type { EditorProps } from './core/Editor/Editor';
|
||||
import { Editor } from './core/Editor/Editor';
|
||||
import { Editor } from './core/Editor/LazyEditor';
|
||||
import { FormattedError } from './core/FormattedError';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
import { VStack } from './core/Stacks';
|
||||
|
||||
@@ -15,7 +15,7 @@ import { copyToClipboard } from '../lib/copy';
|
||||
import { AutoScroller } from './core/AutoScroller';
|
||||
import { Banner } from './core/Banner';
|
||||
import { Button } from './core/Button';
|
||||
import { Editor } from './core/Editor/Editor';
|
||||
import { Editor } from './core/Editor/LazyEditor';
|
||||
import { HotKeyList } from './core/HotKeyList';
|
||||
import { Icon } from './core/Icon';
|
||||
import { IconButton } from './core/IconButton';
|
||||
|
||||
@@ -53,9 +53,6 @@ export function HeadersEditor({
|
||||
disabled
|
||||
disableDrag
|
||||
className="py-1"
|
||||
onChange={() => {}}
|
||||
onEnd={() => {}}
|
||||
onMove={() => {}}
|
||||
pair={ensurePairId(pair)}
|
||||
stateKey={null}
|
||||
nameAutocompleteFunctions
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
|
||||
import classNames from 'classnames';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
import type { CSSProperties } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { lazy, Suspense, useCallback, useMemo, useState } from 'react';
|
||||
import { activeRequestIdAtom } from '../hooks/useActiveRequestId';
|
||||
import { allRequestsAtom } from '../hooks/useAllRequests';
|
||||
import { useAuthTab } from '../hooks/useAuthTab';
|
||||
@@ -37,7 +37,7 @@ import { showToast } from '../lib/toast';
|
||||
import { BinaryFileEditor } from './BinaryFileEditor';
|
||||
import { ConfirmLargeRequestBody } from './ConfirmLargeRequestBody';
|
||||
import { CountBadge } from './core/CountBadge';
|
||||
import { Editor } from './core/Editor/Editor';
|
||||
import { Editor } from './core/Editor/LazyEditor';
|
||||
import type { GenericCompletionConfig } from './core/Editor/genericCompletion';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
import type { Pair } from './core/PairEditor';
|
||||
@@ -53,7 +53,10 @@ import { MarkdownEditor } from './MarkdownEditor';
|
||||
import { RequestMethodDropdown } from './RequestMethodDropdown';
|
||||
import { UrlBar } from './UrlBar';
|
||||
import { UrlParametersEditor } from './UrlParameterEditor';
|
||||
import { GraphQLEditor } from './graphql/GraphQLEditor';
|
||||
|
||||
const GraphQLEditor = lazy(() =>
|
||||
import('./graphql/GraphQLEditor').then((m) => ({ default: m.GraphQLEditor })),
|
||||
);
|
||||
|
||||
interface Props {
|
||||
style: CSSProperties;
|
||||
@@ -405,12 +408,14 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
|
||||
stateKey={`xml.${activeRequest.id}`}
|
||||
/>
|
||||
) : activeRequest.bodyType === BODY_TYPE_GRAPHQL ? (
|
||||
<GraphQLEditor
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
baseRequest={activeRequest}
|
||||
request={activeRequest}
|
||||
onChange={handleBodyChange}
|
||||
/>
|
||||
<Suspense>
|
||||
<GraphQLEditor
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
baseRequest={activeRequest}
|
||||
request={activeRequest}
|
||||
onChange={handleBodyChange}
|
||||
/>
|
||||
</Suspense>
|
||||
) : activeRequest.bodyType === BODY_TYPE_FORM_URLENCODED ? (
|
||||
<FormUrlencodedEditor
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||
import classNames from 'classnames';
|
||||
import type { CSSProperties, ReactNode } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import type { CSSProperties, ReactNode} from 'react';
|
||||
import React, { Suspense , lazy, useCallback, useMemo } from 'react';
|
||||
import { useLocalStorage } from 'react-use';
|
||||
import { useCancelHttpResponse } from '../hooks/useCancelHttpResponse';
|
||||
import { usePinnedHttpResponse } from '../hooks/usePinnedHttpResponse';
|
||||
@@ -28,12 +28,15 @@ import { CsvViewer } from './responseViewers/CsvViewer';
|
||||
import { EventStreamViewer } from './responseViewers/EventStreamViewer';
|
||||
import { HTMLOrTextViewer } from './responseViewers/HTMLOrTextViewer';
|
||||
import { ImageViewer } from './responseViewers/ImageViewer';
|
||||
import { PdfViewer } from './responseViewers/PdfViewer';
|
||||
import { SvgViewer } from './responseViewers/SvgViewer';
|
||||
import { VideoViewer } from './responseViewers/VideoViewer';
|
||||
import { ErrorBoundary } from './ErrorBoundary';
|
||||
import { Button } from './core/Button';
|
||||
|
||||
const PdfViewer = lazy(() =>
|
||||
import('./responseViewers/PdfViewer').then((m) => ({ default: m.PdfViewer })),
|
||||
);
|
||||
|
||||
interface Props {
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
@@ -106,9 +109,7 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
|
||||
)}
|
||||
>
|
||||
{activeResponse == null ? (
|
||||
<HotKeyList
|
||||
hotkeys={['request.send', 'model.create', 'sidebar.focus', 'url_bar.focus']}
|
||||
/>
|
||||
<HotKeyList hotkeys={['request.send', 'model.create', 'sidebar.focus', 'url_bar.focus']} />
|
||||
) : (
|
||||
<div className="h-full w-full grid grid-rows-[auto_minmax(0,1fr)] grid-cols-1">
|
||||
<HStack
|
||||
@@ -161,41 +162,46 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
|
||||
>
|
||||
<TabContent value={TAB_BODY}>
|
||||
<ErrorBoundary name="Http Response Viewer">
|
||||
<ConfirmLargeResponse response={activeResponse}>
|
||||
{activeResponse.state === 'initialized' ? (
|
||||
<EmptyStateText>
|
||||
<VStack space={3}>
|
||||
<HStack space={3}>
|
||||
<LoadingIcon className="text-text-subtlest" />
|
||||
Sending Request
|
||||
</HStack>
|
||||
<Button size="sm" variant="border" onClick={() => cancel.mutate()}>Cancel</Button>
|
||||
</VStack>
|
||||
</EmptyStateText>
|
||||
) : activeResponse.state === 'closed' && activeResponse.contentLength === 0 ? (
|
||||
<EmptyStateText>Empty </EmptyStateText>
|
||||
) : mimeType?.match(/^text\/event-stream/i) && viewMode === 'pretty' ? (
|
||||
<EventStreamViewer response={activeResponse} />
|
||||
) : mimeType?.match(/^image\/svg/) ? (
|
||||
<SvgViewer response={activeResponse} />
|
||||
) : mimeType?.match(/^image/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} render={ImageViewer} />
|
||||
) : mimeType?.match(/^audio/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} render={AudioViewer} />
|
||||
) : mimeType?.match(/^video/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} render={VideoViewer} />
|
||||
) : mimeType?.match(/pdf/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} render={PdfViewer} />
|
||||
) : mimeType?.match(/csv|tab-separated/i) ? (
|
||||
<CsvViewer className="pb-2" response={activeResponse} />
|
||||
) : (
|
||||
<HTMLOrTextViewer
|
||||
textViewerClassName="-mr-2 bg-surface" // Pull to the right
|
||||
response={activeResponse}
|
||||
pretty={viewMode === 'pretty'}
|
||||
/>
|
||||
)}
|
||||
</ConfirmLargeResponse>
|
||||
<Suspense>
|
||||
<ConfirmLargeResponse response={activeResponse}>
|
||||
{activeResponse.state === 'initialized' ? (
|
||||
<EmptyStateText>
|
||||
<VStack space={3}>
|
||||
<HStack space={3}>
|
||||
<LoadingIcon className="text-text-subtlest" />
|
||||
Sending Request
|
||||
</HStack>
|
||||
<Button size="sm" variant="border" onClick={() => cancel.mutate()}>
|
||||
Cancel
|
||||
</Button>
|
||||
</VStack>
|
||||
</EmptyStateText>
|
||||
) : activeResponse.state === 'closed' &&
|
||||
activeResponse.contentLength === 0 ? (
|
||||
<EmptyStateText>Empty </EmptyStateText>
|
||||
) : mimeType?.match(/^text\/event-stream/i) && viewMode === 'pretty' ? (
|
||||
<EventStreamViewer response={activeResponse} />
|
||||
) : mimeType?.match(/^image\/svg/) ? (
|
||||
<SvgViewer response={activeResponse} />
|
||||
) : mimeType?.match(/^image/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} render={ImageViewer} />
|
||||
) : mimeType?.match(/^audio/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} render={AudioViewer} />
|
||||
) : mimeType?.match(/^video/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} render={VideoViewer} />
|
||||
) : mimeType?.match(/pdf/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} render={PdfViewer} />
|
||||
) : mimeType?.match(/csv|tab-separated/i) ? (
|
||||
<CsvViewer className="pb-2" response={activeResponse} />
|
||||
) : (
|
||||
<HTMLOrTextViewer
|
||||
textViewerClassName="-mr-2 bg-surface" // Pull to the right
|
||||
response={activeResponse}
|
||||
pretty={viewMode === 'pretty'}
|
||||
/>
|
||||
)}
|
||||
</ConfirmLargeResponse>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</TabContent>
|
||||
<TabContent value={TAB_HEADERS}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames';
|
||||
import { useRef, useState } from 'react';
|
||||
import type { EditorProps } from './core/Editor/Editor';
|
||||
import { Editor } from './core/Editor/Editor';
|
||||
import { Editor } from './core/Editor/LazyEditor';
|
||||
import { SegmentedControl } from './core/SegmentedControl';
|
||||
import { Markdown } from './Markdown';
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import classNames from 'classnames';
|
||||
import { FocusTrap } from 'focus-trap-react';
|
||||
import * as m from 'motion/react-m';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import type { ReactNode} from 'react';
|
||||
import React, { Suspense , lazy, useRef } from 'react';
|
||||
import { Portal } from './Portal';
|
||||
|
||||
const FocusTrap = lazy(() => import('focus-trap-react'));
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
portalName: string;
|
||||
@@ -50,50 +51,52 @@ export function Overlay({
|
||||
return (
|
||||
<Portal name={portalName}>
|
||||
{open && (
|
||||
<FocusTrap
|
||||
focusTrapOptions={{
|
||||
allowOutsideClick: true, // So we can still click toasts and things
|
||||
delayInitialFocus: true,
|
||||
fallbackFocus: () => containerRef.current!, // always have a target
|
||||
initialFocus: () =>
|
||||
// Doing this explicitly seems to work better than the default behavior for some reason
|
||||
containerRef.current?.querySelector<HTMLElement>(
|
||||
[
|
||||
'a[href]',
|
||||
'input:not([disabled])',
|
||||
'select:not([disabled])',
|
||||
'textarea:not([disabled])',
|
||||
'button:not([disabled])',
|
||||
'[tabindex]:not([tabindex="-1"])',
|
||||
'[contenteditable]:not([contenteditable="false"])',
|
||||
].join(', '),
|
||||
) ?? undefined,
|
||||
}}
|
||||
>
|
||||
<m.div
|
||||
ref={containerRef}
|
||||
tabIndex={-1}
|
||||
className={classNames('fixed inset-0', zIndexes[zIndex])}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
<Suspense>
|
||||
<FocusTrap
|
||||
focusTrapOptions={{
|
||||
allowOutsideClick: true, // So we can still click toasts and things
|
||||
delayInitialFocus: true,
|
||||
fallbackFocus: () => containerRef.current!, // always have a target
|
||||
initialFocus: () =>
|
||||
// Doing this explicitly seems to work better than the default behavior for some reason
|
||||
containerRef.current?.querySelector<HTMLElement>(
|
||||
[
|
||||
'a[href]',
|
||||
'input:not([disabled])',
|
||||
'select:not([disabled])',
|
||||
'textarea:not([disabled])',
|
||||
'button:not([disabled])',
|
||||
'[tabindex]:not([tabindex="-1"])',
|
||||
'[contenteditable]:not([contenteditable="false"])',
|
||||
].join(', '),
|
||||
) ?? undefined,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
aria-hidden
|
||||
onClick={onClose}
|
||||
className={classNames(
|
||||
'absolute inset-0',
|
||||
variant === 'default' && 'bg-backdrop backdrop-blur-sm',
|
||||
)}
|
||||
/>
|
||||
<m.div
|
||||
ref={containerRef}
|
||||
tabIndex={-1}
|
||||
className={classNames('fixed inset-0', zIndexes[zIndex])}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
>
|
||||
<div
|
||||
aria-hidden
|
||||
onClick={onClose}
|
||||
className={classNames(
|
||||
'absolute inset-0',
|
||||
variant === 'default' && 'bg-backdrop backdrop-blur-sm',
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Show the draggable region at the top */}
|
||||
{/* TODO: Figure out tauri drag region and also make clickable still */}
|
||||
{variant === 'default' && (
|
||||
<div data-tauri-drag-region className="absolute top-0 left-0 h-md right-0" />
|
||||
)}
|
||||
{children}
|
||||
</m.div>
|
||||
</FocusTrap>
|
||||
{/* Show the draggable region at the top */}
|
||||
{/* TODO: Figure out tauri drag region and also make clickable still */}
|
||||
{variant === 'default' && (
|
||||
<div data-tauri-drag-region className="absolute top-0 left-0 h-md right-0" />
|
||||
)}
|
||||
{children}
|
||||
</m.div>
|
||||
</FocusTrap>
|
||||
</Suspense>
|
||||
)}
|
||||
</Portal>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { patchModel, settingsAtom } from '@yaakapp-internal/models';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import React from 'react';
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import { activeWorkspaceAtom } from '../../hooks/useActiveWorkspace';
|
||||
import { useResolvedAppearance } from '../../hooks/useResolvedAppearance';
|
||||
import { useResolvedTheme } from '../../hooks/useResolvedTheme';
|
||||
import type { ButtonProps } from '../core/Button';
|
||||
import { Editor } from '../core/Editor/Editor';
|
||||
import type { IconProps } from '../core/Icon';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
@@ -13,6 +12,8 @@ import type { SelectProps } from '../core/Select';
|
||||
import { Select } from '../core/Select';
|
||||
import { HStack, VStack } from '../core/Stacks';
|
||||
|
||||
const Editor = lazy(() => import('../core/Editor/Editor').then((m) => ({ default: m.Editor })));
|
||||
|
||||
const buttonColors: ButtonProps['color'][] = [
|
||||
'primary',
|
||||
'info',
|
||||
@@ -144,17 +145,19 @@ export function SettingsTheme() {
|
||||
/>
|
||||
))}
|
||||
</HStack>
|
||||
<Editor
|
||||
defaultValue={[
|
||||
'let foo = { // Demo code editor',
|
||||
' foo: ("bar" || "baz" ?? \'qux\'),',
|
||||
' baz: [1, 10.2, null, false, true],',
|
||||
'};',
|
||||
].join('\n')}
|
||||
heightMode="auto"
|
||||
language="javascript"
|
||||
stateKey={null}
|
||||
/>
|
||||
<Suspense>
|
||||
<Editor
|
||||
defaultValue={[
|
||||
'let foo = { // Demo code editor',
|
||||
' foo: ("bar" || "baz" ?? \'qux\'),',
|
||||
' baz: [1, 10.2, null, false, true],',
|
||||
'};',
|
||||
].join('\n')}
|
||||
heightMode="auto"
|
||||
language="javascript"
|
||||
stateKey={null}
|
||||
/>
|
||||
</Suspense>
|
||||
</VStack>
|
||||
</VStack>
|
||||
);
|
||||
|
||||
@@ -25,7 +25,7 @@ import { generateId } from '../lib/generateId';
|
||||
import { prepareImportQuerystring } from '../lib/prepareImportQuerystring';
|
||||
import { resolvedModelName } from '../lib/resolvedModelName';
|
||||
import { CountBadge } from './core/CountBadge';
|
||||
import { Editor } from './core/Editor/Editor';
|
||||
import { Editor } from './core/Editor/LazyEditor';
|
||||
import type { GenericCompletionConfig } from './core/Editor/genericCompletion';
|
||||
import { IconButton } from './core/IconButton';
|
||||
import type { Pair } from './core/PairEditor';
|
||||
|
||||
@@ -17,7 +17,7 @@ import { copyToClipboard } from '../lib/copy';
|
||||
import { AutoScroller } from './core/AutoScroller';
|
||||
import { Banner } from './core/Banner';
|
||||
import { Button } from './core/Button';
|
||||
import { Editor } from './core/Editor/Editor';
|
||||
import { Editor } from './core/Editor/LazyEditor';
|
||||
import { HotKeyList } from './core/HotKeyList';
|
||||
import { Icon } from './core/Icon';
|
||||
import { IconButton } from './core/IconButton';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { generateId } from '../../lib/generateId';
|
||||
import { Editor } from './Editor/Editor';
|
||||
import { Editor } from './Editor/LazyEditor';
|
||||
import type { Pair, PairEditorProps, PairWithId } from './PairEditor';
|
||||
|
||||
type Props = PairEditorProps;
|
||||
|
||||
13
src-web/components/core/Editor/LazyEditor.tsx
Normal file
13
src-web/components/core/Editor/LazyEditor.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { EditorView } from '@codemirror/view';
|
||||
import { forwardRef, lazy, Suspense } from 'react';
|
||||
import type { EditorProps } from './Editor';
|
||||
|
||||
const Editor_ = lazy(() => import('./Editor').then((m) => ({ default: m.Editor })));
|
||||
|
||||
export const Editor = forwardRef<EditorView, EditorProps>(function LazyEditor(props, ref) {
|
||||
return (
|
||||
<Suspense>
|
||||
<Editor_ ref={ref} {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
});
|
||||
@@ -1,127 +1,245 @@
|
||||
import type { Color } from '@yaakapp-internal/plugins';
|
||||
import classNames from 'classnames';
|
||||
import * as lucide from 'lucide-react';
|
||||
import {
|
||||
AlertTriangleIcon,
|
||||
ArchiveIcon,
|
||||
ArrowBigDownDashIcon,
|
||||
ArrowBigLeftDashIcon,
|
||||
ArrowBigRightDashIcon,
|
||||
ArrowBigRightIcon,
|
||||
ArrowBigUpDashIcon,
|
||||
ArrowDownIcon,
|
||||
ArrowDownToDotIcon,
|
||||
ArrowDownToLineIcon,
|
||||
ArrowRightCircleIcon,
|
||||
ArrowUpDownIcon,
|
||||
ArrowUpFromDotIcon,
|
||||
ArrowUpFromLineIcon,
|
||||
ArrowUpIcon,
|
||||
BadgeCheckIcon,
|
||||
BookOpenText,
|
||||
BoxIcon,
|
||||
CakeIcon,
|
||||
CheckCircleIcon,
|
||||
CheckIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
CircleAlertIcon,
|
||||
CircleDashedIcon,
|
||||
CircleDollarSignIcon,
|
||||
CircleFadingArrowUpIcon,
|
||||
CircleHelpIcon,
|
||||
ClipboardPasteIcon,
|
||||
ClockIcon,
|
||||
CodeIcon,
|
||||
Columns2Icon,
|
||||
CommandIcon,
|
||||
CookieIcon,
|
||||
CopyCheck,
|
||||
CopyIcon,
|
||||
CornerRightUpIcon,
|
||||
CreditCardIcon,
|
||||
DotIcon,
|
||||
DownloadIcon,
|
||||
EllipsisIcon,
|
||||
ExpandIcon,
|
||||
ExternalLinkIcon,
|
||||
EyeIcon,
|
||||
EyeOffIcon,
|
||||
FileCodeIcon,
|
||||
FileTextIcon,
|
||||
FilterIcon,
|
||||
FlameIcon,
|
||||
FlaskConicalIcon,
|
||||
FolderCodeIcon,
|
||||
FolderCogIcon,
|
||||
FolderGitIcon,
|
||||
FolderIcon,
|
||||
FolderInputIcon,
|
||||
FolderOpenIcon,
|
||||
FolderOutputIcon,
|
||||
FolderSymlinkIcon,
|
||||
FolderSyncIcon,
|
||||
FolderUpIcon,
|
||||
GitBranchIcon,
|
||||
GitBranchPlusIcon,
|
||||
GitCommitIcon,
|
||||
GitCommitVerticalIcon,
|
||||
GitForkIcon,
|
||||
GitPullRequestIcon,
|
||||
GripVerticalIcon,
|
||||
HandIcon,
|
||||
HistoryIcon,
|
||||
HomeIcon,
|
||||
ImportIcon,
|
||||
InfoIcon,
|
||||
KeyboardIcon,
|
||||
KeyRoundIcon,
|
||||
LockIcon,
|
||||
LockOpenIcon,
|
||||
MergeIcon,
|
||||
MessageSquare,
|
||||
MinusCircleIcon,
|
||||
MinusIcon,
|
||||
MoonIcon,
|
||||
MoreVerticalIcon,
|
||||
PaletteIcon,
|
||||
PanelLeftCloseIcon,
|
||||
PanelLeftOpenIcon,
|
||||
PencilIcon,
|
||||
PinIcon,
|
||||
PinOffIcon,
|
||||
Plug,
|
||||
PlusCircleIcon,
|
||||
PlusIcon,
|
||||
PuzzleIcon,
|
||||
RefreshCcwIcon,
|
||||
RefreshCwIcon,
|
||||
RocketIcon,
|
||||
Rows2Icon,
|
||||
SaveIcon,
|
||||
SearchIcon,
|
||||
SendHorizonalIcon,
|
||||
SettingsIcon,
|
||||
ShieldAlertIcon,
|
||||
ShieldCheckIcon,
|
||||
ShieldIcon,
|
||||
ShieldOffIcon,
|
||||
SparklesIcon,
|
||||
SquareCheckIcon,
|
||||
SquareIcon,
|
||||
SquareTerminalIcon,
|
||||
SunIcon,
|
||||
TableIcon,
|
||||
Trash2Icon,
|
||||
UploadIcon,
|
||||
VariableIcon,
|
||||
Wand2Icon,
|
||||
WrenchIcon,
|
||||
XIcon,
|
||||
} from 'lucide-react';
|
||||
import type { CSSProperties, HTMLAttributes } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
const icons = {
|
||||
alert_triangle: lucide.AlertTriangleIcon,
|
||||
archive: lucide.ArchiveIcon,
|
||||
arrow_big_down_dash: lucide.ArrowBigDownDashIcon,
|
||||
arrow_big_left_dash: lucide.ArrowBigLeftDashIcon,
|
||||
arrow_big_right: lucide.ArrowBigRightIcon,
|
||||
arrow_big_right_dash: lucide.ArrowBigRightDashIcon,
|
||||
arrow_big_up_dash: lucide.ArrowBigUpDashIcon,
|
||||
arrow_down: lucide.ArrowDownIcon,
|
||||
arrow_down_to_dot: lucide.ArrowDownToDotIcon,
|
||||
arrow_down_to_line: lucide.ArrowDownToLineIcon,
|
||||
arrow_right_circle: lucide.ArrowRightCircleIcon,
|
||||
arrow_up: lucide.ArrowUpIcon,
|
||||
arrow_up_down: lucide.ArrowUpDownIcon,
|
||||
arrow_up_from_dot: lucide.ArrowUpFromDotIcon,
|
||||
arrow_up_from_line: lucide.ArrowUpFromLineIcon,
|
||||
badge_check: lucide.BadgeCheckIcon,
|
||||
book_open_text: lucide.BookOpenText,
|
||||
box: lucide.BoxIcon,
|
||||
cake: lucide.CakeIcon,
|
||||
chat: lucide.MessageSquare,
|
||||
check: lucide.CheckIcon,
|
||||
check_circle: lucide.CheckCircleIcon,
|
||||
check_square_checked: lucide.SquareCheckIcon,
|
||||
check_square_unchecked: lucide.SquareIcon,
|
||||
chevron_down: lucide.ChevronDownIcon,
|
||||
chevron_left: lucide.ChevronLeftIcon,
|
||||
chevron_right: lucide.ChevronRightIcon,
|
||||
circle_alert: lucide.CircleAlertIcon,
|
||||
circle_dashed: lucide.CircleDashedIcon,
|
||||
circle_dollar_sign: lucide.CircleDollarSignIcon,
|
||||
circle_fading_arrow_up: lucide.CircleFadingArrowUpIcon,
|
||||
clock: lucide.ClockIcon,
|
||||
code: lucide.CodeIcon,
|
||||
columns_2: lucide.Columns2Icon,
|
||||
command: lucide.CommandIcon,
|
||||
cookie: lucide.CookieIcon,
|
||||
copy: lucide.CopyIcon,
|
||||
copy_check: lucide.CopyCheck,
|
||||
corner_right_up: lucide.CornerRightUpIcon,
|
||||
credit_card: lucide.CreditCardIcon,
|
||||
dot: lucide.DotIcon,
|
||||
download: lucide.DownloadIcon,
|
||||
ellipsis: lucide.EllipsisIcon,
|
||||
expand: lucide.ExpandIcon,
|
||||
external_link: lucide.ExternalLinkIcon,
|
||||
eye: lucide.EyeIcon,
|
||||
eye_closed: lucide.EyeOffIcon,
|
||||
file_code: lucide.FileCodeIcon,
|
||||
filter: lucide.FilterIcon,
|
||||
flame: lucide.FlameIcon,
|
||||
flask: lucide.FlaskConicalIcon,
|
||||
folder: lucide.FolderIcon,
|
||||
folder_code: lucide.FolderCodeIcon,
|
||||
folder_cog: lucide.FolderCogIcon,
|
||||
folder_git: lucide.FolderGitIcon,
|
||||
folder_input: lucide.FolderInputIcon,
|
||||
folder_open: lucide.FolderOpenIcon,
|
||||
folder_output: lucide.FolderOutputIcon,
|
||||
folder_symlink: lucide.FolderSymlinkIcon,
|
||||
folder_sync: lucide.FolderSyncIcon,
|
||||
folder_up: lucide.FolderUpIcon,
|
||||
git_branch: lucide.GitBranchIcon,
|
||||
git_branch_plus: lucide.GitBranchPlusIcon,
|
||||
git_commit: lucide.GitCommitIcon,
|
||||
git_commit_vertical: lucide.GitCommitVerticalIcon,
|
||||
git_fork: lucide.GitForkIcon,
|
||||
git_pull_request: lucide.GitPullRequestIcon,
|
||||
grip_vertical: lucide.GripVerticalIcon,
|
||||
hand: lucide.HandIcon,
|
||||
help: lucide.CircleHelpIcon,
|
||||
history: lucide.HistoryIcon,
|
||||
house: lucide.HomeIcon,
|
||||
import: lucide.ImportIcon,
|
||||
info: lucide.InfoIcon,
|
||||
key_round: lucide.KeyRoundIcon,
|
||||
keyboard: lucide.KeyboardIcon,
|
||||
left_panel_hidden: lucide.PanelLeftOpenIcon,
|
||||
left_panel_visible: lucide.PanelLeftCloseIcon,
|
||||
lock: lucide.LockIcon,
|
||||
lock_open: lucide.LockOpenIcon,
|
||||
magic_wand: lucide.Wand2Icon,
|
||||
merge: lucide.MergeIcon,
|
||||
minus: lucide.MinusIcon,
|
||||
minus_circle: lucide.MinusCircleIcon,
|
||||
moon: lucide.MoonIcon,
|
||||
more_vertical: lucide.MoreVerticalIcon,
|
||||
palette: lucide.PaletteIcon,
|
||||
paste: lucide.ClipboardPasteIcon,
|
||||
pencil: lucide.PencilIcon,
|
||||
pin: lucide.PinIcon,
|
||||
plug: lucide.Plug,
|
||||
plus: lucide.PlusIcon,
|
||||
plus_circle: lucide.PlusCircleIcon,
|
||||
puzzle: lucide.PuzzleIcon,
|
||||
refresh: lucide.RefreshCwIcon,
|
||||
rocket: lucide.RocketIcon,
|
||||
rows_2: lucide.Rows2Icon,
|
||||
save: lucide.SaveIcon,
|
||||
search: lucide.SearchIcon,
|
||||
send_horizontal: lucide.SendHorizonalIcon,
|
||||
settings: lucide.SettingsIcon,
|
||||
shield: lucide.ShieldIcon,
|
||||
shield_check: lucide.ShieldCheckIcon,
|
||||
shield_off: lucide.ShieldOffIcon,
|
||||
sparkles: lucide.SparklesIcon,
|
||||
square_terminal: lucide.SquareTerminalIcon,
|
||||
sun: lucide.SunIcon,
|
||||
table: lucide.TableIcon,
|
||||
text: lucide.FileTextIcon,
|
||||
trash: lucide.Trash2Icon,
|
||||
unpin: lucide.PinOffIcon,
|
||||
update: lucide.RefreshCcwIcon,
|
||||
upload: lucide.UploadIcon,
|
||||
variable: lucide.VariableIcon,
|
||||
wrench: lucide.WrenchIcon,
|
||||
x: lucide.XIcon,
|
||||
_unknown: lucide.ShieldAlertIcon,
|
||||
alert_triangle: AlertTriangleIcon,
|
||||
archive: ArchiveIcon,
|
||||
arrow_big_down_dash: ArrowBigDownDashIcon,
|
||||
arrow_big_left_dash: ArrowBigLeftDashIcon,
|
||||
arrow_big_right: ArrowBigRightIcon,
|
||||
arrow_big_right_dash: ArrowBigRightDashIcon,
|
||||
arrow_big_up_dash: ArrowBigUpDashIcon,
|
||||
arrow_down: ArrowDownIcon,
|
||||
arrow_down_to_dot: ArrowDownToDotIcon,
|
||||
arrow_down_to_line: ArrowDownToLineIcon,
|
||||
arrow_right_circle: ArrowRightCircleIcon,
|
||||
arrow_up: ArrowUpIcon,
|
||||
arrow_up_down: ArrowUpDownIcon,
|
||||
arrow_up_from_dot: ArrowUpFromDotIcon,
|
||||
arrow_up_from_line: ArrowUpFromLineIcon,
|
||||
badge_check: BadgeCheckIcon,
|
||||
book_open_text: BookOpenText,
|
||||
box: BoxIcon,
|
||||
cake: CakeIcon,
|
||||
chat: MessageSquare,
|
||||
check: CheckIcon,
|
||||
check_circle: CheckCircleIcon,
|
||||
check_square_checked: SquareCheckIcon,
|
||||
check_square_unchecked: SquareIcon,
|
||||
chevron_down: ChevronDownIcon,
|
||||
chevron_left: ChevronLeftIcon,
|
||||
chevron_right: ChevronRightIcon,
|
||||
circle_alert: CircleAlertIcon,
|
||||
circle_dashed: CircleDashedIcon,
|
||||
circle_dollar_sign: CircleDollarSignIcon,
|
||||
circle_fading_arrow_up: CircleFadingArrowUpIcon,
|
||||
clock: ClockIcon,
|
||||
code: CodeIcon,
|
||||
columns_2: Columns2Icon,
|
||||
command: CommandIcon,
|
||||
cookie: CookieIcon,
|
||||
copy: CopyIcon,
|
||||
copy_check: CopyCheck,
|
||||
corner_right_up: CornerRightUpIcon,
|
||||
credit_card: CreditCardIcon,
|
||||
dot: DotIcon,
|
||||
download: DownloadIcon,
|
||||
ellipsis: EllipsisIcon,
|
||||
expand: ExpandIcon,
|
||||
external_link: ExternalLinkIcon,
|
||||
eye: EyeIcon,
|
||||
eye_closed: EyeOffIcon,
|
||||
file_code: FileCodeIcon,
|
||||
filter: FilterIcon,
|
||||
flame: FlameIcon,
|
||||
flask: FlaskConicalIcon,
|
||||
folder: FolderIcon,
|
||||
folder_code: FolderCodeIcon,
|
||||
folder_cog: FolderCogIcon,
|
||||
folder_git: FolderGitIcon,
|
||||
folder_input: FolderInputIcon,
|
||||
folder_open: FolderOpenIcon,
|
||||
folder_output: FolderOutputIcon,
|
||||
folder_symlink: FolderSymlinkIcon,
|
||||
folder_sync: FolderSyncIcon,
|
||||
folder_up: FolderUpIcon,
|
||||
git_branch: GitBranchIcon,
|
||||
git_branch_plus: GitBranchPlusIcon,
|
||||
git_commit: GitCommitIcon,
|
||||
git_commit_vertical: GitCommitVerticalIcon,
|
||||
git_fork: GitForkIcon,
|
||||
git_pull_request: GitPullRequestIcon,
|
||||
grip_vertical: GripVerticalIcon,
|
||||
hand: HandIcon,
|
||||
help: CircleHelpIcon,
|
||||
history: HistoryIcon,
|
||||
house: HomeIcon,
|
||||
import: ImportIcon,
|
||||
info: InfoIcon,
|
||||
key_round: KeyRoundIcon,
|
||||
keyboard: KeyboardIcon,
|
||||
left_panel_hidden: PanelLeftOpenIcon,
|
||||
left_panel_visible: PanelLeftCloseIcon,
|
||||
lock: LockIcon,
|
||||
lock_open: LockOpenIcon,
|
||||
magic_wand: Wand2Icon,
|
||||
merge: MergeIcon,
|
||||
minus: MinusIcon,
|
||||
minus_circle: MinusCircleIcon,
|
||||
moon: MoonIcon,
|
||||
more_vertical: MoreVerticalIcon,
|
||||
palette: PaletteIcon,
|
||||
paste: ClipboardPasteIcon,
|
||||
pencil: PencilIcon,
|
||||
pin: PinIcon,
|
||||
plug: Plug,
|
||||
plus: PlusIcon,
|
||||
plus_circle: PlusCircleIcon,
|
||||
puzzle: PuzzleIcon,
|
||||
refresh: RefreshCwIcon,
|
||||
rocket: RocketIcon,
|
||||
rows_2: Rows2Icon,
|
||||
save: SaveIcon,
|
||||
search: SearchIcon,
|
||||
send_horizontal: SendHorizonalIcon,
|
||||
settings: SettingsIcon,
|
||||
shield: ShieldIcon,
|
||||
shield_check: ShieldCheckIcon,
|
||||
shield_off: ShieldOffIcon,
|
||||
sparkles: SparklesIcon,
|
||||
square_terminal: SquareTerminalIcon,
|
||||
sun: SunIcon,
|
||||
table: TableIcon,
|
||||
text: FileTextIcon,
|
||||
trash: Trash2Icon,
|
||||
unpin: PinOffIcon,
|
||||
update: RefreshCcwIcon,
|
||||
upload: UploadIcon,
|
||||
variable: VariableIcon,
|
||||
wrench: WrenchIcon,
|
||||
x: XIcon,
|
||||
_unknown: ShieldAlertIcon,
|
||||
|
||||
empty: (props: HTMLAttributes<HTMLSpanElement>) => <div {...props} />,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { EditorSelection } from '@codemirror/state';
|
||||
import type { EditorView } from '@codemirror/view';
|
||||
import type { Color } from '@yaakapp-internal/plugins';
|
||||
import classNames from 'classnames';
|
||||
@@ -30,7 +29,7 @@ import { Button } from './Button';
|
||||
import type { DropdownItem } from './Dropdown';
|
||||
import { Dropdown } from './Dropdown';
|
||||
import type { EditorProps } from './Editor/Editor';
|
||||
import { Editor } from './Editor/Editor';
|
||||
import { Editor } from './Editor/LazyEditor';
|
||||
import type { IconProps } from './Icon';
|
||||
import { Icon } from './Icon';
|
||||
import { IconButton } from './IconButton';
|
||||
@@ -161,11 +160,12 @@ const BaseInput = forwardRef<EditorView, InputProps>(function InputBase(
|
||||
onFocus?.();
|
||||
}, [onFocus, readOnly]);
|
||||
|
||||
const handleBlur = useCallback(() => {
|
||||
const handleBlur = useCallback(async () => {
|
||||
setFocused(false);
|
||||
// Move selection to the end on blur
|
||||
const anchor = editorRef.current?.state.doc.length ?? 0;
|
||||
editorRef.current?.dispatch({
|
||||
selection: EditorSelection.single(editorRef.current.state.doc.length ),
|
||||
selection: { anchor, head: anchor },
|
||||
});
|
||||
onBlur?.();
|
||||
}, [onBlur]);
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
import type { EditorView } from '@codemirror/view';
|
||||
import type { DragEndEvent, DragMoveEvent, DragStartEvent } from '@dnd-kit/core';
|
||||
import {
|
||||
DndContext,
|
||||
DragOverlay,
|
||||
PointerSensor,
|
||||
pointerWithin,
|
||||
useDraggable,
|
||||
useDroppable,
|
||||
useSensor,
|
||||
useSensors,
|
||||
} from '@dnd-kit/core';
|
||||
import { arrayMove } from '@dnd-kit/sortable';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
forwardRef,
|
||||
@@ -10,13 +22,12 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import type { XYCoord } from 'react-dnd';
|
||||
import { useDrag, useDrop } from 'react-dnd';
|
||||
import type { WrappedEnvironmentVariable } from '../../hooks/useEnvironmentVariables';
|
||||
import { useRandomKey } from '../../hooks/useRandomKey';
|
||||
import { useToggle } from '../../hooks/useToggle';
|
||||
import { languageFromContentType } from '../../lib/contentType';
|
||||
import { showDialog } from '../../lib/dialog';
|
||||
import { computeSideForDragMove } from '../../lib/dnd';
|
||||
import { showPrompt } from '../../lib/prompt';
|
||||
import { DropMarker } from '../DropMarker';
|
||||
import { SelectFile } from '../SelectFile';
|
||||
@@ -25,8 +36,8 @@ import { Checkbox } from './Checkbox';
|
||||
import type { DropdownItem } from './Dropdown';
|
||||
import { Dropdown } from './Dropdown';
|
||||
import type { EditorProps } from './Editor/Editor';
|
||||
import { Editor } from './Editor/Editor';
|
||||
import type { GenericCompletionConfig } from './Editor/genericCompletion';
|
||||
import { Editor } from './Editor/LazyEditor';
|
||||
import { Icon } from './Icon';
|
||||
import { IconButton } from './IconButton';
|
||||
import type { InputProps } from './Input';
|
||||
@@ -108,6 +119,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
||||
const [forceFocusNamePairId, setForceFocusNamePairId] = useState<string | null>(null);
|
||||
const [forceFocusValuePairId, setForceFocusValuePairId] = useState<string | null>(null);
|
||||
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
||||
const [isDragging, setIsDragging] = useState<PairWithId | null>(null);
|
||||
const [pairs, setPairs] = useState<PairWithId[]>([]);
|
||||
const [showAll, toggleShowAll] = useToggle(false);
|
||||
// NOTE: Use local force update key because we trigger an effect on forceUpdateKey change. If
|
||||
@@ -158,33 +170,6 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const handleMove = useCallback<PairEditorRowProps['onMove']>(
|
||||
(id, side) => {
|
||||
const dragIndex = pairs.findIndex((r) => r.id === id);
|
||||
setHoveredIndex(side === 'above' ? dragIndex : dragIndex + 1);
|
||||
},
|
||||
[pairs],
|
||||
);
|
||||
|
||||
const handleEnd = useCallback<PairEditorRowProps['onEnd']>(
|
||||
(id: string) => {
|
||||
if (hoveredIndex === null) return;
|
||||
setHoveredIndex(null);
|
||||
|
||||
setPairsAndSave((pairs) => {
|
||||
const index = pairs.findIndex((p) => p.id === id);
|
||||
const pair = pairs[index];
|
||||
if (pair === undefined) return pairs;
|
||||
|
||||
const newPairs = pairs.filter((p) => p.id !== id);
|
||||
if (hoveredIndex > index) newPairs.splice(hoveredIndex - 1, 0, pair);
|
||||
else newPairs.splice(hoveredIndex, 0, pair);
|
||||
return newPairs;
|
||||
});
|
||||
},
|
||||
[hoveredIndex, setPairsAndSave],
|
||||
);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(pair: PairWithId) =>
|
||||
setPairsAndSave((pairs) => pairs.map((p) => (pair.id !== p.id ? p : pair))),
|
||||
@@ -233,6 +218,55 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
||||
});
|
||||
}, []);
|
||||
|
||||
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 6 } }));
|
||||
|
||||
// dnd-kit: show the “between rows” marker while hovering
|
||||
const onDragMove = useCallback(
|
||||
(e: DragMoveEvent) => {
|
||||
const overId = e.over?.id as string | undefined;
|
||||
if (!overId) return setHoveredIndex(null);
|
||||
|
||||
const overPair = pairs.find((p) => p.id === overId);
|
||||
if (overPair == null) return setHoveredIndex(null);
|
||||
|
||||
const side = computeSideForDragMove(overPair.id, e);
|
||||
const overIndex = pairs.findIndex((p) => p.id === overId);
|
||||
const hoveredIndex = overIndex + (side === 'above' ? 0 : 1);
|
||||
|
||||
setHoveredIndex(hoveredIndex);
|
||||
},
|
||||
[pairs],
|
||||
);
|
||||
|
||||
const onDragStart = useCallback(
|
||||
(e: DragStartEvent) => {
|
||||
const pair = pairs.find((p) => p.id === e.active.id);
|
||||
setIsDragging(pair ?? null);
|
||||
},
|
||||
[pairs],
|
||||
);
|
||||
|
||||
const onDragCancel = useCallback(() => setIsDragging(null), []);
|
||||
|
||||
const onDragEnd = useCallback(
|
||||
(e: DragEndEvent) => {
|
||||
setIsDragging(null);
|
||||
setHoveredIndex(null);
|
||||
const activeId = e.active.id as string | undefined;
|
||||
const overId = e.over?.id as string | undefined;
|
||||
if (!activeId || !overId) return;
|
||||
|
||||
const from = pairs.findIndex((p) => p.id === activeId);
|
||||
const baseTo = pairs.findIndex((p) => p.id === overId);
|
||||
const to = hoveredIndex ?? (baseTo === -1 ? from : baseTo);
|
||||
|
||||
if (from !== -1 && to !== -1 && from !== to) {
|
||||
setPairsAndSave((ps) => arrayMove(ps, from, to > from ? to - 1 : to));
|
||||
}
|
||||
},
|
||||
[pairs, hoveredIndex, setPairsAndSave],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
@@ -246,67 +280,82 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
||||
'pt-0.5',
|
||||
)}
|
||||
>
|
||||
{pairs.map((p, i) => {
|
||||
if (!showAll && i > MAX_INITIAL_PAIRS) return null;
|
||||
<DndContext
|
||||
autoScroll
|
||||
sensors={sensors}
|
||||
onDragMove={onDragMove}
|
||||
onDragEnd={onDragEnd}
|
||||
onDragStart={onDragStart}
|
||||
onDragCancel={onDragCancel}
|
||||
collisionDetection={pointerWithin}
|
||||
>
|
||||
{pairs.map((p, i) => {
|
||||
if (!showAll && i > MAX_INITIAL_PAIRS) return null;
|
||||
|
||||
const isLast = i === pairs.length - 1;
|
||||
return (
|
||||
<Fragment key={p.id}>
|
||||
{hoveredIndex === i && <DropMarker />}
|
||||
const isLast = i === pairs.length - 1;
|
||||
return (
|
||||
<Fragment key={p.id}>
|
||||
{hoveredIndex === i && <DropMarker />}
|
||||
<PairEditorRow
|
||||
allowFileValues={allowFileValues}
|
||||
allowMultilineValues={allowMultilineValues}
|
||||
className="py-1"
|
||||
forcedEnvironmentId={forcedEnvironmentId}
|
||||
forceFocusNamePairId={forceFocusNamePairId}
|
||||
forceFocusValuePairId={forceFocusValuePairId}
|
||||
forceUpdateKey={localForceUpdateKey}
|
||||
index={i}
|
||||
isLast={isLast}
|
||||
isDraggingGlobal={!!isDragging}
|
||||
nameAutocomplete={nameAutocomplete}
|
||||
nameAutocompleteFunctions={nameAutocompleteFunctions}
|
||||
nameAutocompleteVariables={nameAutocompleteVariables}
|
||||
namePlaceholder={namePlaceholder}
|
||||
nameValidate={nameValidate}
|
||||
onChange={handleChange}
|
||||
onDelete={handleDelete}
|
||||
onFocusName={handleFocusName}
|
||||
onFocusValue={handleFocusValue}
|
||||
pair={p}
|
||||
stateKey={stateKey}
|
||||
valueAutocomplete={valueAutocomplete}
|
||||
valueAutocompleteFunctions={valueAutocompleteFunctions}
|
||||
valueAutocompleteVariables={valueAutocompleteVariables}
|
||||
valuePlaceholder={valuePlaceholder}
|
||||
valueType={valueType}
|
||||
valueValidate={valueValidate}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
{!showAll && pairs.length > MAX_INITIAL_PAIRS && (
|
||||
<Button onClick={toggleShowAll} variant="border" className="m-2" size="xs">
|
||||
Show {pairs.length - MAX_INITIAL_PAIRS} More
|
||||
</Button>
|
||||
)}
|
||||
<DragOverlay dropAnimation={null}>
|
||||
{isDragging && (
|
||||
<PairEditorRow
|
||||
allowFileValues={allowFileValues}
|
||||
allowMultilineValues={allowMultilineValues}
|
||||
className="py-1"
|
||||
forcedEnvironmentId={forcedEnvironmentId}
|
||||
forceFocusNamePairId={forceFocusNamePairId}
|
||||
forceFocusValuePairId={forceFocusValuePairId}
|
||||
forceUpdateKey={localForceUpdateKey}
|
||||
index={i}
|
||||
isLast={isLast}
|
||||
nameAutocomplete={nameAutocomplete}
|
||||
nameAutocompleteFunctions={nameAutocompleteFunctions}
|
||||
nameAutocompleteVariables={nameAutocompleteVariables}
|
||||
namePlaceholder={namePlaceholder}
|
||||
nameValidate={nameValidate}
|
||||
onChange={handleChange}
|
||||
onDelete={handleDelete}
|
||||
onEnd={handleEnd}
|
||||
onFocusName={handleFocusName}
|
||||
onFocusValue={handleFocusValue}
|
||||
onMove={handleMove}
|
||||
pair={p}
|
||||
stateKey={stateKey}
|
||||
valueAutocomplete={valueAutocomplete}
|
||||
valueAutocompleteFunctions={valueAutocompleteFunctions}
|
||||
valueAutocompleteVariables={valueAutocompleteVariables}
|
||||
valuePlaceholder={valuePlaceholder}
|
||||
valueType={valueType}
|
||||
valueValidate={valueValidate}
|
||||
className="opacity-80"
|
||||
pair={isDragging}
|
||||
index={0}
|
||||
stateKey={null}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
{!showAll && pairs.length > MAX_INITIAL_PAIRS && (
|
||||
<Button onClick={toggleShowAll} variant="border" className="m-2" size="xs">
|
||||
Show {pairs.length - MAX_INITIAL_PAIRS} More
|
||||
</Button>
|
||||
)}
|
||||
)}
|
||||
</DragOverlay>
|
||||
</DndContext>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
enum ItemTypes {
|
||||
ROW = 'pair-row',
|
||||
}
|
||||
|
||||
type PairEditorRowProps = {
|
||||
className?: string;
|
||||
pair: PairWithId;
|
||||
forceFocusNamePairId?: string | null;
|
||||
forceFocusValuePairId?: string | null;
|
||||
onMove: (id: string, side: 'above' | 'below') => void;
|
||||
onEnd: (id: string) => void;
|
||||
onChange: (pair: PairWithId) => void;
|
||||
onChange?: (pair: PairWithId) => void;
|
||||
onDelete?: (pair: PairWithId, focusPrevious: boolean) => void;
|
||||
onFocusName?: (pair: PairWithId) => void;
|
||||
onFocusValue?: (pair: PairWithId) => void;
|
||||
@@ -315,6 +364,7 @@ type PairEditorRowProps = {
|
||||
disabled?: boolean;
|
||||
disableDrag?: boolean;
|
||||
index: number;
|
||||
isDraggingGlobal?: boolean;
|
||||
} & Pick<
|
||||
PairEditorProps,
|
||||
| 'allowFileValues'
|
||||
@@ -352,12 +402,11 @@ export function PairEditorRow({
|
||||
nameAutocompleteVariables,
|
||||
namePlaceholder,
|
||||
nameValidate,
|
||||
isDraggingGlobal,
|
||||
onChange,
|
||||
onDelete,
|
||||
onEnd,
|
||||
onFocusName,
|
||||
onFocusValue,
|
||||
onMove,
|
||||
pair,
|
||||
stateKey,
|
||||
valueAutocomplete,
|
||||
@@ -367,7 +416,6 @@ export function PairEditorRow({
|
||||
valueType,
|
||||
valueValidate,
|
||||
}: PairEditorRowProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const nameInputRef = useRef<EditorView>(null);
|
||||
const valueInputRef = useRef<EditorView>(null);
|
||||
|
||||
@@ -388,29 +436,29 @@ export function PairEditorRow({
|
||||
const handleDelete = useCallback(() => onDelete?.(pair, false), [onDelete, pair]);
|
||||
|
||||
const handleChangeEnabled = useMemo(
|
||||
() => (enabled: boolean) => onChange({ ...pair, enabled }),
|
||||
() => (enabled: boolean) => onChange?.({ ...pair, enabled }),
|
||||
[onChange, pair],
|
||||
);
|
||||
|
||||
const handleChangeName = useMemo(
|
||||
() => (name: string) => onChange({ ...pair, name }),
|
||||
() => (name: string) => onChange?.({ ...pair, name }),
|
||||
[onChange, pair],
|
||||
);
|
||||
|
||||
const handleChangeValueText = useMemo(
|
||||
() => (value: string) => onChange({ ...pair, value, isFile: false }),
|
||||
() => (value: string) => onChange?.({ ...pair, value, isFile: false }),
|
||||
[onChange, pair],
|
||||
);
|
||||
|
||||
const handleChangeValueFile = useMemo(
|
||||
() =>
|
||||
({ filePath }: { filePath: string | null }) =>
|
||||
onChange({ ...pair, value: filePath ?? '', isFile: true }),
|
||||
onChange?.({ ...pair, value: filePath ?? '', isFile: true }),
|
||||
[onChange, pair],
|
||||
);
|
||||
|
||||
const handleChangeValueContentType = useMemo(
|
||||
() => (contentType: string) => onChange({ ...pair, contentType }),
|
||||
() => (contentType: string) => onChange?.({ ...pair, contentType }),
|
||||
[onChange, pair],
|
||||
);
|
||||
|
||||
@@ -448,30 +496,8 @@ export function PairEditorRow({
|
||||
[allowMultilineValues, handleDelete, handleEditMultiLineValue],
|
||||
);
|
||||
|
||||
const [, connectDrop] = useDrop<Pair>(
|
||||
{
|
||||
accept: ItemTypes.ROW,
|
||||
hover: (_, monitor) => {
|
||||
if (!ref.current) return;
|
||||
const hoverBoundingRect = ref.current?.getBoundingClientRect();
|
||||
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
||||
const clientOffset = monitor.getClientOffset();
|
||||
const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top;
|
||||
onMove(pair.id, hoverClientY < hoverMiddleY ? 'above' : 'below');
|
||||
},
|
||||
},
|
||||
[onMove],
|
||||
);
|
||||
|
||||
const [, connectDrag] = useDrag(
|
||||
{
|
||||
type: ItemTypes.ROW,
|
||||
item: () => pair,
|
||||
collect: (m) => ({ isDragging: m.isDragging() }),
|
||||
end: () => onEnd(pair.id),
|
||||
},
|
||||
[pair, onEnd],
|
||||
);
|
||||
const { attributes, listeners, setNodeRef: setDraggableRef } = useDraggable({ id: pair.id });
|
||||
const { setNodeRef: setDroppableRef } = useDroppable({ id: pair.id });
|
||||
|
||||
// Filter out the current pair name
|
||||
const valueAutocompleteVariablesFiltered = useMemo<EditorProps['autocompleteVariables']>(() => {
|
||||
@@ -482,12 +508,17 @@ export function PairEditorRow({
|
||||
}
|
||||
}, [pair.name, valueAutocompleteVariables]);
|
||||
|
||||
connectDrag(ref);
|
||||
connectDrop(ref);
|
||||
const handleSetRef = useCallback(
|
||||
(n: HTMLDivElement | null) => {
|
||||
setDraggableRef(n);
|
||||
setDroppableRef(n);
|
||||
},
|
||||
[setDraggableRef, setDroppableRef],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
ref={handleSetRef}
|
||||
className={classNames(
|
||||
className,
|
||||
'group grid grid-cols-[auto_auto_minmax(0,1fr)_auto]',
|
||||
@@ -505,6 +536,8 @@ export function PairEditorRow({
|
||||
/>
|
||||
{!isLast && !disableDrag ? (
|
||||
<div
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
className={classNames(
|
||||
'py-2 h-7 w-4 flex items-center',
|
||||
'justify-center opacity-0 group-hover:opacity-70',
|
||||
@@ -529,6 +562,7 @@ export function PairEditorRow({
|
||||
hideLabel
|
||||
size="sm"
|
||||
containerClassName={classNames(isLast && 'border-dashed')}
|
||||
className={classNames(isDraggingGlobal && 'pointer-events-none')}
|
||||
label="Name"
|
||||
name={`name[${index}]`}
|
||||
onFocus={handleFocusName}
|
||||
@@ -541,13 +575,13 @@ export function PairEditorRow({
|
||||
stateKey={`name.${pair.id}.${stateKey}`}
|
||||
disabled={disabled}
|
||||
wrapLines={false}
|
||||
readOnly={pair.readOnlyName}
|
||||
readOnly={pair.readOnlyName || isDraggingGlobal}
|
||||
size="sm"
|
||||
required={!isLast && !!pair.enabled && !!pair.value}
|
||||
validate={nameValidate}
|
||||
forcedEnvironmentId={forcedEnvironmentId}
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
containerClassName={classNames(isLast && 'border-dashed')}
|
||||
containerClassName={classNames('bg-surface', isLast && 'border-dashed')}
|
||||
defaultValue={pair.name}
|
||||
label="Name"
|
||||
name={`name[${index}]`}
|
||||
@@ -578,6 +612,7 @@ export function PairEditorRow({
|
||||
containerClassName={classNames(isLast && 'border-dashed')}
|
||||
label="Value"
|
||||
name={`value[${index}]`}
|
||||
className={classNames(isDraggingGlobal && 'pointer-events-none')}
|
||||
onFocus={handleFocusValue}
|
||||
placeholder={valuePlaceholder ?? 'value'}
|
||||
/>
|
||||
@@ -599,7 +634,8 @@ export function PairEditorRow({
|
||||
wrapLines={false}
|
||||
size="sm"
|
||||
disabled={disabled}
|
||||
containerClassName={classNames(isLast && 'border-dashed')}
|
||||
readOnly={isDraggingGlobal}
|
||||
containerClassName={classNames('bg-surface', isLast && 'border-dashed')}
|
||||
validate={valueValidate}
|
||||
forcedEnvironmentId={forcedEnvironmentId}
|
||||
forceUpdateKey={forceUpdateKey}
|
||||
|
||||
@@ -73,7 +73,6 @@ export function Tabs({
|
||||
className={classNames(
|
||||
className,
|
||||
'tabs-container',
|
||||
'transform-gpu',
|
||||
'h-full grid',
|
||||
layout === 'horizontal' && 'grid-rows-1 grid-cols-[auto_minmax(0,1fr)]',
|
||||
layout === 'vertical' && 'grid-rows-[auto_minmax(0,1fr)] grid-cols-1',
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import classNames from 'classnames';
|
||||
import type { CSSProperties, KeyboardEvent, ReactNode } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import type {
|
||||
CSSProperties,
|
||||
KeyboardEvent,
|
||||
ReactNode} from 'react';
|
||||
import React, {
|
||||
lazy,
|
||||
Suspense,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { generateId } from '../../lib/generateId';
|
||||
import { Portal } from '../Portal';
|
||||
|
||||
const Portal = lazy(() => import('../Portal').then((m) => ({ default: m.Portal })));
|
||||
|
||||
export interface TooltipProps {
|
||||
children: ReactNode;
|
||||
content: ReactNode;
|
||||
tabIndex?: number,
|
||||
tabIndex?: number;
|
||||
size?: 'md' | 'lg';
|
||||
}
|
||||
|
||||
@@ -66,7 +75,7 @@ export function Tooltip({ children, content, tabIndex, size = 'md' }: TooltipPro
|
||||
const id = useRef(`tooltip-${generateId()}`);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Suspense>
|
||||
<Portal name="tooltip">
|
||||
<div
|
||||
ref={tooltipRef}
|
||||
@@ -105,7 +114,7 @@ export function Tooltip({ children, content, tabIndex, size = 'md' }: TooltipPro
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
</>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
// AutoScrollWhileDragging.tsx
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useDragLayer } from 'react-dnd';
|
||||
|
||||
type Props = {
|
||||
container: HTMLElement | null | undefined;
|
||||
edgeDistance?: number;
|
||||
maxSpeedPerFrame?: number;
|
||||
};
|
||||
|
||||
export function AutoScrollWhileDragging({
|
||||
container,
|
||||
edgeDistance = 30,
|
||||
maxSpeedPerFrame = 6,
|
||||
}: Props) {
|
||||
const rafId = useRef<number | null>(null);
|
||||
|
||||
const { isDragging, pointer } = useDragLayer((monitor) => ({
|
||||
isDragging: monitor.isDragging(),
|
||||
pointer: monitor.getClientOffset(), // { x, y } | null
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (!container || !isDragging) {
|
||||
if (rafId.current != null) cancelAnimationFrame(rafId.current);
|
||||
rafId.current = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const tick = () => {
|
||||
if (!container || !isDragging || !pointer) return;
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
const y = pointer.y;
|
||||
|
||||
// Compute vertical speed based on proximity to edges
|
||||
let dy = 0;
|
||||
if (y < rect.top + edgeDistance) {
|
||||
const t = (rect.top + edgeDistance - y) / edgeDistance; // 0..1
|
||||
dy = -Math.min(maxSpeedPerFrame, Math.ceil(t * maxSpeedPerFrame));
|
||||
} else if (y > rect.bottom - edgeDistance) {
|
||||
const t = (y - (rect.bottom - edgeDistance)) / edgeDistance; // 0..1
|
||||
dy = Math.min(maxSpeedPerFrame, Math.ceil(t * maxSpeedPerFrame));
|
||||
}
|
||||
|
||||
if (dy !== 0) {
|
||||
// Only scroll if there’s more content in that direction
|
||||
const prev = container.scrollTop;
|
||||
container.scrollTop = prev + dy;
|
||||
}
|
||||
|
||||
rafId.current = requestAnimationFrame(tick);
|
||||
};
|
||||
|
||||
rafId.current = requestAnimationFrame(tick);
|
||||
return () => {
|
||||
if (rafId.current != null) cancelAnimationFrame(rafId.current);
|
||||
rafId.current = null;
|
||||
};
|
||||
}, [container, isDragging, pointer, edgeDistance, maxSpeedPerFrame]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { forwardRef, memo, useCallback, useImperativeHandle, useMemo, useRef } f
|
||||
import { useKey, useKeyPressEvent } from 'react-use';
|
||||
import type { HotkeyAction, HotKeyOptions } from '../../../hooks/useHotKey';
|
||||
import { useHotKey } from '../../../hooks/useHotKey';
|
||||
import { computeSideForDragMove } from '../../../lib/dnd';
|
||||
import { jotaiStore } from '../../../lib/jotai';
|
||||
import type { ContextMenuProps } from '../Dropdown';
|
||||
import {
|
||||
@@ -24,7 +25,7 @@ import {
|
||||
selectedIdsFamily,
|
||||
} from './atoms';
|
||||
import type { SelectableTreeNode, TreeNode } from './common';
|
||||
import { computeSideForDragMove, equalSubtree, getSelectedItems, hasAncestor } from './common';
|
||||
import { equalSubtree, getSelectedItems, hasAncestor } from './common';
|
||||
import { TreeDragOverlay } from './TreeDragOverlay';
|
||||
import type { TreeItemProps } from './TreeItem';
|
||||
import type { TreeItemListProps } from './TreeItemList';
|
||||
@@ -255,7 +256,7 @@ function TreeInner<T extends { id: string }>(
|
||||
}
|
||||
const node = selectableItem.node;
|
||||
|
||||
const side = computeSideForDragMove(node, e);
|
||||
const side = computeSideForDragMove(node.item.id, e);
|
||||
|
||||
const item = node.item;
|
||||
let hoveredParent = node.parent;
|
||||
|
||||
@@ -5,13 +5,13 @@ import { useAtomValue } from 'jotai';
|
||||
import { selectAtom } from 'jotai/utils';
|
||||
import type { MouseEvent, PointerEvent } from 'react';
|
||||
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { computeSideForDragMove } from '../../../lib/dnd';
|
||||
import { jotaiStore } from '../../../lib/jotai';
|
||||
import type { ContextMenuProps, DropdownItem } from '../Dropdown';
|
||||
import { ContextMenu } from '../Dropdown';
|
||||
import { Icon } from '../Icon';
|
||||
import { collapsedFamily, isCollapsedFamily, isLastFocusedFamily, isSelectedFamily } from './atoms';
|
||||
import type { TreeNode } from './common';
|
||||
import { computeSideForDragMove } from './common';
|
||||
import type { TreeProps } from './Tree';
|
||||
import { TreeIndentGuide } from './TreeIndentGuide';
|
||||
|
||||
@@ -161,7 +161,7 @@ function TreeItem_<T extends { id: string }>({
|
||||
clearDropHover();
|
||||
},
|
||||
onDragMove(e: DragMoveEvent) {
|
||||
const side = computeSideForDragMove(node, e);
|
||||
const side = computeSideForDragMove(node.item.id, e);
|
||||
const isFolder = node.children != null;
|
||||
const hasChildren = (node.children?.length ?? 0) > 0;
|
||||
const isCollapsed = jotaiStore.get(isCollapsedFamily({ treeId, itemId: node.item.id }));
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { DragMoveEvent } from '@dnd-kit/core';
|
||||
import { jotaiStore } from '../../../lib/jotai';
|
||||
import { selectedIdsFamily } from './atoms';
|
||||
|
||||
export interface TreeNode<T extends { id: string } > {
|
||||
export interface TreeNode<T extends { id: string }> {
|
||||
children?: TreeNode<T>[];
|
||||
item: T;
|
||||
parent: TreeNode<T> | null;
|
||||
@@ -48,25 +47,3 @@ export function hasAncestor<T extends { id: string }>(node: TreeNode<T>, ancesto
|
||||
// Check parents recursively
|
||||
return hasAncestor(node.parent, ancestorId);
|
||||
}
|
||||
|
||||
export function computeSideForDragMove<T extends { id: string }>(
|
||||
node: TreeNode<T>,
|
||||
e: DragMoveEvent,
|
||||
): 'above' | 'below' | null {
|
||||
if (e.over == null || e.over.id !== node.item.id) {
|
||||
return null;
|
||||
}
|
||||
if (e.active.rect.current.initial == null) return null;
|
||||
|
||||
const overRect = e.over.rect;
|
||||
const activeTop =
|
||||
e.active.rect.current.translated?.top ?? e.active.rect.current.initial.top + e.delta.y;
|
||||
const pointerY = activeTop + e.active.rect.current.initial.height / 2;
|
||||
|
||||
const hoverTop = overRect.top;
|
||||
const hoverBottom = overRect.bottom;
|
||||
const hoverMiddleY = (hoverBottom - hoverTop) / 2;
|
||||
const hoverClientY = pointerY - hoverTop;
|
||||
|
||||
return hoverClientY < hoverMiddleY ? 'above' : 'below';
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Button } from '../core/Button';
|
||||
import type { DropdownItem } from '../core/Dropdown';
|
||||
import { Dropdown } from '../core/Dropdown';
|
||||
import type { EditorProps } from '../core/Editor/Editor';
|
||||
import { Editor } from '../core/Editor/Editor';
|
||||
import { Editor } from '../core/Editor/LazyEditor';
|
||||
import { FormattedError } from '../core/FormattedError';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { Separator } from '../core/Separator';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { AutoScroller } from '../core/AutoScroller';
|
||||
import { Banner } from '../core/Banner';
|
||||
import { Button } from '../core/Button';
|
||||
import type { EditorProps } from '../core/Editor/Editor';
|
||||
import { Editor } from '../core/Editor/Editor';
|
||||
import { Editor } from '../core/Editor/LazyEditor';
|
||||
import { Icon } from '../core/Icon';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
import { Separator } from '../core/Separator';
|
||||
|
||||
@@ -3,10 +3,19 @@ import 'react-pdf/dist/Page/AnnotationLayer.css';
|
||||
import { convertFileSrc } from '@tauri-apps/api/core';
|
||||
import './PdfViewer.css';
|
||||
import type { PDFDocumentProxy } from 'pdfjs-dist';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Document, Page } from 'react-pdf';
|
||||
import React, { lazy, useRef, useState } from 'react';
|
||||
import { useContainerSize } from '../../hooks/useContainerQuery';
|
||||
|
||||
const Document = lazy(() => import('react-pdf').then((m) => ({ default: m.Document })));
|
||||
const Page = lazy(() => import('react-pdf').then((m) => ({ default: m.Page })));
|
||||
|
||||
import('react-pdf').then(({ pdfjs }) => {
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||
'pdfjs-dist/build/pdf.worker.min.mjs',
|
||||
import.meta.url,
|
||||
).toString();
|
||||
});
|
||||
|
||||
interface Props {
|
||||
bodyPath: string;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useDebouncedValue } from '../../hooks/useDebouncedValue';
|
||||
import { useFormatText } from '../../hooks/useFormatText';
|
||||
import { useResponseBodyText } from '../../hooks/useResponseBodyText';
|
||||
import type { EditorProps } from '../core/Editor/Editor';
|
||||
import { Editor } from '../core/Editor/Editor';
|
||||
import { Editor } from '../core/Editor/LazyEditor';
|
||||
import { hyperlink } from '../core/Editor/hyperlink/extension';
|
||||
import { IconButton } from '../core/IconButton';
|
||||
import { Input } from '../core/Input';
|
||||
|
||||
23
src-web/lib/dnd.ts
Normal file
23
src-web/lib/dnd.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { DragMoveEvent } from '@dnd-kit/core';
|
||||
|
||||
export function computeSideForDragMove(
|
||||
id: string,
|
||||
e: DragMoveEvent,
|
||||
): 'above' | 'below' | null {
|
||||
if (e.over == null || e.over.id !== id) {
|
||||
return null;
|
||||
}
|
||||
if (e.active.rect.current.initial == null) return null;
|
||||
|
||||
const overRect = e.over.rect;
|
||||
const activeTop =
|
||||
e.active.rect.current.translated?.top ?? e.active.rect.current.initial.top + e.delta.y;
|
||||
const pointerY = activeTop + e.active.rect.current.initial.height / 2;
|
||||
|
||||
const hoverTop = overRect.top;
|
||||
const hoverBottom = overRect.bottom;
|
||||
const hoverMiddleY = (hoverBottom - hoverTop) / 2;
|
||||
const hoverClientY = pointerY - hoverTop;
|
||||
|
||||
return hoverClientY < hoverMiddleY ? 'above' : 'below';
|
||||
}
|
||||
@@ -10,13 +10,6 @@ import { initGlobalListeners } from './lib/initGlobalListeners';
|
||||
import { jotaiStore } from './lib/jotai';
|
||||
import { router } from './lib/router';
|
||||
|
||||
import('react-pdf').then(({ pdfjs }) => {
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||
'pdfjs-dist/build/pdf.worker.min.mjs',
|
||||
import.meta.url,
|
||||
).toString();
|
||||
});
|
||||
|
||||
// Hide decorations here because it doesn't work in Rust for some reason (bug?)
|
||||
const osType = type();
|
||||
if (osType !== 'macos') {
|
||||
|
||||
@@ -23,10 +23,9 @@
|
||||
"@replit/codemirror-emacs": "^6.1.0",
|
||||
"@replit/codemirror-vim": "^6.3.0",
|
||||
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tanstack/react-query": "^5.76.1",
|
||||
"@tanstack/react-router": "^1.120.3",
|
||||
"@tanstack/react-virtual": "^3.13.8",
|
||||
"@tanstack/react-query": "^5.90.5",
|
||||
"@tanstack/react-router": "^1.133.13",
|
||||
"@tanstack/react-virtual": "^3.13.12",
|
||||
"@tauri-apps/api": "^2.8.0",
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.3.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.4.0",
|
||||
@@ -56,8 +55,6 @@
|
||||
"parse-color": "^1.0.0",
|
||||
"react": "^19.1.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-touch-backend": "^16.0.1",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-pdf": "^10.0.1",
|
||||
@@ -86,6 +83,7 @@
|
||||
"@types/whatwg-mimetype": "^3.0.2",
|
||||
"@vitejs/plugin-react": "^4.6.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"decompress": "^4.2.1",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
"internal-ip": "^8.0.0",
|
||||
|
||||
@@ -3,36 +3,35 @@ import { createRootRoute, Outlet } from '@tanstack/react-router';
|
||||
import { type } from '@tauri-apps/plugin-os';
|
||||
import classNames from 'classnames';
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
import { domAnimation, LazyMotion, MotionConfig } from 'motion/react';
|
||||
import React, { Suspense } from 'react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { TouchBackend } from 'react-dnd-touch-backend';
|
||||
import { Dialogs } from '../components/Dialogs';
|
||||
import { LazyMotion, MotionConfig } from 'motion/react';
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import { GlobalHooks } from '../components/GlobalHooks';
|
||||
import RouteError from '../components/RouteError';
|
||||
import { Toasts } from '../components/Toasts';
|
||||
import { jotaiStore } from '../lib/jotai';
|
||||
import { queryClient } from '../lib/queryClient';
|
||||
|
||||
const Toasts = lazy(() => import('../components/Toasts').then((m) => ({ default: m.Toasts })));
|
||||
const Dialogs = lazy(() => import('../components/Dialogs').then((m) => ({ default: m.Dialogs })));
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: RouteComponent,
|
||||
errorComponent: RouteError,
|
||||
});
|
||||
|
||||
const motionFeatures = () => import('framer-motion').then((mod) => mod.domAnimation);
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<JotaiProvider store={jotaiStore}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<LazyMotion features={domAnimation}>
|
||||
<LazyMotion strict features={motionFeatures}>
|
||||
<MotionConfig transition={{ duration: 0.1 }}>
|
||||
<DndProvider backend={TouchBackend} options={{ enableMouseEvents: true }}>
|
||||
<Suspense>
|
||||
<GlobalHooks />
|
||||
<Toasts />
|
||||
<Dialogs />
|
||||
<Layout />
|
||||
</Suspense>
|
||||
</DndProvider>
|
||||
<Suspense>
|
||||
<Toasts />
|
||||
<Dialogs />
|
||||
</Suspense>
|
||||
<Layout />
|
||||
<GlobalHooks />
|
||||
</MotionConfig>
|
||||
</LazyMotion>
|
||||
</QueryClientProvider>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
import { TanStackRouterVite } from '@tanstack/router-plugin/vite';
|
||||
import { tanstackRouter } from '@tanstack/router-plugin/vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||
import { internalIpV4 } from 'internal-ip';
|
||||
@@ -26,7 +26,8 @@ export default defineConfig(async () => ({
|
||||
plugins: [
|
||||
wasm(),
|
||||
reactRefresh.configs.vite,
|
||||
TanStackRouterVite({
|
||||
tanstackRouter({
|
||||
target: 'react',
|
||||
routesDirectory: './routes',
|
||||
generatedRouteTree: './routeTree.gen.ts',
|
||||
autoCodeSplitting: true,
|
||||
@@ -44,6 +45,14 @@ export default defineConfig(async () => ({
|
||||
build: {
|
||||
outDir: '../dist',
|
||||
emptyOutDir: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
// Make chunk names readable
|
||||
chunkFileNames: 'assets/chunk-[name]-[hash].js',
|
||||
entryFileNames: 'assets/entry-[name]-[hash].js',
|
||||
assetFileNames: 'assets/asset-[name]-[hash][extname]',
|
||||
},
|
||||
},
|
||||
},
|
||||
clearScreen: false,
|
||||
server: {
|
||||
|
||||
Reference in New Issue
Block a user