modals component and model and TOS modal (#164)

Co-authored-by: Sylvia Crowe <software@oneirocosm.com>
Co-authored-by: sawka <mike.sawka@gmail.com>
This commit is contained in:
Red J Adaya 2024-07-31 02:44:19 +08:00 committed by GitHub
parent f3f272a47b
commit 9233b3dbd7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 1018 additions and 65 deletions

View File

@ -6,6 +6,7 @@ import { defineConfig } from "electron-vite";
import flow from "rollup-plugin-flow";
import { ViteImageOptimizer } from "vite-plugin-image-optimizer";
import { viteStaticCopy } from "vite-plugin-static-copy";
import svgr from "vite-plugin-svgr";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
@ -65,6 +66,10 @@ export default defineConfig({
viteStaticCopy({
targets: [{ src: "node_modules/monaco-editor/min/vs/*", dest: "monaco" }],
}),
svgr({
svgrOptions: { exportType: "default", ref: true, svgo: false, titleProp: true },
include: "**/*.svg",
}),
],
},
});

View File

@ -87,3 +87,7 @@ body {
display: block;
}
}
a {
color: var(--accent-color);
}

View File

@ -0,0 +1,34 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="34" viewBox="0 0 48 34" fill="none">
<path
d="M15.4158 15.5474C13.148 15.5474 11.8942 17.0243 11.2121 20.3289L0.960938 18.852C2.21467 8.03354 6.41837 2.79046 14.7336 2.79046C20.8916 2.79046 26.9206 7.46123 29.8706 7.46123C32.1568 7.46123 33.3921 5.87353 34.0743 2.67969L44.3254 4.15661C43.1823 14.9935 38.868 20.2366 30.5528 20.2366C24.3025 20.2182 18.4948 15.5474 15.4158 15.5474Z"
fill="url(#paint0_linear_1073_4777)"
/>
<path
d="M18.322 26.1166C16.0542 26.1166 14.8005 27.5935 14.1183 30.8982L3.86719 29.4212C5.12092 18.6028 9.32462 13.3412 17.6398 13.3412C23.7795 13.3412 29.8269 18.012 32.7768 18.012C35.0631 18.012 36.2984 16.4243 36.9806 13.2305L47.2317 14.7074C46.0886 25.5443 41.7742 30.7874 33.459 30.7874C27.2088 30.7874 21.401 26.1166 18.322 26.1166Z"
fill="url(#paint1_linear_1073_4777)"
/>
<defs>
<linearGradient
id="paint0_linear_1073_4777"
x1="0.963297"
y1="11.5059"
x2="44.3342"
y2="11.5059"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.1418" stop-color="#1F4D22" />
<stop offset="0.8656" stop-color="#418D31" />
</linearGradient>
<linearGradient
id="paint1_linear_1073_4777"
x1="3.87034"
y1="22.0719"
x2="47.2413"
y2="22.0719"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.2223" stop-color="#418D31" />
<stop offset="0.7733" stop-color="#58C142" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -7,7 +7,7 @@
left: 0;
width: 100vw;
height: 100%;
z-index: 100;
z-index: var(--zindex-elem-modal);
background-color: rgba(21, 23, 21, 0.7);
.modal {

View File

@ -0,0 +1,61 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
.check-toggle-wrapper {
user-select: none;
display: flex;
height: 100%;
align-items: center;
justify-content: center;
.checkbox-toggle {
position: relative;
display: inline-block;
width: 32px;
height: 20px;
input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
content: "";
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: var(--toggle-bg-color);
transition: 0.5s;
border-radius: 33px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 3px;
bottom: 2px;
background-color: var(--toggle-thumb-color);
transition: 0.5s;
border-radius: 50%;
}
input:checked + .slider {
background-color: var(--toggle-checked-bg-color);
}
input:checked + .slider:before {
transform: translateX(11px);
}
}
label {
cursor: pointer;
padding: 0 5px;
}
}

View File

@ -0,0 +1,37 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import "./toggle.less";
interface ToggleProps {
checked: boolean;
onChange: (value: boolean) => void;
label?: string;
id?: string;
}
const Toggle = ({ checked, onChange, label, id }: ToggleProps) => {
const handleChange = (e: any) => {
if (onChange != null) {
onChange(e.target.checked);
}
};
const inputId = id || `toggle-${Math.random().toString(36).substr(2, 9)}`;
return (
<div className="check-toggle-wrapper">
<div className="checkbox-toggle">
<input id={inputId} type="checkbox" checked={checked} onChange={handleChange} />
<span className="slider" />
</div>
{label && (
<label htmlFor={inputId} className="toggle-label">
{label}
</label>
)}
</div>
);
};
export { Toggle };

View File

@ -3,5 +3,5 @@
.window-drag {
-webkit-app-region: drag;
z-index: 100;
z-index: var(--zindex-window-drag);
}

View File

@ -0,0 +1,75 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
.modal-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
z-index: var(--zindex-modal-wrapper);
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(21, 23, 21, 0.7);
z-index: var(--zindex-modal-backdrop);
}
}
.modal {
z-index: var(--zindex-modal);
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 16px;
border-radius: 10px;
background: var(--modal-bg-color);
border: 1px solid var(--border-color);
box-shadow: 0px 5px 5px 5px rgba(0, 0, 0, 0.1);
.modal-header {
width: 100%;
display: flex;
align-items: center;
padding: 12px 14px 12px 20px;
justify-content: space-between;
line-height: 20px;
border-bottom: 1px solid var(--modal-header-bottom-border-color);
user-select: none;
.modal-title {
color: var(--main-text-color);
font-size: var(--title-font-size);
}
button {
padding-right: 4px !important;
i {
font-size: 18px;
}
}
}
.modal-body {
width: 100%;
padding: 0px 20px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
width: 100%;
padding: 0 20px 20px;
button:last-child {
margin-left: 8px;
}
}
}

