diff --git a/package.json b/package.json index 4f2b30bd89..f935ef02ac 100644 --- a/package.json +++ b/package.json @@ -136,6 +136,7 @@ "react-highlight.js": "1.0.7", "react-intersection-observer": "^8.32.1", "react-window": "^1.8.6", + "textarea-caret": "^3.1.0", "tslib": "^2.3.1" }, "devDependencies": { @@ -164,7 +165,7 @@ "electron-builder": "22.14", "electron-builder-notarize": "^1.2.0", "gh-pages": "^2.2.0", - "karma": "^6.3.3", + "karma": "^6.3.16", "karma-chrome-launcher": "^3.1.0", "karma-cljs-test": "^0.1.0", "karma-junit-reporter": "^2.0.1", diff --git a/src/cljs/athens/main/core.cljs b/src/cljs/athens/main/core.cljs index e2da33f528..98c74bdff9 100644 --- a/src/cljs/athens/main/core.cljs +++ b/src/cljs/athens/main/core.cljs @@ -94,7 +94,7 @@ :autoHideMenuBar true :frame false :titleBarStyle "hidden" - :trafficLightPosition {:x 19, :y 34} + :trafficLightPosition {:x 19, :y 33} :webPreferences {:contextIsolation false :nodeIntegration true :worldSafeExecuteJavaScript true diff --git a/src/cljs/athens/util.cljs b/src/cljs/athens/util.cljs index 1d0199f1ea..df091b8196 100644 --- a/src/cljs/athens/util.cljs +++ b/src/cljs/athens/util.cljs @@ -1,8 +1,8 @@ (ns athens.util (:require - ["/textarea" :as getCaretCoordinates] ["/theme/theme" :refer [theme]] ["@chakra-ui/react" :refer [createStandaloneToast]] + ["textarea-caret" :as getCaretCoordinates] [athens.config :as config] [athens.electron.utils :as electron.utils] [clojure.string :as string] diff --git a/src/cljs/athens/views.cljs b/src/cljs/athens/views.cljs index 8048ed5488..2b90a327cc 100644 --- a/src/cljs/athens/views.cljs +++ b/src/cljs/athens/views.cljs @@ -70,6 +70,10 @@ :spacing 0 :overflowY "auto" :height "100vh" + :bg "background.floor" + :transitionDuration "fast" + :transitionProperty "background" + :transitionTimingFunction "ease-in-out" :align "stretch" :position "relative"} [app-toolbar/app-toolbar] @@ -81,7 +85,7 @@ [:> MainContent {:rightSidebarWidth @right-sidebar-width :isRightSidebarOpen @right-sidebar-open?} [pages/view]] - [:> RightSidebarResizeControl {:sidebarWidth @right-sidebar-width - :isSidebarOpen @right-sidebar-open? + [:> RightSidebarResizeControl {:rightSidebarWidth @right-sidebar-width + :isRightSidebarOpen @right-sidebar-open? :onResizeSidebar #(rf/dispatch [:right-sidebar/set-width %])}] [right-sidebar/right-sidebar]]]])]]]))) diff --git a/src/cljs/athens/views/blocks/core.cljs b/src/cljs/athens/views/blocks/core.cljs index 7f7eb55c59..293d82cb72 100644 --- a/src/cljs/athens/views/blocks/core.cljs +++ b/src/cljs/athens/views/blocks/core.cljs @@ -5,7 +5,7 @@ ["/components/Block/PropertyName" :refer [PropertyName]] ["/components/Block/Reactions" :refer [Reactions]] ["/components/Block/Toggle" :refer [Toggle]] - ["/components/Icons/Icons" :refer [BlockEmbedIcon TextIcon ChatIcon ArchiveIcon]] + ["/components/Icons/Icons" :refer [BlockEmbedIcon TextIcon ChatBubbleIcon ArchiveIcon]] ["/components/References/InlineReferences" :refer [ReferenceGroup ReferenceBlock]] ["@chakra-ui/react" :refer [Box Breadcrumb BreadcrumbItem BreadcrumbLink Button Divider HStack MenuDivider MenuItem MenuList VStack]] [athens.common-db :as common-db] @@ -375,7 +375,7 @@ (when comments-enabled? [:> MenuItem {:children "Add comment" :onClick #(ctx-menu/handle-click-comment % uid) - :icon (r/as-element [:> ChatIcon])}]) + :icon (r/as-element [:> ChatBubbleIcon])}]) (when reactions-enabled? [:<> [:> MenuDivider] diff --git a/src/cljs/athens/views/blocks/reactions.cljs b/src/cljs/athens/views/blocks/reactions.cljs index 8f83fca60e..69ae5bab9a 100644 --- a/src/cljs/athens/views/blocks/reactions.cljs +++ b/src/cljs/athens/views/blocks/reactions.cljs @@ -14,7 +14,7 @@ [reagent.core :as r])) -(def common-reactions ["❤️" "💔" "😐" "😕" "😡"]) +(def common-reactions ["👍" "👎" "❤️" "🔥" "😂" "😲" "😢" "😡"]) (defn toggle-reaction diff --git a/src/cljs/athens/views/left_sidebar.cljs b/src/cljs/athens/views/left_sidebar.cljs index 8d3847a3f4..cf9d370e7e 100644 --- a/src/cljs/athens/views/left_sidebar.cljs +++ b/src/cljs/athens/views/left_sidebar.cljs @@ -1,6 +1,6 @@ (ns athens.views.left-sidebar (:require - ["/components/Icons/Icons" :refer [DailyNotesIcon AllPagesIcon SearchIcon GraphIcon]] + ["/components/Icons/Icons" :refer [CalendarEditFillIcon AllPagesIcon SearchIcon GraphIcon]] ["/components/Layout/MainSidebar" :refer [MainSidebar]] ["/components/SidebarShortcuts/List" :refer [List]] ["@chakra-ui/react" :refer [Button VStack Flex Heading ButtonGroup Link Flex]] @@ -45,11 +45,11 @@ :justifyContent "start" :leftIcon (r/as-element [:> SearchIcon])} "Find or Create a Page"] - [route-button (= route-name :home) "Daily Notes" (r/as-element [:> DailyNotesIcon]) (fn [_] - (rf/dispatch [:reporting/navigation {:source :main-sidebar - :target :home - :pane :main-pane}]) - (router/nav-daily-notes))] + [route-button (= route-name :home) "Daily Notes" (r/as-element [:> CalendarEditFillIcon]) (fn [_] + (rf/dispatch [:reporting/navigation {:source :main-sidebar + :target :home + :pane :main-pane}]) + (router/nav-daily-notes))] [route-button (= route-name :pages) "All Pages" (r/as-element [:> AllPagesIcon]) (fn [_] (rf/dispatch [:reporting/navigation {:source :main-sidebar :target :all-pages diff --git a/src/cljs/athens/views/notifications/popover.cljs b/src/cljs/athens/views/notifications/popover.cljs index 4fd3261069..ea023c057a 100644 --- a/src/cljs/athens/views/notifications/popover.cljs +++ b/src/cljs/athens/views/notifications/popover.cljs @@ -120,13 +120,11 @@ notification-list (get-inbox-items-for-popover @db/dsdb user-page-title) navigate-user-page #(router/navigate-page user-page-title) num-notifications (count notification-list)] - [:> Popover {:closeOnBlur true} + [:> Popover {:closeOnBlur true :size "md"} [:> PopoverTrigger [:> Box {:position "relative"} [:> IconButton {"aria-label" "Notifications" - :variant "ghost" - :fontSize "1.3em" :onDoubleClick navigate-user-page :onClick (fn [e] (when (.. e -shiftKey) @@ -139,8 +137,7 @@ :right "-3px" :bottom "-1px" :variant "ghost" :zIndex 1} num-notifications])]] - [:> PopoverContent {:maxWidth "max-content" - :maxHeight "calc(100vh - 4rem)"} + [:> PopoverContent {:maxHeight "calc(100vh - 4rem)"} [:> PopoverCloseButton] [:> PopoverHeader [:> Button {:onClick navigate-user-page :rightIcon (r/as-element [:> ArrowRightIcon])} "Notifications"]] [:> Flex {:p 0 diff --git a/src/cljs/athens/views/pages/daily_notes.cljs b/src/cljs/athens/views/pages/daily_notes.cljs index c81c2f0510..31f8317da5 100644 --- a/src/cljs/athens/views/pages/daily_notes.cljs +++ b/src/cljs/athens/views/pages/daily_notes.cljs @@ -1,8 +1,6 @@ (ns athens.views.pages.daily-notes (:require - ["/components/Page/Page" :refer [DailyNotesPage]] - ["@chakra-ui/react" :refer [VStack]] - ["framer-motion" :refer [AnimatePresence]] + ["/components/Page/Page" :refer [DailyNotesPage DailyNotesList]] [athens.dates :as dates] [athens.reactive :as reactive] [athens.views.pages.node-page :as node-page] @@ -26,15 +24,24 @@ (defn page [] (let [note-refs (subscribe [:daily-notes/items]) - get-next-note #(dispatch [:daily-note/next (dates/get-day (dates/uid-to-date (last @note-refs)) 1)])] + get-another-note #(dispatch [:daily-note/next (dates/get-day (dates/uid-to-date (last @note-refs)) 1)])] (fn [] (if (empty? @note-refs) (dispatch [:daily-note/next (dates/get-day)]) (let [notes (reactive-pull-many @note-refs)] - [:> VStack {:alignSelf "stretch" :align "stretch" :py 16 :px [2 4 8] :spacing 8} - [:> AnimatePresence {:initial false} - (doall - (for [{:keys [block/uid]} notes] - [:> DailyNotesPage {:key uid - :onFirstAppear get-next-note} - [node-page/page [:block/uid uid]]]))]]))))) + [:> DailyNotesList {:id "daily-notes" + :onGetAnotherNote get-another-note + :minHeight "calc(100vh + 1px)" + :height "calc(100vh + 1px)" + :display "flex" + :overflowY "auto" + :gap "1.5rem" + :px "2rem" + :alignItems "center" + :flex "1 1 100%" + :flexDirection "column"} + (doall + (for [{:keys [block/uid]} notes] + [:> DailyNotesPage {:key uid + :isReal true} + [node-page/page [:block/uid uid]]]))]))))) diff --git a/src/cljs/athens/views/right_sidebar/core.cljs b/src/cljs/athens/views/right_sidebar/core.cljs index 9db5cc1a4a..d78f0376ad 100644 --- a/src/cljs/athens/views/right_sidebar/core.cljs +++ b/src/cljs/athens/views/right_sidebar/core.cljs @@ -7,7 +7,7 @@ [athens.views.right-sidebar.events] [athens.views.right-sidebar.shared :as shared] [athens.views.right-sidebar.subs] - [re-frame.core :as rf :refer [dispatch]])) + [re-frame.core :as rf])) ;; Components @@ -37,8 +37,7 @@ [open? items rf-width] [:> RightSidebar {:isOpen open? - :rightSidebarWidth rf-width - :onResizeSidebar #(dispatch [:right-sidebar/set-width %])} + :rightSidebarWidth rf-width} (if (empty? items) [empty-message] [:> List {:items (shared/create-sidebar-list items) diff --git a/src/js/components/AllPagesTable/AllPagesTable.tsx b/src/js/components/AllPagesTable/AllPagesTable.tsx index 93244b79b8..1f4ef0d195 100644 --- a/src/js/components/AllPagesTable/AllPagesTable.tsx +++ b/src/js/components/AllPagesTable/AllPagesTable.tsx @@ -31,6 +31,17 @@ const renderDate = (date) => { } } +const RowTd = ({ children, ...props }) => { + return ( + + {children} + + ) +} const Row = ({ index, data, style }) => { @@ -46,7 +57,7 @@ const Row = ({ index, data, style }) => { display="flex" className={index % 2 ? 'index-even' : 'index-odd'} > - - - {item[":block/_refs"]?.length || 0} - {renderDate(item[":time/modified"])} - {renderDate(item[":time/created"])} + + {item[":block/_refs"]?.length || 0} + {renderDate(item[":time/modified"])} + {renderDate(item[":time/created"])} ) }; diff --git a/src/js/components/AppToolbar/AppToolbar.tsx b/src/js/components/AppToolbar/AppToolbar.tsx index a518122d90..852630768b 100644 --- a/src/js/components/AppToolbar/AppToolbar.tsx +++ b/src/js/components/AppToolbar/AppToolbar.tsx @@ -4,19 +4,17 @@ import { RightSidebarIcon, MenuIcon, HelpIcon, - ChatFilledIcon, + ChatBubbleFillIcon, ChevronLeftIcon, ChevronRightIcon, SettingsIcon, ContrastIcon, EllipsisHorizontalCircleIcon, - ChatIcon, + ChatBubbleIcon, } from '@/Icons/Icons'; import { - HTMLChakraProps, Portal, - ThemingProps, Menu, MenuButton, MenuItem, @@ -25,11 +23,12 @@ import { Box, Flex, Spacer, - ButtonOptions, IconButton, ButtonGroup, useColorMode, - useMediaQuery + useMediaQuery, + ButtonGroupProps, + useToast } from '@chakra-ui/react'; import { AnimatePresence, motion } from 'framer-motion'; @@ -37,28 +36,15 @@ import { LayoutContext, layoutAnimationProps, layoutAnimationTransition } from " import { FakeTrafficLights } from './components/FakeTrafficLights'; import { WindowButtons } from './components/WindowButtons'; import { LocationIndicator } from './components/LocationIndicator'; -interface ToolbarIconButtonProps extends ButtonOptions, HTMLChakraProps<'button'>, ThemingProps<"Button"> { - children: React.ReactChild; -} const PAGE_TITLE_SHOW_HEIGHT = 24; -const toolbarIconButtonStyle = { - variant: "ghost", - colorScheme: "subtle", - sx: { - "svg": { - fontSize: "1.5em" - } - } +interface ToolbarButtonGroupProps extends ButtonGroupProps { + key: string } -const ToolbarIconButton = React.forwardRef((props: ToolbarIconButtonProps, ref) => { - const { children } = props; - return {children} -}); - +const ToolbarButtonGroup = (props: ToolbarButtonGroupProps) => export interface AppToolbarProps extends React.HTMLAttributes { /** @@ -141,19 +127,17 @@ export interface AppToolbarProps extends React.HTMLAttributes { } const secondaryToolbarItems = (items) => { - return + return {items.filter(x => !!x).map((item) => - - {item.icon} - + )} - + } const secondaryToolbarOverflowMenu = (items) => { return {({ isOpen }) => <> - + } /> {items.filter(x => !!x).map((item) => ( { isWinFullscreen, isWinFocused, isWinMaximized, - isHelpOpen, isThemeDark, isLeftSidebarOpen, isShowComments, @@ -203,6 +186,8 @@ export const AppToolbar = (props: AppToolbarProps): React.ReactElement => { const [canShowFullSecondaryMenu] = useMediaQuery('(min-width: 900px)'); const [isScrolledPastTitle, setIsScrolledPastTitle] = React.useState(null); + const toast = useToast(); + // add event listener to detect when the user scrolls past the title React.useLayoutEffect(() => { const scrollContainer = document.getElementById("main-layout") as HTMLElement; @@ -238,8 +223,28 @@ export const AppToolbar = (props: AppToolbarProps): React.ReactElement => { const secondaryTools = [ handleClickComments && { label: isShowComments ? "Hide comments" : "Show comments", - onClick: handleClickComments, - icon: isShowComments ? : + onClick: () => { + if (isShowComments) { + handleClickComments(); + toast({ + title: "Comments hidden", + status: "info", + duration: 5000, + position: "top-right" + }); + + } else { + handleClickComments(); + toast({ + title: "Comments shown", + status: "info", + duration: 5000, + position: "top-right" + }); + + } + }, + icon: isShowComments ? : }, { label: "Help", @@ -276,8 +281,8 @@ export const AppToolbar = (props: AppToolbarProps): React.ReactElement => { > {/* Left side */} - { )} - - + {databaseMenu} - + {/* Right side */} {isElectron && ( - - - + + - - + icon={} + /> - - - + onClick={handlePressHistoryForward} + icon={} + /> - ) + ) } ); const rightToolbarControls = ( - @@ -342,16 +344,14 @@ export const AppToolbar = (props: AppToolbarProps): React.ReactElement => { {canShowFullSecondaryMenu ? secondaryToolbarItems(secondaryTools) : secondaryToolbarOverflowMenu(secondaryTools)} - + ); const currentLocationTools = ( - { title={currentPageTitle} /> )} - + ) const contentControls = ( - @@ -427,6 +425,9 @@ export const AppToolbar = (props: AppToolbarProps): React.ReactElement => { position: "absolute", inset: 0, bg: "background.floor", + transitionProperty: "background", + transitionTimingFunction: "ease-in-out", + transitionDuration: "fast", opacity: 0.7, }} position="absolute" diff --git a/src/js/components/Block/Autocomplete.tsx b/src/js/components/Block/Autocomplete.tsx index 0f1a18deb8..8d70470a03 100644 --- a/src/js/components/Block/Autocomplete.tsx +++ b/src/js/components/Block/Autocomplete.tsx @@ -7,18 +7,21 @@ import { PopoverTrigger, useOutsideClick, PopoverContent, - Portal + Portal, + ButtonProps } from '@chakra-ui/react'; -import getCaretCoordinates from '@/../textarea' +import getCaretCoordinates from 'textarea-caret'; const getCaretPositionFromKeyDownEvent = (event) => { if (event?.target) { - const localCaretCoordinates = getCaretCoordinates(event?.target); + const target = event.target; + const selectionStart = target.selectionStart; + const { left: caretLeft, top: caretTop, height: caretHeight } = getCaretCoordinates(event.target, selectionStart); const { x: targetLeft, y: targetTop } = event?.target.getBoundingClientRect(); const position = { - left: localCaretCoordinates.left + targetLeft, - top: localCaretCoordinates.top + targetTop, - height: localCaretCoordinates.height + targetLeft, + left: caretLeft + targetLeft, + top: caretTop + targetTop, + height: caretHeight + targetTop, } return position; } @@ -34,11 +37,12 @@ const scrollToEl = (el) => { } } -export const AutocompleteButton = ({ children, onClick, isActive, ...props }) => { +export const AutocompleteButton = (props: ButtonProps) => { + const { children, isActive, ...buttonProps } = props; const buttonRef = React.useRef(null); React.useEffect(() => { - if (isActive) { + if (isActive && buttonRef.current) { scrollToEl(buttonRef.current); } }, [isActive]); @@ -47,6 +51,7 @@ export const AutocompleteButton = ({ children, onClick, isActive, ...props }) => @@ -74,56 +78,56 @@ export const AutocompleteButton = ({ children, onClick, isActive, ...props }) => } export const Autocomplete = ({ isOpen, onClose, event, children }) => { - // Early return with nothing, to avoid rendering all - // the juicy portaling goodness. - if (!isOpen) { - return null; - } - const menuRef = React.useRef(null); - const lastEventTargetValue = React.useRef(null); - const currentEventPosition = React.useRef({ left: null, top: null }); - const newEventTargetValue = event?.target?.value; - const isNewEvent = newEventTargetValue !== lastEventTargetValue.current; + const [menuPosition, setMenuPosition] = React.useState({ left: null, top: null }); useOutsideClick({ ref: menuRef, handler: () => onClose(), }) - if (isOpen && isNewEvent) { - currentEventPosition.current = getCaretPositionFromKeyDownEvent(event) - }; + React.useEffect(() => { + const target = event?.target; + if (isOpen && target) { + const position = getCaretPositionFromKeyDownEvent(event); + if (position.left && position.top) { + setMenuPosition(position); + } else { + onClose(); + } + } + }, [isOpen]); - lastEventTargetValue.current = newEventTargetValue; + if (!isOpen) { + return false; + } return ( { ref={menuRef} p={0} overflow="auto" - maxHeight={`calc(100vh - 2rem - 2rem - ${currentEventPosition.current.top}px)`} + maxHeight={`calc(100vh - 2rem - 2rem - ${menuPosition?.top || 0}px)`} > {children} diff --git a/src/js/components/Comments/Comments.tsx b/src/js/components/Comments/Comments.tsx index b5af2c0b39..9a3a8ed1ae 100644 --- a/src/js/components/Comments/Comments.tsx +++ b/src/js/components/Comments/Comments.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Box, Text, HStack, Textarea, Button, MenuList, MenuItem } from '@chakra-ui/react' -import { ChatFilledIcon } from '@/Icons/Icons' +import { ChatBubbleFillIcon } from '@/Icons/Icons' import { useContextMenu } from '@/utils/useContextMenu'; import { withErrorBoundary } from "react-error-boundary"; @@ -53,8 +53,8 @@ const formatCount = (count: number): string => { export const CommentCounter = ({ count }) => { return - - {formatCount(count)} + + {formatCount(count)} } diff --git a/src/js/components/Icons/Icons.tsx b/src/js/components/Icons/Icons.tsx index 87437b58fd..340cd13ab8 100644 --- a/src/js/components/Icons/Icons.tsx +++ b/src/js/components/Icons/Icons.tsx @@ -1,592 +1,675 @@ +import React from 'react'; + import { createIcon } from '@chakra-ui/react' export const AllPagesIcon = createIcon({ displayName: 'AllPagesIcon', - viewBox: '0 0 24 24', - path: ( - <> - ), -}); - -export const ArrowAngleUpLeftIcon = createIcon({ - displayName: 'ArrowAngleUpLeftIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ArchiveIcon = createIcon({ displayName: 'ArchiveIcon', - viewBox: '0 0 24 24', - path: ( - - ), -}); -export const BulletIcon = createIcon({ - displayName: 'BulletIcon', - viewBox: '0 0 24 24', - path: ( - - ), -}); - -export const ColonIcon = createIcon({ - displayName: 'ColonIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - + <> ), }); - -export const BellFillIcon = createIcon({ - displayName: 'BellFillIcon', - viewBox: '0 0 24 24', - path: ( - <> - - - - ), -}); - - -export const BellIcon = createIcon({ - displayName: 'BellIcon', - viewBox: '0 0 24 24', +export const ArrowAngleUpLeftIcon = createIcon({ + displayName: 'ArrowAngleUpLeftIcon', + viewBox: '0 0 14 14', path: ( - + <> ), }); - - export const ArrowLeftIcon = createIcon({ displayName: 'ArrowLeftIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ArrowLeftOnBoxIcon = createIcon({ displayName: 'ArrowLeftOnBoxIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ArrowLeftOnBoxFillIcon = createIcon({ displayName: 'ArrowLeftOnBoxFillIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ArrowRightIcon = createIcon({ displayName: 'ArrowRightIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ArrowRightOnBoxIcon = createIcon({ displayName: 'ArrowRightOnBoxIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ArrowRightOnBoxFillIcon = createIcon({ displayName: 'ArrowRightOnBoxFillIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); -export const BlockIcon = createIcon({ - displayName: 'BlockIcon', - viewBox: '0 0 24 24', +export const BellIcon = createIcon({ + displayName: 'BellIcon', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); -export const TextIcon = createIcon({ - displayName: 'TextIcon', - viewBox: '0 0 24 24', +export const BellFillIcon = createIcon({ + displayName: 'BellFillIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const BlockIcon = createIcon({ + displayName: 'BlockIcon', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const BlockAddIcon = createIcon({ displayName: 'BlockAddIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const BlockEmbedIcon = createIcon({ displayName: 'BlockEmbedIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const BlockFillIcon = createIcon({ displayName: 'BlockFillIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const BlockFillAddIcon = createIcon({ displayName: 'BlockFillAddIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const BookmarkIcon = createIcon({ displayName: 'BookmarkIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const BookmarkFillIcon = createIcon({ displayName: 'BookmarkFillIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const BulletIcon = createIcon({ + displayName: 'BulletIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const CalendarIcon = createIcon({ + displayName: 'CalendarIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const CalendarBookmarkIcon = createIcon({ + displayName: 'CalendarBookmarkIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const CalendarBookmarkFillIcon = createIcon({ + displayName: 'CalendarBookmarkFillIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const CalendarCheckmarkIcon = createIcon({ + displayName: 'CalendarCheckmarkIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const CalendarCheckmarkFillIcon = createIcon({ + displayName: 'CalendarCheckmarkFillIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const CalendarCircleFillIcon = createIcon({ + displayName: 'CalendarCircleFillIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const CalendarEditIcon = createIcon({ + displayName: 'CalendarEditIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const CalendarEditFillIcon = createIcon({ + displayName: 'CalendarEditFillIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const CalendarFillIcon = createIcon({ + displayName: 'CalendarFillIcon', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const CalendarNowIcon = createIcon({ displayName: 'CalendarNowIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> + ), +}); + +export const CalendarTimeNowIcon = createIcon({ + displayName: 'CalendarTimeNowIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); + +export const CalendarTimeNowFillIcon = createIcon({ + displayName: 'CalendarTimeNowFillIcon', + viewBox: '0 0 14 14', + path: ( + <> ), }); export const CalendarTomorrowIcon = createIcon({ displayName: 'CalendarTomorrowIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> + ), +}); + +export const CalendarTomorrowFillIcon = createIcon({ + displayName: 'CalendarTomorrowFillIcon', + viewBox: '0 0 14 14', + path: ( + <> ), }); export const CalendarYesterdayIcon = createIcon({ displayName: 'CalendarYesterdayIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); -export const ChatIcon = createIcon({ - d: "M7.85355 20.1464L8.38388 20.6768H8.38388L7.85355 20.1464ZM11.7071 16.2929L11.1768 15.7626L11.7071 16.2929ZM2.75 6C2.75 4.75736 3.75736 3.75 5 3.75V2.25C2.92893 2.25 1.25 3.92893 1.25 6H2.75ZM2.75 13V6H1.25V13H2.75ZM5 15.25C3.75736 15.25 2.75 14.2426 2.75 13H1.25C1.25 15.0711 2.92893 16.75 5 16.75V15.25ZM6 15.25H5V16.75H6V15.25ZM7.75 19.7929V17H6.25V19.7929H7.75ZM7.32322 19.6161C7.48072 19.4586 7.75 19.5702 7.75 19.7929H6.25C6.25 20.9065 7.59642 21.4642 8.38388 20.6768L7.32322 19.6161ZM11.1768 15.7626L7.32322 19.6161L8.38388 20.6768L12.2374 16.8232L11.1768 15.7626ZM19 15.25H12.4142V16.75H19V15.25ZM21.25 13C21.25 14.2426 20.2426 15.25 19 15.25V16.75C21.0711 16.75 22.75 15.0711 22.75 13H21.25ZM21.25 6V13H22.75V6H21.25ZM19 3.75C20.2426 3.75 21.25 4.75736 21.25 6H22.75C22.75 3.92893 21.0711 2.25 19 2.25V3.75ZM5 3.75H19V2.25H5V3.75ZM12.2374 16.8232C12.2843 16.7763 12.3479 16.75 12.4142 16.75V15.25C11.9501 15.25 11.505 15.4344 11.1768 15.7626L12.2374 16.8232ZM6 16.75C6.13807 16.75 6.25 16.8619 6.25 17H7.75C7.75 16.0335 6.9665 15.25 6 15.25V16.75Z", - displayName: "ChatIcon", - viewBox: "0 0 24 24", -}) +export const CalendarYesterdayFillIcon = createIcon({ + displayName: 'CalendarYesterdayFillIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); -export const ChatFilledIcon = createIcon({ - d: "M5 3.25C2.92893 3.25 1.25 4.92893 1.25 7V14C1.25 16.0711 2.92893 17.75 5 17.75H5.25V20.5858C5.25 22.1449 7.135 22.9257 8.23744 21.8232L12.3107 17.75H19C21.0711 17.75 22.75 16.0711 22.75 14V7C22.75 4.92893 21.0711 3.25 19 3.25H5Z", - displayName: "ChatFilledIcon", - viewBox: "0 0 24 24", -}) +export const ChatBubbleIcon = createIcon({ + displayName: 'ChatBubbleIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); +export const ChatBubbleFillIcon = createIcon({ + displayName: 'ChatBubbleFillIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); export const CheckboxIcon = createIcon({ displayName: 'CheckboxIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const CheckmarkIcon = createIcon({ displayName: 'CheckmarkIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const CheckmarkCircleFillIcon = createIcon({ displayName: 'CheckmarkCircleFillIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ChevronDownIcon = createIcon({ displayName: 'ChevronDownIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ChevronLeftIcon = createIcon({ displayName: 'ChevronLeftIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ChevronRightIcon = createIcon({ displayName: 'ChevronRightIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ChevronUpIcon = createIcon({ displayName: 'ChevronUpIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); -export const ContrastFillIcon = createIcon({ - displayName: 'ContrastFillIcon', - viewBox: '0 0 24 24', +export const ColonIcon = createIcon({ + displayName: 'ColonIcon', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ContrastIcon = createIcon({ displayName: 'ContrastIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - + <> + ), +}); +export const ContrastFillIcon = createIcon({ + displayName: 'ContrastFillIcon', + viewBox: '0 0 14 14', + path: ( + <> + ), +}); +export const ControlsIcon = createIcon({ + displayName: 'ControlsIcon', + viewBox: '0 0 14 14', + path: ( + <> ), }); export const DailyNotesIcon = createIcon({ displayName: 'DailyNotesIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const DashIcon = createIcon({ displayName: 'DashIcon', - viewBox: '0 0 24 24', - path: ( - - ), -}); - -export const DragIcon = createIcon({ - displayName: 'DragIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - + <> ), }); - export const EllipsisHorizontalIcon = createIcon({ displayName: 'EllipsisHorizontalIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const EllipsisHorizontalCircleIcon = createIcon({ displayName: 'EllipsisHorizontalCircleIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - + <> ), }); export const ExclamationCircleFillIcon = createIcon({ displayName: 'ExclamationCircleFillIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const GraphIcon = createIcon({ displayName: 'GraphIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const HTMLEmbedIcon = createIcon({ displayName: 'HTMLEmbedIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); -export const HelpFillIcon = createIcon({ - displayName: 'HelpFillIcon', - viewBox: '0 0 24 24', +export const HelpIcon = createIcon({ + displayName: 'HelpIcon', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); -export const HelpIcon = createIcon({ - displayName: 'HelpIcon', - viewBox: '0 0 24 24', +export const HelpFillIcon = createIcon({ + displayName: 'HelpFillIcon', + viewBox: '0 0 14 14', path: ( - - + <> ), }); export const LinkedIcon = createIcon({ displayName: 'LinkedIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const MenuIcon = createIcon({ displayName: 'MenuIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const PageIcon = createIcon({ displayName: 'PageIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const PageAddIcon = createIcon({ displayName: 'PageAddIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const PageFillIcon = createIcon({ displayName: 'PageFillIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ParenLeftIcon = createIcon({ displayName: 'ParenLeftIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const ParenRightIcon = createIcon({ displayName: 'ParenRightIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const PencilIcon = createIcon({ displayName: 'PencilIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const PencilLineIcon = createIcon({ displayName: 'PencilLineIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const PersonIcon = createIcon({ displayName: 'PersonIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - + <> ), }); export const PersonCircleIcon = createIcon({ displayName: 'PersonCircleIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - + <> ), }); export const PlusIcon = createIcon({ displayName: 'PlusIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> + ), +}); + +export const PlusSquareIcon = createIcon({ + displayName: 'PlusSquareIcon', + viewBox: '0 0 14 14', + path: ( + <> ), }); export const RightSidebarIcon = createIcon({ displayName: 'RightSidebarIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - < > - - - + <> ), }); export const RightSidebarAddIcon = createIcon({ displayName: 'RightSidebarAddIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - + <> ), }); export const SearchIcon = createIcon({ displayName: 'SearchIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const SettingsIcon = createIcon({ displayName: 'SettingsIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - + <> ), }); export const SettingsFillIcon = createIcon({ displayName: 'SettingsFillIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const TemplateIcon = createIcon({ displayName: 'TemplateIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); -export const TimeNowIcon = createIcon({ - displayName: 'TimeNowIcon', - viewBox: '0 0 24 24', +export const TextIcon = createIcon({ + displayName: 'TextIcon', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); -export const ThumbUpIcon = createIcon({ - displayName: 'ThumbUpIcon', - viewBox: '0 0 24 24', +export const ThumbsUpIcon = createIcon({ + displayName: 'ThumbsUpIcon', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); -export const ThumbUpFillIcon = createIcon({ - displayName: 'ThumbUpFillIcon', - viewBox: '0 0 24 24', +export const ThumbsUpFillIcon = createIcon({ + displayName: 'ThumbsUpFillIcon', + viewBox: '0 0 14 14', path: ( - <> + <> + ), +}); + +export const TimeNowIcon = createIcon({ + displayName: 'TimeNowIcon', + viewBox: '0 0 14 14', + path: ( + <> ), }); export const TrashIcon = createIcon({ displayName: 'TrashIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const UnlinkedIcon = createIcon({ displayName: 'UnlinkedIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); -export const ViewIcon = createIcon({ - displayName: "ViewIcon", - path: ( - - - - - ), -}) - -export const ViewOffIcon = createIcon({ - displayName: "ViewOffIcon", - path: ( - - - - - ), -}) - export const XmarkIcon = createIcon({ displayName: 'XmarkIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); export const YoutubeIcon = createIcon({ displayName: 'YoutubeIcon', - viewBox: '0 0 24 24', + viewBox: '0 0 14 14', path: ( - <> + <> ), }); - diff --git a/src/js/components/Layout/MainSidebar.tsx b/src/js/components/Layout/MainSidebar.tsx index 30a75c0735..1cad731b9c 100644 --- a/src/js/components/Layout/MainSidebar.tsx +++ b/src/js/components/Layout/MainSidebar.tsx @@ -23,6 +23,9 @@ export const MainSidebar = (props) => { userSelect="none" key="main sidebar" bg="background.upper" + transitionProperty="background" + transitionTimingFunction="ease-in-out" + transitionDuration="fast" pt={toolbarHeight} height="100vh" position="sticky" diff --git a/src/js/components/Layout/RightSidebar.tsx b/src/js/components/Layout/RightSidebar.tsx index 386ac045e0..80ec93eaed 100644 --- a/src/js/components/Layout/RightSidebar.tsx +++ b/src/js/components/Layout/RightSidebar.tsx @@ -1,12 +1,20 @@ import * as React from "react"; import { LayoutContext, layoutAnimationProps } from "./useLayoutState"; import { AnimatePresence, motion } from 'framer-motion'; -import { DragIcon, XmarkIcon, ChevronRightIcon, PageIcon, PageFillIcon, BlockIcon, BlockFillIcon, GraphIcon, ArrowLeftOnBoxIcon } from '@/Icons/Icons'; -import { Button, IconButton, Box, Collapse, VStack } from '@chakra-ui/react'; +import { XmarkIcon, ChevronRightIcon, PageIcon, PageFillIcon, BlockIcon, BlockFillIcon, GraphIcon, ArrowLeftOnBoxIcon } from '@/Icons/Icons'; +import { Button, IconButton, Box, Collapse, VStack, BoxProps } from '@chakra-ui/react'; /** Right Sidebar */ -export const RightSidebar = (props) => { + + +interface RightSidebarProps extends BoxProps { + isOpen: boolean; + rightSidebarWidth: number; +} + +export const RightSidebar = (props: RightSidebarProps) => { const { children, rightSidebarWidth, isOpen } = props; + const { toolbarHeight } = React.useContext(LayoutContext); @@ -19,6 +27,9 @@ export const RightSidebar = (props) => { {...layoutAnimationProps(rightSidebarWidth + "vw")} zIndex={1} bg="background.floor" + transitionProperty="background" + transitionTimingFunction="ease-in-out" + transitionDuration="fast" overflowY="auto" borderLeft="1px solid" borderColor="separator.divider" @@ -149,5 +160,5 @@ export const SidebarItem = ({ title, type, isOpen, onToggle, onRemove, onNavigat {children} - ); + ); }; diff --git a/src/js/components/Layout/RightSidebarResizeControl.tsx b/src/js/components/Layout/RightSidebarResizeControl.tsx index dabb1dea15..cce4e4ecd4 100644 --- a/src/js/components/Layout/RightSidebarResizeControl.tsx +++ b/src/js/components/Layout/RightSidebarResizeControl.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Box } from '@chakra-ui/react'; +import { Box, BoxProps } from '@chakra-ui/react'; const MIN_VW = 20; const MAX_VW = 80; @@ -13,8 +13,14 @@ const getVW = (e, window) => { return calcVW; } -export const RightSidebarResizeControl = (props) => { - const { onResizeSidebar, isSidebarOpen, sidebarWidth, ...rest } = props; +interface RightSidebarResizeControlProps extends BoxProps { + onResizeSidebar: (size: number) => void; + isRightSidebarOpen: boolean; + rightSidebarWidth: number; +} + +export const RightSidebarResizeControl = (props: RightSidebarResizeControlProps) => { + const { onResizeSidebar, isRightSidebarOpen, rightSidebarWidth, ...rest } = props; const [isDragging, setIsDragging] = React.useState(false); const moveHandler = (e) => { @@ -26,8 +32,7 @@ export const RightSidebarResizeControl = (props) => { } } - // todo: set graph property block to this value on mouse up - const mouseUpHandler = (e) => { + const mouseUpHandler = () => { setIsDragging(false); } @@ -40,7 +45,7 @@ export const RightSidebarResizeControl = (props) => { } }); - if (!isSidebarOpen) { + if (!isRightSidebarOpen) { return null; } @@ -52,7 +57,7 @@ export const RightSidebarResizeControl = (props) => { position="fixed" zIndex={100} opacity={0} - right={sidebarWidth + "vw"} + right={rightSidebarWidth + "vw"} height="100%" cursor="col-resize" onMouseDown={() => setIsDragging(true)} @@ -62,6 +67,7 @@ export const RightSidebarResizeControl = (props) => { transition="opacity 0.2s ease-in-out" _hover={{ opacity: 1 }} {...isDragging && { opacity: 1 }} + {...rest} > ); diff --git a/src/js/components/Page/Page.tsx b/src/js/components/Page/Page.tsx index 64b03b0cdd..686386707d 100644 --- a/src/js/components/Page/Page.tsx +++ b/src/js/components/Page/Page.tsx @@ -1,21 +1,22 @@ import React from 'react'; import { - Button, Divider, Center, Box, Heading, Image, IconButton, ButtonGroup, FormControl, Input, - Tooltip, FormLabel + Button, VStack, Divider, Center, Box, Heading, Image, IconButton, ButtonGroup, FormControl, Input, + Tooltip, FormLabel, BoxProps } from '@chakra-ui/react'; -import { useInView } from "react-intersection-observer"; import { ArrowRightOnBoxIcon, ArrowLeftOnBoxIcon } from '@/Icons/Icons'; +import { useInView } from 'react-intersection-observer'; import { withErrorBoundary } from "react-error-boundary"; -import { motion } from 'framer-motion'; const PAGE_PROPS = { as: "article", display: "grid", - flexBasis: "100%", alignSelf: "stretch", gridTemplateAreas: "'header' 'content' 'footer'", gridTemplateRows: "auto 1fr auto", + transitionProperty: "background", + transitionTimingFunction: "ease-in-out", + transitionDuration: "fast", sx: { "--page-padding": "3rem", } @@ -91,28 +92,25 @@ export const PageHeader = ({ > {children} - + {headerImageEnabled && } {onClickOpenInMainView && - - } + icon={} + />} {onClickOpenInSidebar && - - } + icon={} + />} {isPropertiesOpen && @@ -123,7 +121,6 @@ export const PageHeader = ({ } - {headerImageUrl && } ) } @@ -154,14 +151,11 @@ const DailyNotePageError = () => { className="node-page daily-notes" boxShadow="page" bg="background.floor" - alignSelf="stretch" display="flex" borderWidth="1px" borderStyle="solid" borderColor="separator.divider" - transitionDuration="0s" borderRadius="0.5rem" - minHeight="calc(100vh - 10rem)" textAlign="center" p={12} color="foreground.secondary" @@ -172,48 +166,54 @@ const DailyNotePageError = () => { ) } +interface DailyNotesListProps extends BoxProps { + onGetAnotherNote: () => void; +} -export const DailyNotesPage = withErrorBoundary(({ children, onFirstAppear, ...rest }) => { - const hasAppeared = React.useRef(false); - const { ref, inView } = useInView({ threshold: 1, triggerOnce: true, delay: 50 }); +export const DailyNotesList = (props: DailyNotesListProps) => { + const { onGetAnotherNote, ...boxProps } = props; + const listRef = React.useRef(null) + const { ref, inView } = useInView({ threshold: 0 }); - if (!hasAppeared.current) { + React.useLayoutEffect(() => { if (inView) { - onFirstAppear(); - hasAppeared.current = true; + onGetAnotherNote(); } - } + }); + + return + {boxProps.children} + + + + Earlier + + + + +} + + +interface DailyNotesPageProps extends BoxProps { + isReal: boolean; +} + +export const DailyNotesPage = withErrorBoundary((props: DailyNotesPageProps) => { + const { isReal, ...boxProps } = props return ( - {children} - ) + />) }, { fallback: }); diff --git a/src/js/textarea.js b/src/js/textarea.js deleted file mode 100644 index 7ce576864b..0000000000 --- a/src/js/textarea.js +++ /dev/null @@ -1,155 +0,0 @@ -/* jshint browser: true */ - -(function () { - -// We'll copy the properties below into the mirror div. -// Note that some browsers, such as Firefox, do not concatenate properties -// into their shorthand (e.g. padding-top, padding-bottom etc. -> padding), -// so we have to list every single property explicitly. -var properties = [ - 'direction', // RTL support - 'boxSizing', - 'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does - 'height', - 'overflowX', - 'overflowY', // copy the scrollbar for IE - - 'borderTopWidth', - 'borderRightWidth', - 'borderBottomWidth', - 'borderLeftWidth', - 'borderStyle', - - 'paddingTop', - 'paddingRight', - 'paddingBottom', - 'paddingLeft', - - // https://developer.mozilla.org/en-US/docs/Web/CSS/font - 'fontStyle', - 'fontVariant', - 'fontWeight', - 'fontStretch', - 'fontSize', - 'fontSizeAdjust', - 'lineHeight', - 'fontFamily', - - 'textAlign', - 'textTransform', - 'textIndent', - 'textDecoration', // might not make a difference, but better be safe - - 'letterSpacing', - 'wordSpacing', - - 'tabSize', - 'MozTabSize' - -]; - -var isBrowser = (typeof window !== 'undefined'); -var isFirefox = (isBrowser && window.mozInnerScreenX != null); - -function getCaretCoordinates(element, position, options) { - if (!isBrowser) { - throw new Error('textarea-caret-position#getCaretCoordinates should only be called in a browser'); - } - - var debug = options && options.debug || false; - if (debug) { - var el = document.querySelector('#input-textarea-caret-position-mirror-div'); - if (el) el.parentNode.removeChild(el); - } - - // The mirror div will replicate the textarea's style - var div = document.createElement('div'); - div.id = 'input-textarea-caret-position-mirror-div'; - document.body.appendChild(div); - - var style = div.style; - var computed = window.getComputedStyle ? window.getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9 - var isInput = element.nodeName === 'INPUT'; - - // Default textarea styles - style.whiteSpace = 'pre-wrap'; - if (!isInput) - style.wordWrap = 'break-word'; // only for textarea-s - - // Position off-screen - style.position = 'absolute'; // required to return coordinates properly - if (!debug) - style.visibility = 'hidden'; // not 'display: none' because we want rendering - - // Transfer the element's properties to the div - properties.forEach(function (prop) { - if (isInput && prop === 'lineHeight') { - // Special case for s because text is rendered centered and line height may be != height - if (computed.boxSizing === "border-box") { - var height = parseInt(computed.height); - var outerHeight = - parseInt(computed.paddingTop) + - parseInt(computed.paddingBottom) + - parseInt(computed.borderTopWidth) + - parseInt(computed.borderBottomWidth); - var targetHeight = outerHeight + parseInt(computed.lineHeight); - if (height > targetHeight) { - style.lineHeight = height - outerHeight + "px"; - } else if (height === targetHeight) { - style.lineHeight = computed.lineHeight; - } else { - style.lineHeight = 0; - } - } else { - style.lineHeight = computed.height; - } - } else { - style[prop] = computed[prop]; - } - }); - - if (isFirefox) { - // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275 - if (element.scrollHeight > parseInt(computed.height)) - style.overflowY = 'scroll'; - } else { - style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll' - } - - div.textContent = element.value.substring(0, position); - // The second special handling for input type="text" vs textarea: - // spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037 - if (isInput) - div.textContent = div.textContent.replace(/\s/g, '\u00a0'); - - var span = document.createElement('span'); - // Wrapping must be replicated *exactly*, including when a long word gets - // onto the next line, with whitespace at the end of the line before (#7). - // The *only* reliable way to do that is to copy the *entire* rest of the - // textarea's content into the created at the caret position. - // For inputs, just '.' would be enough, but no need to bother. - span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all - div.appendChild(span); - - var coordinates = { - top: span.offsetTop + parseInt(computed['borderTopWidth']), - left: span.offsetLeft + parseInt(computed['borderLeftWidth']), - height: parseInt(computed['lineHeight']) - }; - - if (debug) { - span.style.backgroundColor = '#aaa'; - } else { - document.body.removeChild(div); - } - - return coordinates; -} - -if (typeof module != 'undefined' && typeof module.exports != 'undefined') { - module.exports = getCaretCoordinates; -} else if(isBrowser) { - window.getCaretCoordinates = getCaretCoordinates; -} - -}()); diff --git a/src/js/theme/theme.js b/src/js/theme/theme.js index 792508b4d7..1d7bdb063e 100644 --- a/src/js/theme/theme.js +++ b/src/js/theme/theme.js @@ -4,6 +4,13 @@ import { spacing } from './spacing' const $arrowBg = cssVar("popper-arrow-bg"); +const buttonIconFontSize = { + xs: "16px", + sm: "20px", + md: "24px", + lg: "32px", +} + const shadows = { focusLight: '0 0 0 3px #0071DB', focusDark: '0 0 0 3px #498eda', @@ -311,8 +318,7 @@ const components = { } }, Button: { - baseStyle: { - transitionProperty: 'common', + baseStyle: ({ size }) => ({ transitionTimingFunction: 'ease-in-out', _active: { transitionDuration: "0s", @@ -324,8 +330,11 @@ const components = { _focusVisible: { outline: 'none', boxShadow: 'focus' + }, + "> .chakra-button__icon, > .chakra-icon": { + fontSize: buttonIconFontSize[size], } - }, + }), variants: { link: { color: "link", @@ -397,37 +406,13 @@ const components = { error: { color: "error" } - } + }, }, FormLabel: { baseStyle: { color: "foreground.secondary", } }, - IconButton: { - baseStyle: { - fontSize: "1em", - _active: { - transitionDuration: "0s", - }, - _focus: { - outline: 'none', - boxShadow: 'none' - }, - _focusVisible: { - outline: 'none', - boxShadow: 'focus' - } - }, - variants: { - solid: { - _active: { - color: 'linkContrast', - bg: 'link', - }, - } - } - }, Menu: { baseStyle: { list: { @@ -511,6 +496,13 @@ const components = { content: { bg: "background.upper", shadow: "popover", + _focus: { + outline: 'none', + shadow: "popover", + }, + _focusVisible: { + shadow: "popover", + }, [$arrowBg.variable]: "colors.background.upper", } } @@ -619,7 +611,11 @@ const components = { } // Default prop overrides -Tooltip.defaultProps = { ...Tooltip.defaultProps, openDelay: 500 } +Tooltip.defaultProps = { + ...Tooltip.defaultProps, + closeOnMouseDown: true, + openDelay: 500 +} const config = { initialColorMode: 'system', diff --git a/yarn.lock b/yarn.lock index 6d468f064d..7a950ff727 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3016,6 +3016,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@socket.io/base64-arraybuffer@~1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#568d9beae00b0d835f4f8c53fd55714986492e61" + integrity sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ== + "@storybook/addon-a11y@^6.3.8": version "6.3.8" resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-6.3.8.tgz#7d6542d8e11fab38b21e7280100b0001a2b75232" @@ -4064,12 +4069,12 @@ resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.10.tgz#ef5b1589b9f16544642e473db5ea5639107ef3ea" integrity sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg== -"@types/cookie@^0.4.0": +"@types/cookie@^0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== -"@types/cors@^2.8.8": +"@types/cors@^2.8.12": version "2.8.12" resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== @@ -5046,11 +5051,6 @@ async@0.2.10: resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= -async@0.9.x: - version "0.9.2" - resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" - integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= - async@^2.6.1: version "2.6.3" resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" @@ -5058,6 +5058,11 @@ async@^2.6.1: dependencies: lodash "^4.17.14" +async@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" + integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -5263,11 +5268,6 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-arraybuffer@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" - integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= - base64-js@^1.0.2, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -5435,6 +5435,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^2.3.1, braces@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" @@ -5592,9 +5599,9 @@ buffer-fill@^1.0.0: integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer-xor@^1.0.3: version "1.0.3" @@ -5870,7 +5877,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.1: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -5920,10 +5927,10 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.4.0, chokidar@^3.4.1, chokidar@^3.4.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== +chokidar@^3.4.0, chokidar@^3.4.1, chokidar@^3.4.2, chokidar@^3.5.1: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" braces "~3.0.2" @@ -6114,7 +6121,7 @@ colors@1.0.3: resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= -colors@^1.1.2, colors@^1.4.0: +colors@1.4.0, colors@^1.1.2, colors@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== @@ -6980,7 +6987,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6. dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@~4.3.1: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@~4.3.1, debug@~4.3.2: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== @@ -7388,11 +7395,11 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= ejs@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a" - integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw== + version "3.1.7" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.7.tgz#c544d9c7f715783dd92f0bddcf73a59e6962d006" + integrity sha512-BIar7R6abbUxDA3bfXrO4DSgwo8I+fB5/1zgujl3HLLjwd6+9iOnrT+t3grn2qbk9vOgBubXOFwX2m9axoFaGw== dependencies: - jake "^10.6.1" + jake "^10.8.5" electron-builder-notarize@^1.2.0: version "1.2.0" @@ -7580,25 +7587,28 @@ endent@^2.0.1: fast-json-parse "^1.0.3" objectorarray "^1.0.5" -engine.io-parser@~4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-4.0.3.tgz#83d3a17acfd4226f19e721bb22a1ee8f7662d2f6" - integrity sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA== +engine.io-parser@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.3.tgz#ca1f0d7b11e290b4bfda251803baea765ed89c09" + integrity sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg== dependencies: - base64-arraybuffer "0.1.4" + "@socket.io/base64-arraybuffer" "~1.0.2" -engine.io@~4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-4.1.2.tgz#f96ceb56d4b39cc7ca5bd29a20e9c99c1ad1a765" - integrity sha512-t5z6zjXuVLhXDMiFJPYsPOWEER8B0tIsD3ETgw19S1yg9zryvUfY3Vhtk3Gf4sihw/bQGIqQ//gjvVlu+Ca0bQ== +engine.io@~6.1.0: + version "6.1.3" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.1.3.tgz#f156293d011d99a3df5691ac29d63737c3302e6f" + integrity sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA== dependencies: + "@types/cookie" "^0.4.1" + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" accepts "~1.3.4" base64id "2.0.0" cookie "~0.4.1" cors "~2.8.5" debug "~4.3.1" - engine.io-parser "~4.0.0" - ws "~7.4.2" + engine.io-parser "~5.0.3" + ws "~8.2.3" enhanced-resolve@^4.5.0: version "4.5.0" @@ -8080,11 +8090,11 @@ file-uri-to-path@1.0.0: integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== filelist@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b" - integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ== + version "1.0.3" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.3.tgz#448607750376484932f67ef1b9ff07386b036c83" + integrity sha512-LwjCsruLWQULGYKy7TX0OPtrL9kLpojOFKc5VCTxdFTV7w5zbsgqVKfnkKG7Qgjtq50gKfO56hJv88OfcGb70Q== dependencies: - minimatch "^3.0.4" + minimatch "^5.0.1" filename-reserved-regex@^1.0.0: version "1.0.0" @@ -8240,9 +8250,9 @@ focus-lock@^0.9.1: tslib "^2.0.3" follow-redirects@^1.0.0: - version "1.14.7" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" - integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== + version "1.14.8" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" + integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== for-in@^1.0.2: version "1.0.2" @@ -8680,10 +8690,10 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@^7.0.0, glob@^7.0.3, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== +glob@^7.0.0, glob@^7.0.3, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -8831,17 +8841,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.9, graceful-fs@^4.2.4: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== - -graceful-fs@^4.1.2: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -graceful-fs@^4.1.6, graceful-fs@^4.2.0: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.9" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== @@ -9833,7 +9833,7 @@ isbinaryfile@^3.0.2: dependencies: buffer-alloc "^1.2.0" -isbinaryfile@^4.0.6, isbinaryfile@^4.0.8: +isbinaryfile@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.8.tgz#5d34b94865bd4946633ecc78a026fc76c5b11fcf" integrity sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w== @@ -9905,13 +9905,13 @@ iterate-value@^1.0.2: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" -jake@^10.6.1: - version "10.8.2" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b" - integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A== +jake@^10.8.5: + version "10.8.5" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" + integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== dependencies: - async "0.9.x" - chalk "^2.4.2" + async "^3.2.3" + chalk "^4.0.2" filelist "^1.0.1" minimatch "^3.0.4" @@ -10031,9 +10031,9 @@ jest-worker@^26.5.0, jest-worker@^26.6.2: supports-color "^7.0.0" jpeg-js@^0.4.2: - version "0.4.3" - resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b" - integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q== + version "0.4.4" + resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa" + integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg== js-string-escape@^1.0.1: version "1.0.1" @@ -10174,33 +10174,34 @@ karma-junit-reporter@^2.0.1: path-is-absolute "^1.0.0" xmlbuilder "12.0.0" -karma@^6.3.3: - version "6.3.3" - resolved "https://registry.yarnpkg.com/karma/-/karma-6.3.3.tgz#bd04c7c533f8de99b3c3e85e191d0a6ae2621ad1" - integrity sha512-JRAujkKWaOtO2LmyPH7K2XXRhrxuFAn9loIL9+iiah6vjz+ZLkqdKsySV9clRITGhj10t9baIfbCl6CJ5hu9gQ== +karma@^6.3.16: + version "6.3.16" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.3.16.tgz#76d1a705fd1cf864ee5ed85270b572641e0958ef" + integrity sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ== dependencies: body-parser "^1.19.0" braces "^3.0.2" - chokidar "^3.4.2" - colors "^1.4.0" + chokidar "^3.5.1" + colors "1.4.0" connect "^3.7.0" di "^0.0.1" dom-serialize "^2.2.1" - glob "^7.1.6" - graceful-fs "^4.2.4" + glob "^7.1.7" + graceful-fs "^4.2.6" http-proxy "^1.18.1" - isbinaryfile "^4.0.6" - lodash "^4.17.19" - log4js "^6.2.1" - mime "^2.4.5" + isbinaryfile "^4.0.8" + lodash "^4.17.21" + log4js "^6.4.1" + mime "^2.5.2" minimatch "^3.0.4" + mkdirp "^0.5.5" qjobs "^1.2.0" range-parser "^1.2.1" rimraf "^3.0.2" - socket.io "^3.1.0" + socket.io "^4.2.0" source-map "^0.6.1" - tmp "0.2.1" - ua-parser-js "^0.7.23" + tmp "^0.2.1" + ua-parser-js "^0.7.30" yargs "^16.1.1" katex@^0.12.0: @@ -10411,7 +10412,7 @@ lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log4js@^6.2.1: +log4js@^6.4.1: version "6.4.1" resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.4.1.tgz#9d3a8bf2c31c1e213fe3fc398a6053f7a2bc53e8" integrity sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg== @@ -10774,12 +10775,7 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.4.4, mime@^2.4.5: - version "2.5.2" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" - integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== - -mime@^2.4.6, mime@^2.5.2: +mime@^2.4.4, mime@^2.4.6, mime@^2.5.2: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== @@ -10811,13 +10807,27 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -10828,9 +10838,9 @@ minimist-options@4.1.0: kind-of "^6.0.3" minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== minipass-collect@^1.0.2: version "1.0.2" @@ -10892,7 +10902,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@~0.5.1: +mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -11734,19 +11744,10 @@ playwright@^1.17.1: dependencies: playwright-core "=1.17.1" -plist@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.2.tgz#74bbf011124b90421c22d15779cee60060ba95bc" - integrity sha512-MSrkwZBdQ6YapHy87/8hDU8MnIcyxBKjeF+McXnr5A9MtffPewTs7G3hlpodT5TacyfIyFTaJEhh3GGcmasTgQ== - dependencies: - base64-js "^1.5.1" - xmlbuilder "^9.0.7" - xmldom "^0.5.0" - -plist@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe" - integrity sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg== +plist@^3.0.1, plist@^3.0.4: + version "3.0.5" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987" + integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA== dependencies: base64-js "^1.5.1" xmlbuilder "^9.0.7" @@ -13339,12 +13340,12 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socket.io-adapter@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz#edc5dc36602f2985918d631c1399215e97a1b527" - integrity sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg== +socket.io-adapter@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz#4d6111e4d42e9f7646e365b4f578269821f13486" + integrity sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ== -socket.io-parser@~4.0.3: +socket.io-parser@~4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0" integrity sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g== @@ -13353,20 +13354,17 @@ socket.io-parser@~4.0.3: component-emitter "~1.3.0" debug "~4.3.1" -socket.io@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-3.1.2.tgz#06e27caa1c4fc9617547acfbb5da9bc1747da39a" - integrity sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw== +socket.io@^4.2.0: + version "4.4.1" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.4.1.tgz#cd6de29e277a161d176832bb24f64ee045c56ab8" + integrity sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg== dependencies: - "@types/cookie" "^0.4.0" - "@types/cors" "^2.8.8" - "@types/node" ">=10.0.0" accepts "~1.3.4" base64id "~2.0.0" - debug "~4.3.1" - engine.io "~4.1.0" - socket.io-adapter "~2.1.0" - socket.io-parser "~4.0.3" + debug "~4.3.2" + engine.io "~6.1.0" + socket.io-adapter "~2.3.3" + socket.io-parser "~4.0.4" socks-proxy-agent@^6.1.0: version "6.1.1" @@ -13415,15 +13413,7 @@ source-map-support@^0.4.15, source-map-support@^0.4.18: dependencies: source-map "^0.5.6" -source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@~0.5.12, source-map-support@~0.5.19: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-support@^0.5.17: +source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@~0.5.12, source-map-support@~0.5.19: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -14012,9 +14002,9 @@ terser-webpack-plugin@^4.2.3: webpack-sources "^1.4.3" terser@^4.1.2, terser@^4.6.3: - version "4.8.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" - integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== + version "4.8.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f" + integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -14048,6 +14038,11 @@ text-table@0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +textarea-caret@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/textarea-caret/-/textarea-caret-3.1.0.tgz#5d5a35bb035fd06b2ff0e25d5359e97f2655087f" + integrity sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q== + throttle-debounce@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb" @@ -14097,7 +14092,7 @@ tmp-promise@^3.0.2: dependencies: tmp "^0.2.0" -tmp@0.2.1, tmp@^0.2.0: +tmp@^0.2.0, tmp@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== @@ -14342,7 +14337,7 @@ typescript@^4.3.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== -ua-parser-js@0.7.28, ua-parser-js@^0.7.23: +ua-parser-js@0.7.28, ua-parser-js@^0.7.30: version "0.7.28" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g== @@ -15005,10 +15000,10 @@ ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== -ws@~7.4.2: - version "7.4.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" - integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +ws@~8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" + integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== xdg-basedir@^4.0.0: version "4.0.0" @@ -15030,11 +15025,6 @@ xmlbuilder@^9.0.7: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= -xmldom@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.5.0.tgz#193cb96b84aa3486127ea6272c4596354cb4962e" - integrity sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA== - xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"