Franknoirot/help menu (#2173)
* Add exclamationMark icon * Add basic LowerRightControls component * Create a help menu * Remove NetworkHealthIndicator from AppHeader * Refactor Tooltip to be able to be corner-anchored * Add a better flag back to the Tooltip * Give tooltip a faint theme outline on light mode too * Fix broken reset onboarding behavior on home page * Fix bug with isInProject * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 45 KiB |
@ -21,6 +21,7 @@ import { isTauri } from 'lib/isTauri'
|
|||||||
import { useLspContext } from 'components/LspProvider'
|
import { useLspContext } from 'components/LspProvider'
|
||||||
import { useRefreshSettings } from 'hooks/useRefreshSettings'
|
import { useRefreshSettings } from 'hooks/useRefreshSettings'
|
||||||
import { ModelingSidebar } from 'components/ModelingSidebar/ModelingSidebar'
|
import { ModelingSidebar } from 'components/ModelingSidebar/ModelingSidebar'
|
||||||
|
import { LowerRightControls } from 'components/LowerRightControls'
|
||||||
import ModalContainer from 'react-modal-promise'
|
import ModalContainer from 'react-modal-promise'
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
@ -127,6 +128,7 @@ export function App() {
|
|||||||
<ModelingSidebar paneOpacity={paneOpacity} />
|
<ModelingSidebar paneOpacity={paneOpacity} />
|
||||||
<Stream className="absolute inset-0 z-0" />
|
<Stream className="absolute inset-0 z-0" />
|
||||||
{/* <CamToggle /> */}
|
{/* <CamToggle /> */}
|
||||||
|
<LowerRightControls />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import { type IndexLoaderData } from 'lib/types'
|
|||||||
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import styles from './AppHeader.module.css'
|
import styles from './AppHeader.module.css'
|
||||||
import { NetworkHealthIndicator } from './NetworkHealthIndicator'
|
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import usePlatform from 'hooks/usePlatform'
|
import usePlatform from 'hooks/usePlatform'
|
||||||
@ -61,12 +60,7 @@ export const AppHeader = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1 py-1 ml-auto">
|
<div className="flex items-center gap-1 py-1 ml-auto">
|
||||||
{/* If there are children, show them, otherwise show User menu */}
|
{/* If there are children, show them, otherwise show User menu */}
|
||||||
{children || (
|
{children || <UserSidebarMenu user={user} />}
|
||||||
<>
|
|
||||||
<NetworkHealthIndicator />
|
|
||||||
<UserSidebarMenu user={user} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
|
@ -142,7 +142,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
|
|||||||
name="make-variable"
|
name="make-variable"
|
||||||
className="w-4 h-4"
|
className="w-4 h-4"
|
||||||
/>
|
/>
|
||||||
<Tooltip position="blockEnd">
|
<Tooltip position="bottom">
|
||||||
New variable:{' '}
|
New variable:{' '}
|
||||||
{
|
{
|
||||||
(
|
(
|
||||||
|
@ -99,6 +99,14 @@ const CustomIconMap = {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
|
exclamationMark: (
|
||||||
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M9.76391 11.6597L9.3633 7.91112V5.00671H10.6224V7.91112L10.2217 11.6597H9.76391ZM9.99283 15.1221C9.60176 15.1221 9.32515 15.041 9.163 14.8788C9.01039 14.7167 8.93408 14.5116 8.93408 14.2636V14.0061C8.93408 13.7581 9.01039 13.553 9.163 13.3909C9.32515 13.2287 9.60176 13.1476 9.99283 13.1476C10.3839 13.1476 10.6557 13.2287 10.8084 13.3909C10.9705 13.553 11.0516 13.7581 11.0516 14.0061V14.2636C11.0516 14.5116 10.9705 14.7167 10.8084 14.8788C10.6557 15.041 10.3839 15.1221 9.99283 15.1221Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
exportFile: (
|
exportFile: (
|
||||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
@ -289,6 +297,14 @@ const CustomIconMap = {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
|
questionMark: (
|
||||||
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M9.12005 11.9172V9.67093C9.94034 9.63278 10.5842 9.45632 11.0515 9.14156C11.5189 8.81725 11.7526 8.3308 11.7526 7.6822V7.48189C11.7526 6.94775 11.5905 6.54714 11.2662 6.28007C10.9514 6.013 10.5174 5.87946 9.96419 5.87946C9.39189 5.87946 8.93405 6.03685 8.59067 6.35161C8.2473 6.66637 8.00884 7.06698 7.8753 7.55343L6.80225 7.15282C6.89763 6.83806 7.03116 6.54237 7.20285 6.26576C7.37454 5.97962 7.58915 5.73162 7.84669 5.52178C8.11376 5.31194 8.42375 5.14502 8.77667 5.02102C9.13912 4.89702 9.54927 4.83502 10.0071 4.83502C10.4649 4.83502 10.8751 4.90179 11.2375 5.03533C11.6095 5.15932 11.9243 5.34055 12.1818 5.57901C12.4394 5.80793 12.6397 6.08931 12.7827 6.42315C12.9258 6.75699 12.9974 7.12898 12.9974 7.53912C12.9974 7.98742 12.9163 8.38326 12.7541 8.72664C12.592 9.06048 12.3821 9.34663 12.1246 9.58508C11.8671 9.82354 11.5714 10.0191 11.2375 10.1717C10.9132 10.3148 10.5842 10.4149 10.2503 10.4721V11.9172H9.12005ZM9.73527 15.1221C9.3442 15.1221 9.06759 15.041 8.90544 14.8788C8.75282 14.7167 8.67652 14.5116 8.67652 14.2636V14.0061C8.67652 13.7581 8.75282 13.553 8.90544 13.3909C9.06759 13.2287 9.3442 13.1476 9.73527 13.1476C10.1263 13.1476 10.3982 13.2287 10.5508 13.3909C10.7129 13.553 10.794 13.7581 10.794 14.0061V14.2636C10.794 14.5116 10.7129 14.7167 10.5508 14.8788C10.3982 15.041 10.1263 15.1221 9.73527 15.1221Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
refresh: (
|
refresh: (
|
||||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
|
@ -362,8 +362,8 @@ export const FileTree = ({
|
|||||||
className="!p-0 !bg-transparent hover:text-primary border-transparent hover:border-primary !outline-none"
|
className="!p-0 !bg-transparent hover:text-primary border-transparent hover:border-primary !outline-none"
|
||||||
onClick={createFile}
|
onClick={createFile}
|
||||||
>
|
>
|
||||||
<Tooltip position="inlineStart" delay={750}>
|
<Tooltip position="bottom-right" delay={750}>
|
||||||
Create File
|
Create file
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
|
||||||
@ -377,8 +377,8 @@ export const FileTree = ({
|
|||||||
className="!p-0 !bg-transparent hover:text-primary border-transparent hover:border-primary !outline-none"
|
className="!p-0 !bg-transparent hover:text-primary border-transparent hover:border-primary !outline-none"
|
||||||
onClick={createFolder}
|
onClick={createFolder}
|
||||||
>
|
>
|
||||||
<Tooltip position="inlineStart" delay={750}>
|
<Tooltip position="bottom-right" delay={750}>
|
||||||
Create Folder
|
Create folder
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
|
145
src/components/HelpMenu.tsx
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import { Popover } from '@headlessui/react'
|
||||||
|
import Tooltip from './Tooltip'
|
||||||
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
|
import { CustomIcon } from './CustomIcon'
|
||||||
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
import { createAndOpenNewProject } from 'lib/tauriFS'
|
||||||
|
import { paths } from 'lib/paths'
|
||||||
|
|
||||||
|
const HelpMenuDivider = () => (
|
||||||
|
<div className="h-[1px] bg-chalkboard-110 dark:bg-chalkboard-80" />
|
||||||
|
)
|
||||||
|
|
||||||
|
export function HelpMenu(props: React.PropsWithChildren) {
|
||||||
|
const location = useLocation()
|
||||||
|
const isInProject = location.pathname.includes(paths.FILE)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const { settings } = useSettingsAuthContext()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover className="relative">
|
||||||
|
<Popover.Button className="border-none p-0 m-0 rounded-full grid place-content-center">
|
||||||
|
<CustomIcon
|
||||||
|
name="questionMark"
|
||||||
|
className="w-7 h-7 rounded-full bg-chalkboard-110 dark:bg-chalkboard-80 text-chalkboard-10"
|
||||||
|
/>
|
||||||
|
<Tooltip position="top-right" className="ui-open:hidden">
|
||||||
|
Help and resources
|
||||||
|
</Tooltip>
|
||||||
|
</Popover.Button>
|
||||||
|
<Popover.Panel
|
||||||
|
as="ul"
|
||||||
|
className="absolute right-0 left-auto bottom-full mb-1 w-64 py-2 flex flex-col gap-1 align-stretch text-chalkboard-10 dark:text-inherit bg-chalkboard-110 dark:bg-chalkboard-100 rounded shadow-lg border border-solid border-chalkboard-110 dark:border-chalkboard-80 text-sm m-0 p-0"
|
||||||
|
>
|
||||||
|
<HelpMenuItem
|
||||||
|
as="a"
|
||||||
|
href="https://github.com/KittyCAD/modeling-app/issues/new/choose"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Report a bug
|
||||||
|
</HelpMenuItem>
|
||||||
|
<HelpMenuItem
|
||||||
|
as="a"
|
||||||
|
href="https://github.com/KittyCAD/modeling-app/discussions"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Request a feature
|
||||||
|
</HelpMenuItem>
|
||||||
|
<HelpMenuItem
|
||||||
|
as="a"
|
||||||
|
href="https://discord.gg/JQEpHR7Nt2"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Ask the community
|
||||||
|
</HelpMenuItem>
|
||||||
|
<HelpMenuDivider />
|
||||||
|
<HelpMenuItem
|
||||||
|
as="a"
|
||||||
|
href="https://zoo.dev/docs/kcl-samples"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
KCL code samples
|
||||||
|
</HelpMenuItem>
|
||||||
|
<HelpMenuItem
|
||||||
|
as="a"
|
||||||
|
href="https://zoo.dev/docs/kcl"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
KCL docs
|
||||||
|
</HelpMenuItem>
|
||||||
|
<HelpMenuDivider />
|
||||||
|
<HelpMenuItem
|
||||||
|
as="a"
|
||||||
|
href="https://github.com/KittyCAD/modeling-app/releases"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Release notes
|
||||||
|
</HelpMenuItem>
|
||||||
|
<HelpMenuItem
|
||||||
|
as="button"
|
||||||
|
onClick={() => {
|
||||||
|
settings.send({
|
||||||
|
type: 'set.app.onboardingStatus',
|
||||||
|
data: {
|
||||||
|
value: '',
|
||||||
|
level: 'user',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (isInProject) {
|
||||||
|
navigate('onboarding')
|
||||||
|
} else {
|
||||||
|
createAndOpenNewProject(
|
||||||
|
settings.context.app.projectDirectory.current,
|
||||||
|
navigate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reset onboarding
|
||||||
|
</HelpMenuItem>
|
||||||
|
</Popover.Panel>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelpMenuItemProps =
|
||||||
|
| ({
|
||||||
|
as: 'a'
|
||||||
|
} & React.ComponentProps<'a'>)
|
||||||
|
| ({
|
||||||
|
as: 'button'
|
||||||
|
} & React.ComponentProps<'button'>)
|
||||||
|
|
||||||
|
function HelpMenuItem({
|
||||||
|
as,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: HelpMenuItemProps) {
|
||||||
|
const baseClassName = 'block px-2 py-1 hover:bg-chalkboard-80'
|
||||||
|
return (
|
||||||
|
<li className="m-0 p-0">
|
||||||
|
{as === 'a' ? (
|
||||||
|
<a
|
||||||
|
{...(props as React.ComponentProps<'a'>)}
|
||||||
|
className={`no-underline text-inherit ${baseClassName} ${className}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
{...(props as React.ComponentProps<'button'>)}
|
||||||
|
className={`border-0 p-0 m-0 text-sm w-full rounded-none text-left ${baseClassName} ${className}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
57
src/components/LowerRightControls.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { APP_VERSION } from 'routes/Settings'
|
||||||
|
import { CustomIcon } from 'components/CustomIcon'
|
||||||
|
import Tooltip from 'components/Tooltip'
|
||||||
|
import { paths } from 'lib/paths'
|
||||||
|
import { NetworkHealthIndicator } from 'components/NetworkHealthIndicator'
|
||||||
|
import { HelpMenu } from './HelpMenu'
|
||||||
|
import { Link, useLocation } from 'react-router-dom'
|
||||||
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||||
|
|
||||||
|
export function LowerRightControls(props: React.PropsWithChildren) {
|
||||||
|
const location = useLocation()
|
||||||
|
const filePath = useAbsoluteFilePath()
|
||||||
|
const linkOverrideClassName =
|
||||||
|
'!text-chalkboard-70 hover:!text-chalkboard-80 dark:!text-chalkboard-40 dark:hover:!text-chalkboard-30'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="fixed bottom-2 right-2">
|
||||||
|
{props.children}
|
||||||
|
<menu className="flex items-center justify-end gap-3">
|
||||||
|
<a
|
||||||
|
href={`https://github.com/KittyCAD/modeling-app/releases/tag/v${APP_VERSION}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={'!no-underline font-mono text-xs ' + linkOverrideClassName}
|
||||||
|
>
|
||||||
|
v{APP_VERSION}
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://github.com/KittyCAD/modeling-app/issues/new/choose"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<CustomIcon
|
||||||
|
name="exclamationMark"
|
||||||
|
className={`w-5 h-5 ${linkOverrideClassName}`}
|
||||||
|
/>
|
||||||
|
<Tooltip position="top">Report a bug</Tooltip>
|
||||||
|
</a>
|
||||||
|
<Link
|
||||||
|
to={
|
||||||
|
location.pathname.includes(paths.FILE)
|
||||||
|
? filePath + paths.SETTINGS
|
||||||
|
: paths.HOME + paths.SETTINGS
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CustomIcon
|
||||||
|
name="settings"
|
||||||
|
className={`w-5 h-5 ${linkOverrideClassName}`}
|
||||||
|
/>
|
||||||
|
<Tooltip position="top">Settings</Tooltip>
|
||||||
|
</Link>
|
||||||
|
<NetworkHealthIndicator />
|
||||||
|
<HelpMenu />
|
||||||
|
</menu>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
@ -227,11 +227,11 @@ export const NetworkHealthIndicator = () => {
|
|||||||
'rounded-sm ' + overallConnectionStateColor[overallState].bg
|
'rounded-sm ' + overallConnectionStateColor[overallState].bg
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Tooltip position="left" delay={750} className="ui-open:hidden">
|
<Tooltip position="top-right" className="ui-open:hidden">
|
||||||
Network Health ({NETWORK_HEALTH_TEXT[overallState]})
|
Network Health ({NETWORK_HEALTH_TEXT[overallState]})
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Popover.Button>
|
</Popover.Button>
|
||||||
<Popover.Panel className="absolute right-0 left-auto top-full mt-1 w-64 flex flex-col gap-1 align-stretch bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm">
|
<Popover.Panel className="absolute right-0 left-auto bottom-full mb-1 w-64 flex flex-col gap-1 align-stretch bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm">
|
||||||
<div
|
<div
|
||||||
className={`flex items-center justify-between p-2 rounded-t-sm ${overallConnectionStateColor[overallState].bg} ${overallConnectionStateColor[overallState].icon}`}
|
className={`flex items-center justify-between p-2 rounded-t-sm ${overallConnectionStateColor[overallState].bg} ${overallConnectionStateColor[overallState].icon}`}
|
||||||
>
|
>
|
||||||
|
@ -1,47 +1,40 @@
|
|||||||
/* Adapted from https://github.com/argyleink/gui-challenges/blob/main/tooltips/tool-tip.css */
|
/* Adapted from https://github.com/argyleink/gui-challenges/blob/main/tooltips/tool-tip.css */
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
/* internal CSS vars */
|
|
||||||
--_delay: 200ms;
|
|
||||||
--_p-inline: 1ch;
|
|
||||||
--_p-block: 4px;
|
|
||||||
--_triangle-size: 7px;
|
|
||||||
/* --_bg: hsl(0 0% 20%); */
|
|
||||||
--_bg: var(--chalkboard-10);
|
|
||||||
--_shadow-alpha: 20%;
|
|
||||||
|
|
||||||
/* Used to power spacing and layout for RTL languages */
|
/* Used to power spacing and layout for RTL languages */
|
||||||
--isRTL: -1;
|
--isRTL: -1;
|
||||||
|
|
||||||
/* Using conic gradients to get a clear tip triangle */
|
/* internal CSS vars */
|
||||||
--_bottom-tip: conic-gradient(
|
--_delay: 200ms;
|
||||||
from -30deg at bottom,
|
--_triangle-width: 8px;
|
||||||
#0000,
|
--_triangle-height: 12px;
|
||||||
#000 1deg 60deg,
|
--_p-inline: calc(50% + calc(var(--isRTL) * var(--_triangle-width) / 2));
|
||||||
#0000 61deg
|
--_p-block: 4px;
|
||||||
|
--_bg: var(--chalkboard-10);
|
||||||
|
--_shadow-alpha: 5%;
|
||||||
|
--_theme-alpha: 0.15;
|
||||||
|
--_theme-outline: drop-shadow(
|
||||||
|
0 1px 0
|
||||||
|
oklch(
|
||||||
|
var(--primary-lightness) var(--primary-chroma) var(--primary-hue) /
|
||||||
|
var(--_theme-alpha)
|
||||||
)
|
)
|
||||||
bottom / 100% 50% no-repeat;
|
|
||||||
--_top-tip: conic-gradient(
|
|
||||||
from 150deg at top,
|
|
||||||
#0000,
|
|
||||||
#000 1deg 60deg,
|
|
||||||
#0000 61deg
|
|
||||||
)
|
)
|
||||||
top / 100% 50% no-repeat;
|
drop-shadow(
|
||||||
--_right-tip: conic-gradient(
|
0 -1px 0 oklch(var(--primary-lightness) var(--primary-chroma)
|
||||||
from -120deg at right,
|
var(--primary-hue) / var(--_theme-alpha))
|
||||||
#0000,
|
|
||||||
#000 1deg 60deg,
|
|
||||||
#0000 61deg
|
|
||||||
)
|
)
|
||||||
right / 50% 100% no-repeat;
|
drop-shadow(
|
||||||
--_left-tip: conic-gradient(
|
1px 0 0
|
||||||
from 60deg at left,
|
oklch(
|
||||||
#0000,
|
var(--primary-lightness) var(--primary-chroma) var(--primary-hue) /
|
||||||
#000 1deg 60deg,
|
var(--_theme-alpha)
|
||||||
#0000 61deg
|
|
||||||
)
|
)
|
||||||
left / 50% 100% no-repeat;
|
)
|
||||||
|
drop-shadow(
|
||||||
|
-1px 0 0 oklch(var(--primary-lightness) var(--primary-chroma)
|
||||||
|
var(--primary-hue) / var(--_theme-alpha))
|
||||||
|
);
|
||||||
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -69,22 +62,17 @@
|
|||||||
@apply text-chalkboard-110;
|
@apply text-chalkboard-110;
|
||||||
will-change: filter;
|
will-change: filter;
|
||||||
filter: drop-shadow(0 1px 3px hsl(0 0% 0% / var(--_shadow-alpha)))
|
filter: drop-shadow(0 1px 3px hsl(0 0% 0% / var(--_shadow-alpha)))
|
||||||
drop-shadow(0 6px 12px hsl(0 0% 0% / var(--_shadow-alpha)));
|
drop-shadow(0 4px 8px hsl(0 0% 0% / var(--_shadow-alpha)))
|
||||||
|
var(--_theme-outline);
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.dark) .tooltip {
|
:global(.dark) .tooltip {
|
||||||
--_bg: var(--chalkboard-110);
|
--_bg: var(--chalkboard-110);
|
||||||
|
--_theme-alpha: 40%;
|
||||||
@apply text-chalkboard-10;
|
@apply text-chalkboard-10;
|
||||||
|
filter: var(--_theme-outline);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO we don't support a light theme yet */
|
|
||||||
/* @media (prefers-color-scheme: light) {
|
|
||||||
.tooltip {
|
|
||||||
--_bg: white;
|
|
||||||
--_shadow-alpha: 15%;
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
.tooltip:dir(rtl) {
|
.tooltip:dir(rtl) {
|
||||||
--isRTL: 1;
|
--isRTL: 1;
|
||||||
}
|
}
|
||||||
@ -103,7 +91,7 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
:is(:focus, :focus-visible, :focus-within) > .tooltip {
|
.tooltip:focus-visible {
|
||||||
--_delay: 0 !important;
|
--_delay: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,114 +108,148 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tooltip shape is a pseudo element so we can cast a shadow */
|
/* Sometimes there's no visible label,
|
||||||
.tooltip::after {
|
* so we'll use the tooltip as the label
|
||||||
content: '';
|
*/
|
||||||
background: var(--_bg);
|
.tooltip:only-child::before {
|
||||||
position: absolute;
|
content: 'Tooltip:';
|
||||||
z-index: -1;
|
|
||||||
inset: 0;
|
|
||||||
mask: var(--_tip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip.top,
|
.caret {
|
||||||
.tooltip.blockStart,
|
width: 8px;
|
||||||
.tooltip.bottom,
|
height: var(--_triangle-height);
|
||||||
.tooltip.blockEnd {
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
transform-origin: center center;
|
||||||
|
color: var(--_bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top,
|
||||||
|
.bottom {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TOP || BLOCK-START */
|
.tooltip.top {
|
||||||
.tooltip.top,
|
|
||||||
.tooltip.blockStart {
|
|
||||||
inset-inline-start: 50%;
|
inset-inline-start: 50%;
|
||||||
inset-block-end: calc(100% + var(--_p-block) + var(--_triangle-size));
|
inset-block-end: calc(100% + var(--_p-block) + var(--_triangle-height));
|
||||||
--_x: calc(50% * var(--isRTL));
|
--_x: calc(50% * var(--isRTL));
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip.top::after,
|
.top .caret {
|
||||||
.tooltip.tooltip.blockStart::after {
|
inset-block-start: calc(100% - 1px);
|
||||||
--_tip: var(--_bottom-tip);
|
inset-inline-start: 50%;
|
||||||
inset-block-end: calc(var(--_triangle-size) * -1);
|
transform: translateX(-50%);
|
||||||
border-block-end: var(--_triangle-size) solid transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RIGHT || INLINE-END */
|
.tooltip.top-right {
|
||||||
.tooltip.right,
|
inset-inline-end: var(--_p-inline);
|
||||||
.tooltip.inlineEnd {
|
inset-block-end: calc(100% + var(--_p-block) + var(--_triangle-height));
|
||||||
inset-inline-start: calc(100% + var(--_p-inline) + var(--_triangle-size));
|
}
|
||||||
|
|
||||||
|
/* The corner caret SVG is bottom-right oriented by default */
|
||||||
|
.top-right .caret {
|
||||||
|
inset-block-start: calc(100% - 1px);
|
||||||
|
inset-inline-end: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip.right {
|
||||||
|
inset-inline-start: calc(100% + var(--_p-inline) + var(--_triangle-height));
|
||||||
inset-block-end: 50%;
|
inset-block-end: 50%;
|
||||||
--_y: 50%;
|
--_y: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip.right::after,
|
.right .caret {
|
||||||
.tooltip.tooltip.inlineEnd::after {
|
inset-inline-end: calc(100% - 1px);
|
||||||
--_tip: var(--_left-tip);
|
inset-block-start: 50%;
|
||||||
inset-inline-start: calc(var(--_triangle-size) * -1);
|
transform: translateY(-50%) rotate(90deg);
|
||||||
border-inline-start: var(--_triangle-size) solid transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip.right:dir(rtl)::after,
|
.tooltip.bottom-right {
|
||||||
.tooltip.inlineEnd:dir(rtl)::after {
|
inset-inline-end: var(--_p-inline);
|
||||||
--_tip: var(--_right-tip);
|
inset-block-start: calc(100% + var(--_p-block) + var(--_triangle-height));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BOTTOM || BLOCK-END */
|
.bottom-right .caret {
|
||||||
.tooltip.bottom,
|
inset-block-end: calc(100% - 1px);
|
||||||
.tooltip.blockEnd {
|
inset-inline-end: 0;
|
||||||
inset-inline-start: 50%;
|
transform: rotate(180deg) scaleX(-1);
|
||||||
inset-block-start: calc(100% + var(--_p-block) + var(--_triangle-size));
|
}
|
||||||
|
|
||||||
|
.tooltip.bottom {
|
||||||
--_x: calc(50% * var(--isRTL));
|
--_x: calc(50% * var(--isRTL));
|
||||||
|
inset-inline-start: 50%;
|
||||||
|
inset-block-start: calc(100% + var(--_p-block) + var(--_triangle-height));
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip.bottom::after,
|
.bottom .caret {
|
||||||
.tooltip.tooltip.blockEnd::after {
|
inset-block-end: calc(100% - 1px);
|
||||||
--_tip: var(--_top-tip);
|
inset-inline-start: 50%;
|
||||||
inset-block-start: calc(var(--_triangle-size) * -1);
|
transform: translateX(-50%) scaleY(-1);
|
||||||
border-block-start: var(--_triangle-size) solid transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* LEFT || INLINE-START */
|
.tooltip.bottom-left {
|
||||||
.tooltip.left,
|
inset-inline-start: var(--_p-inline);
|
||||||
.tooltip.inlineStart {
|
inset-block-start: calc(100% + var(--_p-block) + var(--_triangle-height));
|
||||||
inset-inline-end: calc(100% + var(--_p-inline) + var(--_triangle-size));
|
}
|
||||||
|
|
||||||
|
.bottom-left .caret {
|
||||||
|
inset-block-end: calc(100% - 1px);
|
||||||
|
inset-inline-start: 0;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip.left {
|
||||||
|
inset-inline-end: calc(100% + var(--_p-inline) + var(--_triangle-height));
|
||||||
inset-block-end: 50%;
|
inset-block-end: 50%;
|
||||||
--_y: 50%;
|
--_y: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip.left::after,
|
.left .caret {
|
||||||
.tooltip.tooltip.inlineStart::after {
|
inset-inline-start: calc(100% - 1px);
|
||||||
--_tip: var(--_right-tip);
|
inset-block-start: 50%;
|
||||||
inset-inline-end: calc(var(--_triangle-size) * -1);
|
transform: translateY(-50%) rotate(-90deg);
|
||||||
border-inline-end: var(--_triangle-size) solid transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip.left:dir(rtl)::after,
|
.tooltip.top-left {
|
||||||
.tooltip.inlineStart:dir(rtl)::after {
|
inset-inline-start: var(--_p-inline);
|
||||||
--_tip: var(--_left-tip);
|
inset-block-end: calc(100% + var(--_p-block) + var(--_triangle-height));
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-left .caret {
|
||||||
|
inset-block-start: calc(100% - 1px);
|
||||||
|
inset-inline-start: 0;
|
||||||
|
transform: rotate(-90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
/* TOP || BLOCK-START */
|
/* TOP || BLOCK-START */
|
||||||
:has(> :is(.tooltip.top, .tooltip.blockStart)):not(:hover, :active) .tooltip {
|
:has(> :is(.tooltip.top, .tooltip.top-left, .tooltip.top-right)):not(
|
||||||
|
:hover,
|
||||||
|
:active
|
||||||
|
)
|
||||||
|
.tooltip {
|
||||||
--_y: 3px;
|
--_y: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RIGHT || INLINE-END */
|
/* RIGHT || INLINE-END */
|
||||||
:has(> :is(.tooltip.right, .tooltip.inlineEnd)):not(:hover, :active)
|
:has(> :is(.tooltip.right)):not(:hover, :active) .tooltip {
|
||||||
.tooltip {
|
|
||||||
--_x: calc(var(--isRTL) * -3px * -1);
|
--_x: calc(var(--isRTL) * -3px * -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BOTTOM || BLOCK-END */
|
/* BOTTOM || BLOCK-END */
|
||||||
:has(> :is(.tooltip.bottom, .tooltip.blockEnd)):not(:hover, :active)
|
:has(
|
||||||
|
> :is(
|
||||||
|
.tooltip.bottom,
|
||||||
|
.tooltip.tooltip.bottom-left,
|
||||||
|
.tooltip.bottom-right
|
||||||
|
)
|
||||||
|
):not(:hover, :active)
|
||||||
.tooltip {
|
.tooltip {
|
||||||
--_y: -3px;
|
--_y: -3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BOTTOM || BLOCK-END */
|
/* BOTTOM || BLOCK-END */
|
||||||
:has(> :is(.tooltip.left, .tooltip.inlineStart)):not(:hover, :active)
|
:has(> :is(.tooltip.left)):not(:hover, :active) .tooltip {
|
||||||
.tooltip {
|
|
||||||
--_x: calc(var(--isRTL) * 3px * -1);
|
--_x: calc(var(--isRTL) * 3px * -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,16 +3,14 @@
|
|||||||
// eslint-disable-next-line css-modules/no-unused-class
|
// eslint-disable-next-line css-modules/no-unused-class
|
||||||
import styles from './Tooltip.module.css'
|
import styles from './Tooltip.module.css'
|
||||||
|
|
||||||
|
const SIDES = ['top', 'bottom', 'left', 'right'] as const
|
||||||
|
type TopOrBottom = 'top' | 'bottom'
|
||||||
|
type LeftOrRight = 'left' | 'right'
|
||||||
|
type Corner = `${TopOrBottom}-${LeftOrRight}`
|
||||||
|
type TooltipPosition = TopOrBottom | LeftOrRight | Corner
|
||||||
|
|
||||||
interface TooltipProps extends React.PropsWithChildren {
|
interface TooltipProps extends React.PropsWithChildren {
|
||||||
position?:
|
position?: TooltipPosition
|
||||||
| 'top'
|
|
||||||
| 'bottom'
|
|
||||||
| 'left'
|
|
||||||
| 'right'
|
|
||||||
| 'blockStart'
|
|
||||||
| 'blockEnd'
|
|
||||||
| 'inlineStart'
|
|
||||||
| 'inlineEnd'
|
|
||||||
className?: string
|
className?: string
|
||||||
delay?: number
|
delay?: number
|
||||||
hoverOnly?: boolean
|
hoverOnly?: boolean
|
||||||
@ -36,6 +34,31 @@ export default function Tooltip({
|
|||||||
style={{ '--_delay': delay + 'ms' } as React.CSSProperties}
|
style={{ '--_delay': delay + 'ms' } as React.CSSProperties}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
<div className={styles.caret}>
|
||||||
|
{SIDES.includes(position as any) ? (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 8 12"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M3.0513 9.154c.304.9116 1.5935.9116 1.8974 0L8 0H0l3.0513 9.154Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 8 10"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="m0 0 6.168 9.252C6.7168 10.0751 8 9.6865 8 8.6971V0H0Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -96,14 +96,19 @@ button:disabled {
|
|||||||
@apply bg-chalkboard-90 text-chalkboard-40 border-chalkboard-70;
|
@apply bg-chalkboard-90 text-chalkboard-40 border-chalkboard-70;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:not(.action-button) {
|
a {
|
||||||
@apply text-primary underline hover:hue-rotate-15;
|
@apply text-primary underline hover:hue-rotate-15;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark a:not(.action-button) {
|
.dark a {
|
||||||
@apply hover:brightness-110 hover:hue-rotate-0;
|
@apply hover:brightness-110 hover:hue-rotate-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.action-button,
|
||||||
|
.dark a.action-button {
|
||||||
|
@apply text-inherit no-underline hover:hue-rotate-0;
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
@apply selection:bg-primary/50;
|
@apply selection:bg-primary/50;
|
||||||
}
|
}
|
||||||
|
@ -42,3 +42,5 @@ export const RELEVANT_FILE_TYPES = [
|
|||||||
'step',
|
'step',
|
||||||
'stl',
|
'stl',
|
||||||
] as const
|
] as const
|
||||||
|
/** The default name for a tutorial project */
|
||||||
|
export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
|
||||||
|
@ -212,7 +212,7 @@ export function createSettings() {
|
|||||||
data-testid="project-directory-button"
|
data-testid="project-directory-button"
|
||||||
>
|
>
|
||||||
<CustomIcon name="folder" className="w-5 h-5" />
|
<CustomIcon name="folder" className="w-5 h-5" />
|
||||||
<Tooltip position="inlineStart">Choose a folder</Tooltip>
|
<Tooltip position="top-right">Choose a folder</Tooltip>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -19,6 +19,7 @@ import {
|
|||||||
FILE_EXT,
|
FILE_EXT,
|
||||||
INDEX_IDENTIFIER,
|
INDEX_IDENTIFIER,
|
||||||
MAX_PADDING,
|
MAX_PADDING,
|
||||||
|
ONBOARDING_PROJECT_NAME,
|
||||||
PROJECT_ENTRYPOINT,
|
PROJECT_ENTRYPOINT,
|
||||||
PROJECT_FOLDER,
|
PROJECT_FOLDER,
|
||||||
RELEVANT_FILE_TYPES,
|
RELEVANT_FILE_TYPES,
|
||||||
@ -26,6 +27,8 @@ import {
|
|||||||
} from 'lib/constants'
|
} from 'lib/constants'
|
||||||
import { SaveSettingsPayload, SettingsLevel } from './settings/settingsTypes'
|
import { SaveSettingsPayload, SettingsLevel } from './settings/settingsTypes'
|
||||||
import { initPromise, tomlParse } from 'lang/wasm'
|
import { initPromise, tomlParse } from 'lang/wasm'
|
||||||
|
import { bracket } from './exampleKcl'
|
||||||
|
import { paths } from './paths'
|
||||||
|
|
||||||
type PathWithPossibleError = {
|
type PathWithPossibleError = {
|
||||||
path: string | null
|
path: string | null
|
||||||
@ -428,3 +431,20 @@ export async function getSettingsFolderPaths(projectPath?: string) {
|
|||||||
project,
|
project,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createAndOpenNewProject(
|
||||||
|
projectDirectory: string,
|
||||||
|
navigate: (path: string) => void
|
||||||
|
) {
|
||||||
|
const projects = await getProjectsInDir(projectDirectory)
|
||||||
|
const nextIndex = await getNextProjectIndex(ONBOARDING_PROJECT_NAME, projects)
|
||||||
|
const name = interpolateProjectNameWithIndex(
|
||||||
|
ONBOARDING_PROJECT_NAME,
|
||||||
|
nextIndex
|
||||||
|
)
|
||||||
|
const newFile = await createNewProject(
|
||||||
|
await join(projectDirectory, name),
|
||||||
|
bracket
|
||||||
|
)
|
||||||
|
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
||||||
|
}
|
||||||
|
@ -38,6 +38,7 @@ import { isTauri } from 'lib/isTauri'
|
|||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { useLspContext } from 'components/LspProvider'
|
import { useLspContext } from 'components/LspProvider'
|
||||||
import { useRefreshSettings } from 'hooks/useRefreshSettings'
|
import { useRefreshSettings } from 'hooks/useRefreshSettings'
|
||||||
|
import { LowerRightControls } from 'components/LowerRightControls'
|
||||||
|
|
||||||
// This route only opens in the Tauri desktop context for now,
|
// This route only opens in the Tauri desktop context for now,
|
||||||
// as defined in Router.tsx, so we can use the Tauri APIs and types.
|
// as defined in Router.tsx, so we can use the Tauri APIs and types.
|
||||||
@ -288,6 +289,7 @@ const Home = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
<LowerRightControls />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
import {
|
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||||
ONBOARDING_PROJECT_NAME,
|
|
||||||
OnboardingButtons,
|
|
||||||
useDismiss,
|
|
||||||
useNextClick,
|
|
||||||
} from '.'
|
|
||||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { Themes, getSystemTheme } from 'lib/theme'
|
import { Themes, getSystemTheme } from 'lib/theme'
|
||||||
@ -20,7 +15,11 @@ import { paths } from 'lib/paths'
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { codeManager, kclManager } from 'lib/singletons'
|
import { codeManager, kclManager } from 'lib/singletons'
|
||||||
import { join } from '@tauri-apps/api/path'
|
import { join } from '@tauri-apps/api/path'
|
||||||
import { APP_NAME, PROJECT_ENTRYPOINT } from 'lib/constants'
|
import {
|
||||||
|
APP_NAME,
|
||||||
|
ONBOARDING_PROJECT_NAME,
|
||||||
|
PROJECT_ENTRYPOINT,
|
||||||
|
} from 'lib/constants'
|
||||||
|
|
||||||
function OnboardingWithNewFile() {
|
function OnboardingWithNewFile() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
@ -20,7 +20,6 @@ import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
|||||||
import { ActionButton } from 'components/ActionButton'
|
import { ActionButton } from 'components/ActionButton'
|
||||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||||
|
|
||||||
export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
|
|
||||||
export const kbdClasses =
|
export const kbdClasses =
|
||||||
'p-0.5 text-sm rounded-sm bg-chalkboard-10 dark:bg-chalkboard-100 border border-chalkboard-50'
|
'p-0.5 text-sm rounded-sm bg-chalkboard-10 dark:bg-chalkboard-100 border border-chalkboard-50'
|
||||||
|
|
||||||
|
@ -11,16 +11,11 @@ import { paths } from 'lib/paths'
|
|||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
||||||
import {
|
import {
|
||||||
createNewProject,
|
createAndOpenNewProject,
|
||||||
getInitialDefaultDir,
|
getInitialDefaultDir,
|
||||||
getNextProjectIndex,
|
|
||||||
getProjectsInDir,
|
|
||||||
getSettingsFolderPaths,
|
getSettingsFolderPaths,
|
||||||
interpolateProjectNameWithIndex,
|
|
||||||
} from 'lib/tauriFS'
|
} from 'lib/tauriFS'
|
||||||
import { ONBOARDING_PROJECT_NAME } from './Onboarding'
|
import { sep } from '@tauri-apps/api/path'
|
||||||
import { join, sep } from '@tauri-apps/api/path'
|
|
||||||
import { bracket } from 'lib/exampleKcl'
|
|
||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
@ -75,28 +70,10 @@ export const Settings = () => {
|
|||||||
if (isFileSettings) {
|
if (isFileSettings) {
|
||||||
navigate(dotDotSlash(1) + paths.ONBOARDING.INDEX)
|
navigate(dotDotSlash(1) + paths.ONBOARDING.INDEX)
|
||||||
} else {
|
} else {
|
||||||
createAndOpenNewProject()
|
createAndOpenNewProject(context.app.projectDirectory.current, navigate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createAndOpenNewProject() {
|
|
||||||
const defaultDirectory = context.app.projectDirectory.current
|
|
||||||
const projects = await getProjectsInDir(defaultDirectory)
|
|
||||||
const nextIndex = await getNextProjectIndex(
|
|
||||||
ONBOARDING_PROJECT_NAME,
|
|
||||||
projects
|
|
||||||
)
|
|
||||||
const name = interpolateProjectNameWithIndex(
|
|
||||||
ONBOARDING_PROJECT_NAME,
|
|
||||||
nextIndex
|
|
||||||
)
|
|
||||||
const newFile = await createNewProject(
|
|
||||||
await join(defaultDirectory, name),
|
|
||||||
bracket
|
|
||||||
)
|
|
||||||
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition appear show={true} as={Fragment}>
|
<Transition appear show={true} as={Fragment}>
|
||||||
<Dialog
|
<Dialog
|
||||||
|