View File

@ -0,0 +1,121 @@
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { Button } from "@/app/element/button";
import ReactDOM from "react-dom";
import "./modal.less";
interface ModalHeaderProps {
title: React.ReactNode;
description?: string;
onClose?: () => void;
}
const ModalHeader = ({ onClose, title, description }: ModalHeaderProps) => (
<header className="modal-header">
{typeof title === "string" ? <h3 className="modal-title">{title}</h3> : title}
{description && <p>{description}</p>}
{onClose && (
<Button className="secondary ghost" onClick={onClose} title="Close (ESC)">
<i className="fa-sharp fa-solid fa-xmark"></i>
</Button>
)}
</header>
);
interface ModalContentProps {
children: React.ReactNode;
}
function ModalContent({ children }: ModalContentProps) {
return <div className="modal-content">{children}</div>;
}
interface ModalFooterProps {
okLabel?: string;
cancelLabel?: string;
onOk?: () => void;
onCancel?: () => void;
}
const ModalFooter = ({ onCancel, onOk, cancelLabel = "Cancel", okLabel = "Ok" }: ModalFooterProps) => (
<footer className="modal-footer">
{onCancel && (
<Button className="secondary" onClick={onCancel}>
{cancelLabel}
</Button>
)}
{onOk && (
<Button className="primary" onClick={onOk}>
{okLabel}
</Button>
)}
</footer>
);
interface ModalProps {
title: string;
children?: React.ReactNode;
description?: string;
okLabel?: string;
cancelLabel?: string;
className?: string;
onClickBackdrop?: () => void;
onOk?: () => void;
onCancel?: () => void;
onClose?: () => void;
}
const Modal = ({
children,
className,
title,
description,
cancelLabel,
okLabel,
onCancel,
onOk,
onClose,
onClickBackdrop,
}: ModalProps) => {
const renderBackdrop = (onClick) => <div className="modal-backdrop" onClick={onClick}></div>;
const renderModal = () => (
<div className="modal-wrapper">
{renderBackdrop(onClickBackdrop)}
<div className={`modal ${className}`}>
<ModalHeader title={title} onClose={onClose} description={description} />
<ModalContent>{children}</ModalContent>
<ModalFooter onCancel={onCancel} onOk={onOk} cancelLabel={cancelLabel} okLabel={okLabel} />
</div>
</div>
);
return ReactDOM.createPortal(renderModal(), document.getElementById("main"));
};
interface FlexiModalProps {
children?: React.ReactNode;
className?: string;
onClickBackdrop?: () => void;
}
const FlexiModal = ({ children, className, onClickBackdrop }: FlexiModalProps) => {
const renderBackdrop = (onClick) => <div className="modal-backdrop" onClick={onClick}></div>;
const renderModal = () => (
<div className="modal-wrapper">
{renderBackdrop(onClickBackdrop)}
<div className={`modal ${className}`}>{children}</div>
</div>
);
return ReactDOM.createPortal(renderModal(), document.getElementById("main"));
};
FlexiModal.Header = ModalHeader;
FlexiModal.Content = ModalContent;
FlexiModal.Footer = ModalFooter;
export { FlexiModal, Modal };

View File

@ -0,0 +1,14 @@
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { TosModal } from "./tos";
import { UserInputModal } from "./userinputmodal";
const modalRegistry: { [key: string]: React.ComponentType<any> } = {
[TosModal.displayName || "TosModal"]: TosModal,
[UserInputModal.displayName || "UserInputModal"]: UserInputModal,
};
export const getModalComponent = (key: string): React.ComponentType<any> | undefined => {
return modalRegistry[key];
};

View File

@ -0,0 +1,26 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { atoms } from "@/store/global";
import { modalsModel } from "@/store/modalmodel";
import * as jotai from "jotai";
import { getModalComponent } from "./modalregistry";
import { TosModal } from "./tos";
const ModalsRenderer = () => {
const clientData = jotai.useAtomValue(atoms.client);
const [modals] = jotai.useAtom(modalsModel.modalsAtom);
const rtn: JSX.Element[] = [];
for (const modal of modals) {
const ModalComponent = getModalComponent(modal.displayName);
if (ModalComponent) {
rtn.push(<ModalComponent key={modal.displayName} {...modal.props} />);
}
}
if (!clientData.tosagreed) {
rtn.push(<TosModal key={TosModal.displayName} />);
}
return <>{rtn}</>;
};
export { ModalsRenderer };

View File

@ -0,0 +1,127 @@
.tos-modal {
width: 640px;
.modal-inner {
padding: 40px 76px;
gap: 32px;
display: flex;
flex-direction: column;
header.tos-header {
flex-direction: column;
gap: var(--sizing-sm, 12px);
border-bottom: none;
padding: 0;
margin-bottom: 20px;
.logo {
margin-bottom: 10px;
}
.modal-title {
text-align: center;
font-size: 25px;
font-weight: 400;
color: var(--main-text-color);
}
.modal-subtitle {
color: var(--main-text-color);
text-align: center;
font-style: normal;
font-weight: 300;
line-height: 20px;
}
}
.tos-content {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 32px;
width: 100%;
margin-bottom: 0;
.content-section {
display: flex;
width: 100%;
align-items: center;
gap: 32px;
.icon-wrapper {
.icon {
font-size: 32px;
color: rgba(255, 255, 255, 0.5);
}
.fa-people-group {
font-size: 25px;
}
}
.content-section-inner {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
flex: 1 0 0;
.content-section-title {
color: var(--main-text-color);
font-style: normal;
line-height: 18px;
font-size: 16px;
margin-bottom: 5px;
}
.content-section-text {
color: rgba(255, 255, 255, 0.7);
font-style: normal;
line-height: 20px;
}
.content-section-field {
display: flex;
align-items: center;
gap: 8px;
}
.check-toggle-wrapper {
margin-top: 5px;
label {
font-size: 13px;
}
}
}
}
}
footer {
.content-section-text {
text-align: center;
color: rgba(255, 255, 255, 0.7);
font-size: 11px;
}
.button-wrapper {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
button {
font-size: 14px;
margin-bottom: 28px;
color: #000;
font-weight: 600;
}
button.disabled-button {
cursor: default;
}
}
}
}
}

