Compare commits

...

5 Commits

Author SHA1 Message Date
b47ebd14d2 Bump to v0.8.1 (#637)
Signed-off-by: Frank Noirot <frank@kittycad.io>
2023-09-19 15:55:55 -04:00
e74bcd0695 make it so the lsp server doesnt vom on restart (#636)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2023-09-19 15:17:34 -04:00
22161ec386 Variables needs to scroll (#624)
* Variables needs to scroll
Fixes #609

* Run yarn fmt

---------

Co-authored-by: Frank Noirot <frank@kittycad.io>
2023-09-19 18:29:22 +00:00
ada46c4317 Fix Tauri auth in development (#635)
* Fix Tauri auth in development

* Fix Rust formatting
2023-09-19 14:08:26 -04:00
6675fa8d1e UX Papercuts 3: use absolute paths, add error page with buttons to help refresh, etc (#615)
* Fix #593: don't prevent default on link click

* Use absolute/explicit path for settings
Trying to test fix for #594

* Broken: replace almost all relative URLs with absolute

* Clean up to use clean useDismiss with absolute path

* Merge branch 'main' into franknoirot/ux-papercuts-3a

* Add buttons to home, reload, clear, and bug report on error screen
2023-09-19 14:06:56 -04:00
23 changed files with 131 additions and 87 deletions

View File

@ -1,6 +1,6 @@
{
"name": "untitled-app",
"version": "0.8.0",
"version": "0.8.1",
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.9.0",

View File

@ -6,6 +6,7 @@ use std::io::Read;
use anyhow::Result;
use oauth2::TokenResponse;
use tauri::{InvokeError, Manager};
const DEFAULT_HOST: &str = "https://api.kittycad.io";
/// This command returns the a json string parse from a toml file at the path.
#[tauri::command]
@ -88,11 +89,34 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
///This command returns the KittyCAD user info given a token.
/// The string returned from this method is the user info as a json string.
#[tauri::command]
async fn get_user(token: Option<String>) -> Result<kittycad::types::User, InvokeError> {
async fn get_user(
token: Option<String>,
hostname: &str,
) -> Result<kittycad::types::User, InvokeError> {
// Use the host passed in if it's set.
// Otherwise, use the default host.
let host = if hostname.is_empty() {
DEFAULT_HOST.to_string()
} else {
hostname.to_string()
};
// Change the baseURL to the one we want.
let mut baseurl = host.to_string();
if !host.starts_with("http://") && !host.starts_with("https://") {
baseurl = format!("https://{host}");
if host.starts_with("localhost") {
baseurl = format!("http://{host}")
}
}
println!("Getting user info...");
// use kittycad library to fetch the user info from /user/me
let client = kittycad::Client::new(token.unwrap());
let mut client = kittycad::Client::new(token.unwrap());
if baseurl != DEFAULT_HOST {
client.set_base_url(&baseurl);
}
let user_info: kittycad::types::User = client
.users()

View File

@ -8,7 +8,7 @@
},
"package": {
"productName": "kittycad-modeling",
"version": "0.8.0"
"version": "0.8.1"
},
"tauri": {
"allowlist": {

View File

@ -6,9 +6,9 @@ export const Auth = ({ children }: React.PropsWithChildren) => {
const {
auth: { state },
} = useGlobalStateContext()
const isLoggedIn = state.matches('checkIfLoggedIn')
const isLoggingIn = state.matches('checkIfLoggedIn')
return isLoggedIn ? (
return isLoggingIn ? (
<Loading>Loading KittyCAD Modeling App...</Loading>
) : (
<>{children}</>

View File

@ -130,6 +130,7 @@ const router = createBrowserRouter(
path: paths.INDEX,
loader: () =>
isTauri() ? redirect(paths.HOME) : redirect(paths.FILE + '/new'),
errorElement: <ErrorPage />,
},
{
path: paths.FILE + '/:id',
@ -140,7 +141,6 @@ const router = createBrowserRouter(
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
</Auth>
),
errorElement: <ErrorPage />,
id: paths.FILE,
loader: async ({
request,

View File

@ -1,4 +1,12 @@
import { useRouteError } from 'react-router-dom'
import { isTauri } from 'lib/isTauri'
import { useRouteError, isRouteErrorResponse } from 'react-router-dom'
import { ActionButton } from './ActionButton'
import {
faBug,
faHome,
faRefresh,
faTrash,
} from '@fortawesome/free-solid-svg-icons'
export const ErrorPage = () => {
let error = useRouteError()
@ -11,7 +19,43 @@ export const ErrorPage = () => {
<h1 className="text-4xl mb-8 font-bold">
An unexpected error occurred
</h1>
<p>{String(error)}</p>
{isRouteErrorResponse(error) && (
<p className="mb-8">
{error.status}: {error.data}
</p>
)}
<div className="flex justify-between gap-2 mt-6">
{isTauri() && (
<ActionButton Element="link" to={'/'} icon={{ icon: faHome }}>
Go Home
</ActionButton>
)}
<ActionButton
Element="button"
icon={{ icon: faRefresh }}
onClick={() => window.location.reload()}
>
Reload
</ActionButton>
<ActionButton
Element="button"
icon={{ icon: faTrash }}
onClick={() => {
window.localStorage.clear()
}}
>
Clear storage
</ActionButton>
<ActionButton
Element="link"
icon={{ icon: faBug }}
target="_blank"
rel="noopener noreferrer"
to="https://discord.com/channels/915388055236509727/1138967922614743060"
>
Report Bug
</ActionButton>
</div>
</section>
</div>
)

View File

@ -24,7 +24,11 @@ export const MemoryPanel = ({
<CollapsiblePanel {...props}>
<div className="h-full relative">
<div className="absolute inset-0 flex flex-col items-start">
<div className=" h-full console-tile w-full">
<div
className="overflow-y-auto h-full console-tile w-full"
style={{ marginBottom: 36 }}
>
{/* 36px is the height of PanelHeader */}
<ReactJson
src={ProcessedMemory}
collapsed={1}

View File

@ -5,8 +5,6 @@ import init, {
} from '../../wasm-lib/pkg/wasm_lib'
import { FromServer, IntoServer } from './codec'
let server: null | Server
export default class Server {
readonly initOutput: InitOutput
readonly #intoServer: IntoServer
@ -26,12 +24,8 @@ export default class Server {
intoServer: IntoServer,
fromServer: FromServer
): Promise<Server> {
if (null == server) {
const initOutput = await init()
server = new Server(initOutput, intoServer, fromServer)
} else {
console.warn('Server already initialized; ignoring')
}
const initOutput = await init()
const server = new Server(initOutput, intoServer, fromServer)
return server
}

View File

@ -4,6 +4,7 @@ import withBaseURL from '../lib/withBaseURL'
import { CommandBarMeta } from '../lib/commands'
import { isTauri } from 'lib/isTauri'
import { invoke } from '@tauri-apps/api'
import { VITE_KC_API_BASE_URL } from 'env'
const SKIP_AUTH =
import.meta.env.VITE_KC_SKIP_AUTH === 'true' && import.meta.env.DEV
@ -132,6 +133,7 @@ async function getUser(context: UserContext) {
.catch((err) => console.error('error from Browser getUser', err))
: invoke<Models['User_type'] | Record<'error_code', unknown>>('get_user', {
token: context.token,
hostname: VITE_KC_API_BASE_URL,
}).catch((err) => console.error('error from Tauri getUser', err))
const user = await userPromise

View File

@ -9,7 +9,6 @@ import {
cameraMouseDragGuards,
cameraSystems,
} from 'lib/cameraControls'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function Units() {
const { buttonDownInStream } = useStore((s) => ({
@ -25,7 +24,6 @@ export default function Units() {
},
},
} = useGlobalStateContext()
const dotDotSlash = useDotDotSlash()
return (
<div className="fixed grid justify-center items-end inset-0 z-50 pointer-events-none">
@ -74,7 +72,7 @@ export default function Units() {
<div className="flex justify-between">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -2,7 +2,6 @@ import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons'
import { ActionButton } from '../../components/ActionButton'
import { onboardingPaths, useDismiss, useNextClick } from '.'
import { useStore } from '../../useStore'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function CmdK() {
const { buttonDownInStream } = useStore((s) => ({
@ -10,7 +9,6 @@ export default function CmdK() {
}))
const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.USER_MENU)
const dotDotSlash = useDotDotSlash()
return (
<div className="fixed grid justify-center items-end inset-0 z-50 pointer-events-none">
@ -43,7 +41,7 @@ export default function CmdK() {
<div className="flex justify-between">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -3,7 +3,6 @@ import { ActionButton } from '../../components/ActionButton'
import { onboardingPaths, useDismiss, useNextClick } from '.'
import { useStore } from '../../useStore'
import { useBackdropHighlight } from 'hooks/useBackdropHighlight'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function CodeEditor() {
const { buttonDownInStream } = useStore((s) => ({
@ -11,7 +10,6 @@ export default function CodeEditor() {
}))
const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.PARAMETRIC_MODELING)
const dotDotSlash = useDotDotSlash()
return (
<div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none">
@ -62,7 +60,7 @@ export default function CodeEditor() {
<div className="flex justify-between">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -2,7 +2,6 @@ import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons'
import { ActionButton } from '../../components/ActionButton'
import { onboardingPaths, useDismiss, useNextClick } from '.'
import { useStore } from '../../useStore'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function Export() {
const { buttonDownInStream } = useStore((s) => ({
@ -10,7 +9,6 @@ export default function Export() {
}))
const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.SKETCHING)
const dotDotSlash = useDotDotSlash()
return (
<div className="fixed grid justify-center items-end inset-0 z-50 pointer-events-none">
@ -42,7 +40,7 @@ export default function Export() {
<div className="flex justify-between">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -4,14 +4,12 @@ import { useDismiss } from '.'
import { useEffect } from 'react'
import { useStore } from 'useStore'
import { bracket } from 'lib/exampleKcl'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function FutureWork() {
const dismiss = useDismiss()
const { deferredSetCode } = useStore((s) => ({
deferredSetCode: s.deferredSetCode,
}))
const dotDotSlash = useDotDotSlash()
useEffect(() => {
deferredSetCode(bracket)
@ -36,7 +34,7 @@ export default function FutureWork() {
<div className="flex justify-between mt-6">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',
@ -49,7 +47,7 @@ export default function FutureWork() {
</ActionButton>
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{ icon: faArrowRight }}
>
Finish

View File

@ -3,7 +3,6 @@ import { ActionButton } from '../../components/ActionButton'
import { onboardingPaths, useDismiss, useNextClick } from '.'
import { useStore } from '../../useStore'
import { useBackdropHighlight } from 'hooks/useBackdropHighlight'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function InteractiveNumbers() {
const { buttonDownInStream } = useStore((s) => ({
@ -11,7 +10,6 @@ export default function InteractiveNumbers() {
}))
const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.COMMAND_K)
const dotDotSlash = useDotDotSlash()
return (
<div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none">
@ -102,7 +100,7 @@ export default function InteractiveNumbers() {
<div className="flex justify-between">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -15,15 +15,13 @@ import { isTauri } from 'lib/isTauri'
import { useNavigate } from 'react-router-dom'
import { paths } from 'Router'
import { useEffect } from 'react'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
function OnboardingWithNewFile() {
const navigate = useNavigate()
const dotDotSlash = useDotDotSlash()
const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.INDEX)
const { setCode } = useStore((s) => ({
setCode: s.setCode,
const { deferredSetCode } = useStore((s) => ({
deferredSetCode: s.deferredSetCode,
}))
const {
settings: {
@ -53,7 +51,7 @@ function OnboardingWithNewFile() {
<div className="flex justify-between mt-6">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash())}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',
@ -67,7 +65,7 @@ function OnboardingWithNewFile() {
<ActionButton
Element="button"
onClick={() => {
setCode(bracket)
deferredSetCode(bracket)
next()
}}
icon={{ icon: faArrowRight }}
@ -91,7 +89,7 @@ function OnboardingWithNewFile() {
<div className="flex justify-between mt-6">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash())}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',
@ -118,9 +116,9 @@ function OnboardingWithNewFile() {
}
export default function Introduction() {
const { setCode, code } = useStore((s) => ({
const { deferredSetCode, code } = useStore((s) => ({
code: s.code,
setCode: s.setCode,
deferredSetCode: s.deferredSetCode,
}))
const {
settings: {
@ -136,11 +134,10 @@ export default function Introduction() {
: ''
const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.CAMERA)
const dotDotSlash = useDotDotSlash()
useEffect(() => {
if (code === '') setCode(bracket)
}, [code, setCode])
if (code === '') deferredSetCode(bracket)
}, [code, deferredSetCode])
return !(code !== '' && code !== bracket) ? (
<div className="fixed grid place-content-center inset-0 bg-chalkboard-110/50 z-50">
@ -180,7 +177,7 @@ export default function Introduction() {
<div className="flex justify-between mt-6">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash())}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -5,7 +5,6 @@ import { useStore } from '../../useStore'
import { useBackdropHighlight } from 'hooks/useBackdropHighlight'
import { Themes, getSystemTheme } from 'lib/theme'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function ParametricModeling() {
const { buttonDownInStream } = useStore((s) => ({
@ -23,7 +22,6 @@ export default function ParametricModeling() {
: ''
const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.INTERACTIVE_NUMBERS)
const dotDotSlash = useDotDotSlash()
return (
<div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none">
@ -62,7 +60,7 @@ export default function ParametricModeling() {
<div className="flex justify-between">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -3,7 +3,6 @@ import { ActionButton } from '../../components/ActionButton'
import { onboardingPaths, useDismiss, useNextClick } from '.'
import { useStore } from '../../useStore'
import { isTauri } from 'lib/isTauri'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function ProjectMenu() {
const { buttonDownInStream } = useStore((s) => ({
@ -11,7 +10,6 @@ export default function ProjectMenu() {
}))
const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.EXPORT)
const dotDotSlash = useDotDotSlash()
return (
<div className="fixed grid justify-center items-start inset-0 z-50 pointer-events-none">
@ -33,7 +31,7 @@ export default function ProjectMenu() {
<div className="flex justify-between">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -3,7 +3,6 @@ import { ActionButton } from '../../components/ActionButton'
import { onboardingPaths, useDismiss, useNextClick } from '.'
import { useStore } from 'useStore'
import { useEffect } from 'react'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function Sketching() {
const { deferredSetCode, buttonDownInStream } = useStore((s) => ({
@ -16,7 +15,6 @@ export default function Sketching() {
useEffect(() => {
deferredSetCode('')
}, [deferredSetCode])
const dotDotSlash = useDotDotSlash()
return (
<div className="fixed grid justify-center items-end inset-0 z-50 pointer-events-none">
@ -40,7 +38,7 @@ export default function Sketching() {
<div className="flex justify-between mt-6">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -2,7 +2,6 @@ import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons'
import { ActionButton } from '../../components/ActionButton'
import { onboardingPaths, useDismiss, useNextClick } from '.'
import { useStore } from '../../useStore'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function Streaming() {
const { buttonDownInStream } = useStore((s) => ({
@ -10,7 +9,6 @@ export default function Streaming() {
}))
const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.EDITOR)
const dotDotSlash = useDotDotSlash()
return (
<div className="fixed grid justify-start items-center inset-0 z-50 pointer-events-none">
@ -43,7 +41,7 @@ export default function Streaming() {
<div className="flex justify-between">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -6,7 +6,6 @@ import { Toggle } from '../../components/Toggle/Toggle'
import { onboardingPaths, useDismiss, useNextClick } from '.'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { UnitSystem } from 'machines/settingsMachine'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function Units() {
const dismiss = useDismiss()
@ -17,7 +16,6 @@ export default function Units() {
context: { unitSystem, baseUnit },
},
} = useGlobalStateContext()
const dotDotSlash = useDotDotSlash()
return (
<div className="fixed grid place-content-center inset-0 bg-chalkboard-110/50 z-50">
@ -68,7 +66,7 @@ export default function Units() {
<div className="flex justify-between mt-6">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -2,7 +2,6 @@ import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons'
import { ActionButton } from '../../components/ActionButton'
import { onboardingPaths, useDismiss, useNextClick } from '.'
import { useStore } from '../../useStore'
import { useDotDotSlash } from 'hooks/useDotDotSlash'
export default function UserMenu() {
const { buttonDownInStream } = useStore((s) => ({
@ -10,7 +9,6 @@ export default function UserMenu() {
}))
const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.PROJECT_MENU)
const dotDotSlash = useDotDotSlash()
return (
<div className="fixed grid justify-center items-start inset-0 z-50 pointer-events-none">
@ -30,7 +28,7 @@ export default function UserMenu() {
<div className="flex justify-between">
<ActionButton
Element="button"
onClick={() => dismiss(dotDotSlash(2))}
onClick={dismiss}
icon={{
icon: faXmark,
bgClassName: 'bg-destroy-80',

View File

@ -1,5 +1,5 @@
import { useHotkeys } from 'react-hotkeys-hook'
import { Outlet, useLocation, useNavigate } from 'react-router-dom'
import { Outlet, useRouteLoaderData, useNavigate } from 'react-router-dom'
import Introduction from './Introduction'
import Camera from './Camera'
import Sketching from './Sketching'
@ -15,6 +15,7 @@ import UserMenu from './UserMenu'
import ProjectMenu from './ProjectMenu'
import Export from './Export'
import FutureWork from './FutureWork'
import { IndexLoaderData, paths } from 'Router'
export const onboardingPaths = {
INDEX: '/',
@ -89,42 +90,44 @@ export function useNextClick(newStatus: string) {
settings: { send },
} = useGlobalStateContext()
const navigate = useNavigate()
const location = useLocation()
const lastSlashIndex = location.pathname.lastIndexOf('/')
const { project } = useRouteLoaderData(paths.FILE) as IndexLoaderData
return useCallback(() => {
send({
type: 'Set Onboarding Status',
data: { onboardingStatus: newStatus },
})
navigate(location.pathname.slice(0, lastSlashIndex) + newStatus)
}, [location, lastSlashIndex, newStatus, send, navigate])
navigate(
paths.FILE +
'/' +
encodeURIComponent(project?.path || 'new') +
paths.ONBOARDING.INDEX.slice(0, -1) +
newStatus
)
}, [project, newStatus, send, navigate])
}
export function useDismiss() {
const routeData = useRouteLoaderData(paths.FILE) as IndexLoaderData
const {
settings: { send },
} = useGlobalStateContext()
const navigate = useNavigate()
return useCallback(
(path: string) => {
send({
type: 'Set Onboarding Status',
data: { onboardingStatus: 'dismissed' },
})
console.log('yoyo', window.location.pathname, path)
navigate(path)
},
[send, navigate]
)
return useCallback(() => {
send({
type: 'Set Onboarding Status',
data: { onboardingStatus: 'dismissed' },
})
navigate(
paths.FILE + '/' + encodeURIComponent(routeData?.project?.path || 'new')
)
}, [send, navigate, routeData])
}
const Onboarding = () => {
const location = useLocation()
const dismiss = useDismiss()
const lastSlashIndex = location.pathname.lastIndexOf('/')
useHotkeys('esc', () => dismiss(location.pathname.slice(0, lastSlashIndex)))
useHotkeys('esc', dismiss)
return (
<>