View File

@ -0,0 +1,97 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import Logo from "@/app/asset/logo.svg";
import { Button } from "@/app/element/button";
import * as services from "@/store/services";
import { FlexiModal } from "./modal";
import "./tos.less";
const TosModal = () => {
const acceptTos = () => {
services.ClientService.AgreeTos();
};
return (
<FlexiModal className="tos-modal">
<div className="modal-inner">
<header className="modal-header tos-header unselectable">
<div className="logo">
<Logo />
</div>
<div className="modal-title">Welcome to Wave Terminal!</div>
</header>
<div className="modal-content tos-content unselectable">
<div className="content-section">
<div className="icon-wrapper">
<a target="_blank" href="https://github.com/wavetermdev/waveterm" rel={"noopener"}>
<i className="icon fa-brands fa-github"></i>
</a>
</div>
<div className="content-section-inner">
<div className="content-section-title">Support us on GitHub</div>
<div className="content-section-text">
We're <i>open source</i> and committed to providing a free terminal for individual
users. Please show your support by giving us a star on{" "}
<a target="_blank" href="https://github.com/wavetermdev/waveterm" rel={"noopener"}>
Github&nbsp;(wavetermdev/waveterm)
</a>
</div>
</div>
</div>
<div className="content-section">
<div className="icon-wrapper">
<a target="_blank" href="https://github.com/wavetermdev/thenextwave" rel={"noopener"}>
<i className="icon fa-solid fa-people-group"></i>
</a>
</div>
<div className="content-section-inner">
<div className="content-section-title">Join our Community</div>
<div className="content-section-text">
Get help, submit feature requests, report bugs, or just chat with fellow terminal
enthusiasts.
<br />
<a target="_blank" href="https://discord.gg/XfvZ334gwU" rel={"noopener"}>
Join the Wave&nbsp;Discord&nbsp;Channel
</a>
</div>
</div>
</div>
<div className="content-section">
<div className="icon-wrapper">
<i className="icon fa-solid fa-chart-line"></i>
</div>
<div className="content-section-inner">
<div className="content-section-title">Telemetry</div>
<div className="content-section-text">
We collect minimal anonymous
<a
target="_blank"
href="https://docs.waveterm.dev/reference/telemetry"
rel={"noopener"}
>
&nbsp;telemetry data&nbsp;
</a>
to help us understand how people are using Wave.
</div>
</div>
</div>
</div>
<footer className="unselectable">
<div className="button-wrapper">
<Button onClick={acceptTos}>Get Started</Button>
</div>
<div className="content-section-text">
By continuing, I accept the&nbsp;
<a href="https://www.waveterm.dev/tos">Terms of Service</a>
</div>
</footer>
</div>
</FlexiModal>
);
};
TosModal.displayName = "TosModal";
export { TosModal };

View File

@ -6,12 +6,13 @@
flex-direction: column;
justify-content: space-between;
gap: 1rem;
margin: 0 1rem 1rem 1rem;
font: var(--fixed-font);
color: var(--main-text-color);
.userinput-markdown {
color: var(--main-text-color);
color: inherit;
}
.userinput-text {
@ -25,6 +26,7 @@
border: var(--border-color);
padding: 5px 0 5px 16px;
min-height: 30px;
color: inherit;
&:hover {
cursor: text;

View File

@ -1,18 +1,16 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { Modal } from "@/app/modals/modal";
import { Markdown } from "@/element/markdown";
import { WaveModal } from "@/element/modal";
import { atoms } from "@/store/global";
import { modalsModel } from "@/store/modalmodel";
import * as keyutil from "@/util/keyutil";
import * as jotai from "jotai";
import * as React from "react";
import { UserInputService } from "../store/services";
import "./userinputmodal.less";
export const UserInputModal = (userInputRequest: UserInputRequest) => {
const setModals = jotai.useSetAtom(atoms.userInput);
const UserInputModal = (userInputRequest: UserInputRequest) => {
const [responseText, setResponseText] = React.useState("");
const [countdown, setCountdown] = React.useState(Math.floor(userInputRequest.timeoutms / 1000));
const checkboxStatus = React.useRef(false);
@ -23,10 +21,7 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
requestid: userInputRequest.requestid,
errormsg: "Canceled by the user",
});
setModals((prev) => {
prev.pop();
return [...prev];
});
modalsModel.popModal();
}, [responseText, userInputRequest]);
const handleSendText = React.useCallback(() => {
@ -36,10 +31,7 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
text: responseText,
checkboxstat: checkboxStatus.current,
});
setModals((prev) => {
prev.pop();
return [...prev];
});
modalsModel.popModal();
}, [responseText, userInputRequest]);
const handleSendConfirm = React.useCallback(() => {
@ -49,10 +41,7 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
confirm: true,
checkboxstat: checkboxStatus.current,
});
setModals((prev) => {
prev.pop();
return [...prev];
});
modalsModel.popModal();
}, [userInputRequest]);
const handleSubmit = React.useCallback(() => {
@ -119,15 +108,19 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
}, [countdown]);
return (
<WaveModal
<Modal
title={userInputRequest.title + ` (${countdown}s)`}
onSubmit={() => handleSubmit()}
onOk={() => handleSubmit()}
onCancel={() => handleSendCancel()}
>
<div className="userinput-body">
{queryText}
{inputBox}
</div>
</WaveModal>
</Modal>
);
};
UserInputModal.displayName = "UserInputModal";
export { UserInputModal };

View File

@ -16,6 +16,7 @@ import * as layoututil from "@/util/layoututil";
import { produce } from "immer";
import * as jotai from "jotai";
import * as rxjs from "rxjs";
import { modalsModel } from "./modalmodel";
import * as services from "./services";
import * as WOS from "./wos";
import { WSControl } from "./ws";
@ -101,7 +102,6 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
}
return windowData.activetabid;
});
const userInputAtom = jotai.atom([]) as jotai.PrimitiveAtom<Array<UserInputRequest>>;
atoms = {
// initialized in wave.ts (will not be null inside of application)
windowId: windowIdAtom,
@ -113,7 +113,6 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
settingsConfigAtom: settingsConfigAtom,
tabAtom: tabAtom,
activeTabId: activeTabIdAtom,
userInput: userInputAtom,
isFullScreen: isFullScreenAtom,
};
}
@ -229,10 +228,8 @@ function handleWSEventMessage(msg: WSEventType) {
return;
}
if (msg.eventtype == "userinput") {
// handle user input
const data: UserInputRequest = msg.data;
console.log(data);
globalStore.set(atoms.userInput, (prev) => [...prev, data]);
modalsModel.pushModal("UserInputModal", { ...data });
return;
}
if (msg.eventtype == "blockfile") {

View File

@ -0,0 +1,41 @@
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as jotai from "jotai";
import { globalStore } from "./global";
class ModalsModel {
modalsAtom: jotai.PrimitiveAtom<Array<{ displayName: string; props?: any }>>;
constructor() {
this.modalsAtom = jotai.atom([]);
}
pushModal = (displayName: string, props?: any) => {
const modals = globalStore.get(this.modalsAtom);
globalStore.set(this.modalsAtom, [...modals, { displayName, props }]);
};
popModal = (callback?: () => void) => {
const modals = globalStore.get(this.modalsAtom);
if (modals.length > 0) {
const updatedModals = modals.slice(0, -1);
globalStore.set(this.modalsAtom, updatedModals);
if (callback) callback();
}
};
hasOpenModals(): boolean {
const modals = globalStore.get(this.modalsAtom);
return modals.length > 0;
}
isModalOpen(displayName: string): boolean {
const modals = globalStore.get(this.modalsAtom);
return modals.some((modal) => modal.displayName === displayName);
}
}
const modalsModel = new ModalsModel();
export { modalsModel };

View File

@ -22,6 +22,10 @@ export const BlockService = new BlockServiceType();
// clientservice.ClientService (client)
class ClientServiceType {
// @returns object updates
AgreeTos(): Promise<void> {
return WOS.callBackendService("client", "AgreeTos", Array.from(arguments))
}
FocusWindow(arg2: string): Promise<void> {
return WOS.callBackendService("client", "FocusWindow", Array.from(arguments))
}

View File

@ -46,7 +46,7 @@
left: 50%;
transform: translate3d(-50%, -50%, 0);
user-select: none;
z-index: 3;
z-index: var(--zindex-tab-name);
font-size: 11px;
font-weight: 500;
@ -71,7 +71,7 @@
justify-content: center;
cursor: pointer;
opacity: 0.5;
z-index: 3;
z-index: var(--zindex-tab-name);
&:hover {
opacity: 1;

View File

@ -38,4 +38,34 @@
/* z-index values */
--zindex-header-hover: 100;
--zindex-termstickers: 20;
--zindex-modal: 2;
--zindex-modal-wrapper: 500;
--zindex-modal-backdrop: 1;
--zindex-elem-modal: 100;
--zindex-window-drag: 100;
--zindex-tab-name: 3;
--zindex-layout-placeholder-container: 1;
--zindex-layout-overlay-container: 2;
// z-indexes in xterm.css
// xterm-helpers: 5
// xterm-helper-textarea: -5
// composition-view: 1
// xterm-message: 10
// xterm-decoration: 6
// xterm-decoration-top-layer: 7
// xterm-decoration-overview-ruler: 8
// xterm-decoration-top: 2
/* modal colors */
--modal-bg-color: rgb(26, 28, 26);
--modal-header-bottom-border-color: rgba(241, 246, 243, 0.15);
/* toggle colors */
--toggle-bg-color: var(--border-color);
--toggle-thumb-color: var(--main-text-color);
--toggle-checked-bg-color: var(--accent-color);
/* link color */
--link-color: #58c142;
}

View File

@ -1,9 +1,9 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { ModalsRenderer } from "@/app/modals/modalsrenderer";
import { TabBar } from "@/app/tab/tabbar";
import { TabContent } from "@/app/tab/tabcontent";
import { UserInputModal } from "@/element/userinputmodal";
import { atoms, createBlock } from "@/store/global";
import * as services from "@/store/services";
import * as util from "@/util/util";
@ -107,11 +107,10 @@ const Widgets = React.memo(() => {
const WorkspaceElem = React.memo(() => {
const windowData = jotai.useAtomValue(atoms.waveWindow);
const activeTabId = windowData?.activetabid;
const modals = jotai.useAtomValue(atoms.userInput);
const ws = jotai.useAtomValue(atoms.workspace);
return (
<div className="workspace">
{modals.length > 0 && <UserInputModal {...modals[modals.length - 1]} />}
<TabBar key={ws.oid} workspace={ws} />
<div className="workspace-tabcontent">
{activeTabId == "" ? (
@ -120,6 +119,7 @@ const WorkspaceElem = React.memo(() => {
<>
<TabContent key={activeTabId} tabId={activeTabId} />
<Widgets />
<ModalsRenderer />
</>
)}
</div>

View File

@ -21,11 +21,11 @@
}
.placeholder-container {
z-index: 1;
z-index: var(--zindex-layout-placeholder-container);
}
.overlay-container {
z-index: 2;
z-index: var(--zindex-layout-overlay-container);
}
.overlay-node {

View File

@ -15,7 +15,6 @@ declare global {
settingsConfigAtom: jotai.PrimitiveAtom<SettingsConfigType>; // driven from WOS, settings -- updated via WebSocket
tabAtom: jotai.Atom<Tab>; // driven from WOS
activeTabId: jotai.Atom<string>; // derrived from windowDataAtom
userInput: jotai.PrimitiveAtom<Array<UserInputRequest>>;
isFullScreen: jotai.PrimitiveAtom<boolean>;
};

View File

@ -63,6 +63,7 @@ declare global {
mainwindowid: string;
windowids: string[];
meta: MetaType;
tosagreed?: number;
};
// wshrpc.CommandAppendIJsonData

View File

@ -197,6 +197,16 @@ function getCrypto() {
}
}
/**
* Generates an external link by appending the given URL to the "https://extern?" endpoint.
*
* @param {string} url - The URL to be encoded and appended to the external link.
* @return {string} The generated external link.
*/
function makeExternLink(url: string): string {
return "https://extern?" + encodeURIComponent(url);
}
export {
base64ToArray,
base64ToString,
@ -209,6 +219,7 @@ export {
jotaiLoadableValue,
jsonDeepEqual,
lazy,
makeExternLink,
makeIconClass,
stringToBase64,
useAtomValueSafe,

View File

@ -67,6 +67,7 @@
"vite": "^5.3.5",
"vite-plugin-image-optimizer": "^1.1.8",
"vite-plugin-static-copy": "^1.0.6",
"vite-plugin-svgr": "^4.2.0",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^2.0.4"
},

View File

@ -73,3 +73,18 @@ func (cs *ClientService) FocusWindow(ctx context.Context, windowId string) error
client.WindowIds = utilfn.MoveSliceIdxToFront(client.WindowIds, winIdx)
return wstore.DBUpdate(ctx, client)
}
func (cs *ClientService) AgreeTos(ctx context.Context) (wstore.UpdatesRtnType, error) {
ctx = wstore.ContextWithUpdates(ctx)
clientData, err := wstore.DBGetSingleton[*wstore.Client](ctx)
if err != nil {
return nil, fmt.Errorf("error getting client data: %w", err)
}
timestamp := time.Now().UnixMilli()
clientData.TosAgreed = timestamp
err = wstore.DBUpdate(ctx, clientData)
if err != nil {
return nil, fmt.Errorf("error updating client data: %w", err)
}
return wstore.ContextGetUpdatesRtn(ctx), nil
}

View File

@ -349,8 +349,8 @@ func CreateWindow(ctx context.Context) (*Window, error) {
Y: 100,
},
WinSize: WinSize{
Width: 800,
Height: 600,
Width: 1200,
Height: 800,
},
}
err := DBInsert(ctx, window)

View File

@ -166,6 +166,7 @@ type Client struct {
MainWindowId string `json:"mainwindowid"` // deprecated
WindowIds []string `json:"windowids"`
Meta map[string]any `json:"meta"`
TosAgreed int64 `json:"tosagreed,omitempty"`
}
func (*Client) GetOType() string {

View File

@ -23,6 +23,10 @@
"@/element/*": ["frontend/app/element/*"],
"@/bindings/*": ["frontend/bindings/github.com/wavetermdev/thenextwave/pkg/service/*"],
"@/gopkg/*": ["frontend/bindings/github.com/wavetermdev/thenextwave/pkg/*"],
}
}
},
"types": [
"vite/client",
"vite-plugin-svgr/client"
]
},
}

303
yarn.lock
View File

@ -29,6 +29,16 @@ __metadata:
languageName: node
linkType: hard
"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/code-frame@npm:7.24.7"
dependencies:
"@babel/highlight": "npm:^7.24.7"
picocolors: "npm:^1.0.0"
checksum: 10c0/ab0af539473a9f5aeaac7047e377cb4f4edd255a81d84a76058595f8540784cc3fbe8acf73f1e073981104562490aabfb23008cd66dc677a456a4ed5390fdde6
languageName: node
linkType: hard
"@babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/code-frame@npm:7.24.6"
@ -39,16 +49,6 @@ __metadata:
languageName: node
linkType: hard
"@babel/code-frame@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/code-frame@npm:7.24.7"
dependencies:
"@babel/highlight": "npm:^7.24.7"
picocolors: "npm:^1.0.0"
checksum: 10c0/ab0af539473a9f5aeaac7047e377cb4f4edd255a81d84a76058595f8540784cc3fbe8acf73f1e073981104562490aabfb23008cd66dc677a456a4ed5390fdde6
languageName: node
linkType: hard
"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/compat-data@npm:7.24.6"
@ -86,7 +86,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/core@npm:^7.24.7":
"@babel/core@npm:^7.21.3, @babel/core@npm:^7.24.7":
version: 7.24.9
resolution: "@babel/core@npm:7.24.9"
dependencies:
@ -1763,6 +1763,17 @@ __metadata:
languageName: node
linkType: hard
"@babel/types@npm:^7.21.3, @babel/types@npm:^7.24.8, @babel/types@npm:^7.24.9":
version: 7.24.9
resolution: "@babel/types@npm:7.24.9"
dependencies:
"@babel/helper-string-parser": "npm:^7.24.8"
"@babel/helper-validator-identifier": "npm:^7.24.7"
to-fast-properties: "npm:^2.0.0"
checksum: 10c0/4970b3481cab39c5c3fdb7c28c834df5c7049f3c7f43baeafe121bb05270ebf0da7c65b097abf314877f213baa591109c82204f30d66cdd46c22ece4a2f32415
languageName: node
linkType: hard
"@babel/types@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/types@npm:7.24.7"
@ -1774,17 +1785,6 @@ __metadata:
languageName: node
linkType: hard
"@babel/types@npm:^7.24.8, @babel/types@npm:^7.24.9":
version: 7.24.9
resolution: "@babel/types@npm:7.24.9"
dependencies:
"@babel/helper-string-parser": "npm:^7.24.8"
"@babel/helper-validator-identifier": "npm:^7.24.7"
to-fast-properties: "npm:^2.0.0"
checksum: 10c0/4970b3481cab39c5c3fdb7c28c834df5c7049f3c7f43baeafe121bb05270ebf0da7c65b097abf314877f213baa591109c82204f30d66cdd46c22ece4a2f32415
languageName: node
linkType: hard
"@base2/pretty-print-object@npm:1.0.1":
version: 1.0.1
resolution: "@base2/pretty-print-object@npm:1.0.1"
@ -2603,7 +2603,7 @@ __metadata:
languageName: node
linkType: hard
"@rollup/pluginutils@npm:^5.0.2":
"@rollup/pluginutils@npm:^5.0.2, @rollup/pluginutils@npm:^5.0.5":
version: 5.1.0
resolution: "@rollup/pluginutils@npm:5.1.0"
dependencies:
@ -3198,6 +3198,133 @@ __metadata:
languageName: node
linkType: hard
"@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/a50bd0baa34faf16bcba712091f94c7f0e230431fe99a9dfc3401fa92823ad3f68495b86ab9bf9044b53839e8c416cfbb37eb3f246ff33f261e0fa9ee1779c5b
languageName: node
linkType: hard
"@svgr/babel-plugin-remove-jsx-attribute@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-remove-jsx-attribute@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/8a98e59bd9971e066815b4129409932f7a4db4866834fe75677ea6d517972fb40b380a69a4413189f20e7947411f9ab1b0f029dd5e8068686a5a0188d3ccd4c7
languageName: node
linkType: hard
"@svgr/babel-plugin-remove-jsx-empty-expression@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-remove-jsx-empty-expression@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/517dcca75223bd05d3f056a8514dbba3031278bea4eadf0842c576d84f4651e7a4e0e7082d3ee4ef42456de0f9c4531d8a1917c04876ca64b014b859ca8f1bde
languageName: node
linkType: hard
"@svgr/babel-plugin-replace-jsx-attribute-value@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-replace-jsx-attribute-value@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/004bd1892053b7e9c1b0bb14acc44e77634ec393722b87b1e4fae53e2c35122a2dd0d5c15e9070dbeec274e22e7693a2b8b48506733a8009ee92b12946fcb10a
languageName: node
linkType: hard
"@svgr/babel-plugin-svg-dynamic-title@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-svg-dynamic-title@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/80e0a7fcf902f984c705051ca5c82ea6050ccbb70b651a8fea6d0eb5809e4dac274b49ea6be2d87f1eb9dfc0e2d6cdfffe1669ec2117f44b67a60a07d4c0b8b8
languageName: node
linkType: hard
"@svgr/babel-plugin-svg-em-dimensions@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-svg-em-dimensions@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/73e92c8277a89279745c0c500f59f083279a8dc30cd552b22981fade2a77628fb2bd2819ee505725fcd2e93f923e3790b52efcff409a159e657b46604a0b9a21
languageName: node
linkType: hard
"@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0":
version: 8.1.0
resolution: "@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/655ed6bc7a208ceaa4ecff0a54ccc36008c3cb31efa90d11e171cab325ebbb21aa78f09c7b65f9b3ddeda3a85f348c0c862902c48be13c14b4de165c847974e3
languageName: node
linkType: hard
"@svgr/babel-plugin-transform-svg-component@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/babel-plugin-transform-svg-component@npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/4ac00bb99a3db4ef05e4362f116a3c608ee365a2d26cf7318d8d41a4a5b30a02c80455cce0e62c65b60ed815b5d632bedabac2ccd4b56f998fadef5286e3ded4
languageName: node
linkType: hard
"@svgr/babel-preset@npm:8.1.0":
version: 8.1.0
resolution: "@svgr/babel-preset@npm:8.1.0"
dependencies:
"@svgr/babel-plugin-add-jsx-attribute": "npm:8.0.0"
"@svgr/babel-plugin-remove-jsx-attribute": "npm:8.0.0"
"@svgr/babel-plugin-remove-jsx-empty-expression": "npm:8.0.0"
"@svgr/babel-plugin-replace-jsx-attribute-value": "npm:8.0.0"
"@svgr/babel-plugin-svg-dynamic-title": "npm:8.0.0"
"@svgr/babel-plugin-svg-em-dimensions": "npm:8.0.0"
"@svgr/babel-plugin-transform-react-native-svg": "npm:8.1.0"
"@svgr/babel-plugin-transform-svg-component": "npm:8.0.0"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 10c0/49367d3ad0831f79b1056871b91766246f449d4d1168623af5e283fbaefce4a01d77ab00de6b045b55e956f9aae27895823198493cd232d88d3435ea4517ffc5
languageName: node
linkType: hard
"@svgr/core@npm:^8.1.0":
version: 8.1.0
resolution: "@svgr/core@npm:8.1.0"
dependencies:
"@babel/core": "npm:^7.21.3"
"@svgr/babel-preset": "npm:8.1.0"
camelcase: "npm:^6.2.0"
cosmiconfig: "npm:^8.1.3"
snake-case: "npm:^3.0.4"
checksum: 10c0/6a2f6b1bc79bce39f66f088d468985d518005fc5147ebf4f108570a933818b5951c2cb7da230ddff4b7c8028b5a672b2d33aa2acce012b8b9770073aa5a2d041
languageName: node
linkType: hard
"@svgr/hast-util-to-babel-ast@npm:8.0.0":
version: 8.0.0
resolution: "@svgr/hast-util-to-babel-ast@npm:8.0.0"
dependencies:
"@babel/types": "npm:^7.21.3"
entities: "npm:^4.4.0"
checksum: 10c0/f4165b583ba9eaf6719e598977a7b3ed182f177983e55f9eb55a6a73982d81277510e9eb7ab41f255151fb9ed4edd11ac4bef95dd872f04ed64966d8c85e0f79
languageName: node
linkType: hard
"@svgr/plugin-jsx@npm:^8.1.0":
version: 8.1.0
resolution: "@svgr/plugin-jsx@npm:8.1.0"
dependencies:
"@babel/core": "npm:^7.21.3"
"@svgr/babel-preset": "npm:8.1.0"
"@svgr/hast-util-to-babel-ast": "npm:8.0.0"
svg-parser: "npm:^2.0.4"
peerDependencies:
"@svgr/core": "*"
checksum: 10c0/07b4d9e00de795540bf70556fa2cc258774d01e97a12a26234c6fdf42b309beb7c10f31ee24d1a71137239347b1547b8bb5587d3a6de10669f95dcfe99cddc56
languageName: node
linkType: hard
"@swc/core-darwin-arm64@npm:1.7.0":
version: 1.7.0
resolution: "@swc/core-darwin-arm64@npm:1.7.0"
@ -4995,6 +5122,13 @@ __metadata:
languageName: node
linkType: hard
"camelcase@npm:^6.2.0":
version: 6.3.0
resolution: "camelcase@npm:6.3.0"
checksum: 10c0/0d701658219bd3116d12da3eab31acddb3f9440790c0792e0d398f0a520a6a4058018e546862b6fba89d7ae990efaeb97da71e1913e9ebf5a8b5621a3d55c710
languageName: node
linkType: hard
"caniuse-lite@npm:^1.0.30001587":
version: 1.0.30001617
resolution: "caniuse-lite@npm:1.0.30001617"
@ -5481,6 +5615,23 @@ __metadata:
languageName: node
linkType: hard
"cosmiconfig@npm:^8.1.3":
version: 8.3.6
resolution: "cosmiconfig@npm:8.3.6"
dependencies:
import-fresh: "npm:^3.3.0"
js-yaml: "npm:^4.1.0"
parse-json: "npm:^5.2.0"
path-type: "npm:^4.0.0"
peerDependencies:
typescript: ">=4.9.5"
peerDependenciesMeta:
typescript:
optional: true
checksum: 10c0/0382a9ed13208f8bfc22ca2f62b364855207dffdb73dc26e150ade78c3093f1cf56172df2dd460c8caf2afa91c0ed4ec8a88c62f8f9cd1cf423d26506aa8797a
languageName: node
linkType: hard
"crc@npm:^3.8.0":
version: 3.8.0
resolution: "crc@npm:3.8.0"
@ -6148,6 +6299,16 @@ __metadata:
languageName: node
linkType: hard
"dot-case@npm:^3.0.4":
version: 3.0.4
resolution: "dot-case@npm:3.0.4"
dependencies:
no-case: "npm:^3.0.4"
tslib: "npm:^2.0.3"
checksum: 10c0/5b859ea65097a7ea870e2c91b5768b72ddf7fa947223fd29e167bcdff58fe731d941c48e47a38ec8aa8e43044c8fbd15cd8fa21689a526bc34b6548197cd5b05
languageName: node
linkType: hard
"dotenv-expand@npm:^5.1.0":
version: 5.1.0
resolution: "dotenv-expand@npm:5.1.0"
@ -6389,6 +6550,15 @@ __metadata:
languageName: node
linkType: hard
"error-ex@npm:^1.3.1":
version: 1.3.2
resolution: "error-ex@npm:1.3.2"
dependencies:
is-arrayish: "npm:^0.2.1"
checksum: 10c0/ba827f89369b4c93382cfca5a264d059dfefdaa56ecc5e338ffa58a6471f5ed93b71a20add1d52290a4873d92381174382658c885ac1a2305f7baca363ce9cce
languageName: node
linkType: hard
"es-define-property@npm:^1.0.0":
version: 1.0.0
resolution: "es-define-property@npm:1.0.0"
@ -7950,7 +8120,7 @@ __metadata:
languageName: node
linkType: hard
"import-fresh@npm:^3.2.1":
"import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0":
version: 3.3.0
resolution: "import-fresh@npm:3.3.0"
dependencies:
@ -8065,6 +8235,13 @@ __metadata:
languageName: node
linkType: hard
"is-arrayish@npm:^0.2.1":
version: 0.2.1
resolution: "is-arrayish@npm:0.2.1"
checksum: 10c0/e7fb686a739068bb70f860b39b67afc62acc62e36bb61c5f965768abce1873b379c563e61dd2adad96ebb7edf6651111b385e490cf508378959b0ed4cac4e729
languageName: node
linkType: hard
"is-arrayish@npm:^0.3.1":
version: 0.3.2
resolution: "is-arrayish@npm:0.3.2"
@ -8463,6 +8640,13 @@ __metadata:
languageName: node
linkType: hard
"json-parse-even-better-errors@npm:^2.3.0":
version: 2.3.1
resolution: "json-parse-even-better-errors@npm:2.3.1"
checksum: 10c0/140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3
languageName: node
linkType: hard
"json-schema-traverse@npm:^0.4.1":
version: 0.4.1
resolution: "json-schema-traverse@npm:0.4.1"
@ -8607,6 +8791,13 @@ __metadata:
languageName: node
linkType: hard
"lines-and-columns@npm:^1.1.6":
version: 1.2.4
resolution: "lines-and-columns@npm:1.2.4"
checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d
languageName: node
linkType: hard
"locate-path@npm:^3.0.0":
version: 3.0.0
resolution: "locate-path@npm:3.0.0"
@ -8730,6 +8921,15 @@ __metadata:
languageName: node
linkType: hard
"lower-case@npm:^2.0.2":
version: 2.0.2
resolution: "lower-case@npm:2.0.2"
dependencies:
tslib: "npm:^2.0.3"
checksum: 10c0/3d925e090315cf7dc1caa358e0477e186ffa23947740e4314a7429b6e62d72742e0bbe7536a5ae56d19d7618ce998aba05caca53c2902bd5742fdca5fc57fd7b
languageName: node
linkType: hard
"lowercase-keys@npm:^2.0.0":
version: 2.0.0
resolution: "lowercase-keys@npm:2.0.0"
@ -9753,6 +9953,16 @@ __metadata:
languageName: node
linkType: hard
"no-case@npm:^3.0.4":
version: 3.0.4
resolution: "no-case@npm:3.0.4"
dependencies:
lower-case: "npm:^2.0.2"
tslib: "npm:^2.0.3"
checksum: 10c0/8ef545f0b3f8677c848f86ecbd42ca0ff3cd9dd71c158527b344c69ba14710d816d8489c746b6ca225e7b615108938a0bda0a54706f8c255933703ac1cf8e703
languageName: node
linkType: hard
"node-addon-api@npm:^1.6.3":
version: 1.7.2
resolution: "node-addon-api@npm:1.7.2"
@ -10091,6 +10301,18 @@ __metadata:
languageName: node
linkType: hard
"parse-json@npm:^5.2.0":
version: 5.2.0
resolution: "parse-json@npm:5.2.0"
dependencies:
"@babel/code-frame": "npm:^7.0.0"
error-ex: "npm:^1.3.1"
json-parse-even-better-errors: "npm:^2.3.0"
lines-and-columns: "npm:^1.1.6"
checksum: 10c0/77947f2253005be7a12d858aedbafa09c9ae39eb4863adf330f7b416ca4f4a08132e453e08de2db46459256fb66afaac5ee758b44fe6541b7cdaf9d252e59585
languageName: node
linkType: hard
"parse-node-version@npm:^1.0.1":
version: 1.0.1
resolution: "parse-node-version@npm:1.0.1"
@ -11437,6 +11659,16 @@ __metadata:
languageName: node
linkType: hard
"snake-case@npm:^3.0.4":
version: 3.0.4
resolution: "snake-case@npm:3.0.4"
dependencies:
dot-case: "npm:^3.0.4"
tslib: "npm:^2.0.3"
checksum: 10c0/ab19a913969f58f4474fe9f6e8a026c8a2142a01f40b52b79368068343177f818cdfef0b0c6b9558f298782441d5ca8ed5932eb57822439fad791d866e62cecd
languageName: node
linkType: hard
"socks-proxy-agent@npm:^8.0.3":
version: 8.0.3
resolution: "socks-proxy-agent@npm:8.0.3"
@ -11735,6 +11967,13 @@ __metadata:
languageName: node
linkType: hard
"svg-parser@npm:^2.0.4":
version: 2.0.4
resolution: "svg-parser@npm:2.0.4"
checksum: 10c0/02f6cb155dd7b63ebc2f44f36365bc294543bebb81b614b7628f1af3c54ab64f7e1cec20f06e252bf95bdde78441ae295a412c68ad1678f16a6907d924512b7a
languageName: node
linkType: hard
"tar@npm:^6.1.11, tar@npm:^6.1.12, tar@npm:^6.1.2, tar@npm:^6.2.0":
version: 6.2.1
resolution: "tar@npm:6.2.1"
@ -11905,6 +12144,7 @@ __metadata:
vite: "npm:^5.3.5"
vite-plugin-image-optimizer: "npm:^1.1.8"
vite-plugin-static-copy: "npm:^1.0.6"
vite-plugin-svgr: "npm:^4.2.0"
vite-tsconfig-paths: "npm:^4.3.2"
vitest: "npm:^2.0.4"
winston: "npm:^3.13.1"
@ -12136,7 +12376,7 @@ __metadata:
languageName: node
linkType: hard
"tslib@npm:^2.6.3":
"tslib@npm:^2.0.3, tslib@npm:^2.6.3":
version: 2.6.3
resolution: "tslib@npm:2.6.3"
checksum: 10c0/2598aef53d9dbe711af75522464b2104724d6467b26a60f2bdac8297d2b5f1f6b86a71f61717384aa8fd897240467aaa7bcc36a0700a0faf751293d1331db39a
@ -12656,6 +12896,19 @@ __metadata:
languageName: node
linkType: hard
"vite-plugin-svgr@npm:^4.2.0":
version: 4.2.0
resolution: "vite-plugin-svgr@npm:4.2.0"
dependencies:
"@rollup/pluginutils": "npm:^5.0.5"
"@svgr/core": "npm:^8.1.0"
"@svgr/plugin-jsx": "npm:^8.1.0"
peerDependencies:
vite: ^2.6.0 || 3 || 4 || 5
checksum: 10c0/0a6400f20905f53d08f1ce7d1f22d9a57db403e110e790f80c2e0411a0064a071a36b781f56f6823654f98052219171003f9ea023d4a31d930b4a4fc01776d1f
languageName: node
linkType: hard
"vite-tsconfig-paths@npm:^4.3.2":
version: 4.3.2
resolution: "vite-tsconfig-paths@npm:4.3.2"