diff --git a/src/App.tsx b/src/App.tsx
index 73ffe4f26..7496f32eb 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -26,6 +26,7 @@ import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher'
export function App() {
const { project, file } = useLoaderData() as IndexLoaderData
+ // Keep a lookout for a URL query string that invokes the 'import file from URL' command
useCreateFileLinkQuery()
useRefreshSettings(PATHS.FILE + 'SETTINGS')
const navigate = useNavigate()
diff --git a/src/components/CommandComboBox.tsx b/src/components/CommandComboBox.tsx
index d647c9af5..a4f1566d2 100644
--- a/src/components/CommandComboBox.tsx
+++ b/src/components/CommandComboBox.tsx
@@ -4,6 +4,7 @@ import { useCommandsContext } from 'hooks/useCommandsContext'
import { Command } from 'lib/commandTypes'
import { useEffect, useState } from 'react'
import { CustomIcon } from './CustomIcon'
+import { getActorNextEvents } from 'lib/utils'
function CommandComboBox({
options,
@@ -73,7 +74,8 @@ function CommandComboBox({
{'icon' in option && option.icon && (
@@ -96,3 +98,11 @@ function CommandComboBox({
}
export default CommandComboBox
+
+function optionIsDisabled(option: Command): boolean {
+ return (
+ 'machineActor' in option &&
+ option.machineActor !== undefined &&
+ !getActorNextEvents(option.machineActor.getSnapshot()).includes(option.name)
+ )
+}
diff --git a/src/components/ProjectSidebarMenu.tsx b/src/components/ProjectSidebarMenu.tsx
index 5735743a5..74ee48577 100644
--- a/src/components/ProjectSidebarMenu.tsx
+++ b/src/components/ProjectSidebarMenu.tsx
@@ -186,7 +186,6 @@ function ProjectMenuPopover({
{
id: 'share-link',
Element: 'button',
- className: !isDesktop() ? 'hidden' : '',
children: 'Share link to file',
onClick: async () => {
const shareUrl = createFileLink({
diff --git a/src/components/ProjectsContextProvider.tsx b/src/components/ProjectsContextProvider.tsx
index 4615d222b..b6df1776f 100644
--- a/src/components/ProjectsContextProvider.tsx
+++ b/src/components/ProjectsContextProvider.tsx
@@ -3,11 +3,11 @@ import { useCommandsContext } from 'hooks/useCommandsContext'
import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher'
import { useProjectsLoader } from 'hooks/useProjectsLoader'
import { projectsMachine } from 'machines/projectsMachine'
-import { createContext, useEffect, useState } from 'react'
+import { createContext, useCallback, useEffect, useState } from 'react'
import { Actor, AnyStateMachine, fromPromise, Prop, StateFrom } from 'xstate'
import { useLspContext } from './LspProvider'
import toast from 'react-hot-toast'
-import { useLocation, useNavigate } from 'react-router-dom'
+import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
import { PATHS } from 'lib/paths'
import {
createNewProjectDirectory,
@@ -18,11 +18,27 @@ import {
getNextProjectIndex,
interpolateProjectNameWithIndex,
doesProjectNameNeedInterpolated,
+ getNextFileName,
} from 'lib/desktopFS'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import useStateMachineCommands from 'hooks/useStateMachineCommands'
import { projectsCommandBarConfig } from 'lib/commandBarConfigs/projectsCommandConfig'
import { isDesktop } from 'lib/isDesktop'
+import {
+ CREATE_FILE_URL_PARAM,
+ FILE_EXT,
+ PROJECT_ENTRYPOINT,
+} from 'lib/constants'
+import { DeepPartial } from 'lib/types'
+import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
+import { codeManager } from 'lib/singletons'
+import {
+ loadAndValidateSettings,
+ projectConfigurationToSettingsPayload,
+ saveSettings,
+ setSettingsAtLevel,
+} from 'lib/settings/settingsUtils'
+import { Project } from 'lib/project'
type MachineContext = {
state?: StateFrom
@@ -44,47 +60,25 @@ export const ProjectsContextProvider = ({
children,
}: {
children: React.ReactNode
-}) => {
- return isDesktop() ? (
- {children}
- ) : (
- {children}
- )
-}
-
-const ProjectsContextWeb = ({ children }: { children: React.ReactNode }) => {
- return (
- {},
- }}
- >
- {children}
-
- )
-}
-
-const ProjectsContextDesktop = ({
- children,
-}: {
- children: React.ReactNode
}) => {
const navigate = useNavigate()
const location = useLocation()
+ const [searchParams, setSearchParams] = useSearchParams()
+ const clearImportSearchParams = useCallback(() => {
+ // Clear the search parameters related to the "Import file from URL" command
+ // or we'll never be able cancel or submit it.
+ searchParams.delete(CREATE_FILE_URL_PARAM)
+ searchParams.delete('code')
+ searchParams.delete('name')
+ searchParams.delete('units')
+ setSearchParams(searchParams)
+ }, [searchParams, setSearchParams])
const { commandBarSend } = useCommandsContext()
const { onProjectOpen } = useLspContext()
const {
- settings: { context: settings },
+ settings: { context: settings, send: settingsSend },
} = useSettingsAuthContext()
- useEffect(() => {
- console.log(
- 'project directory changed',
- settings.app.projectDirectory.current
- )
- }, [settings.app.projectDirectory.current])
-
const [projectsLoaderTrigger, setProjectsLoaderTrigger] = useState(0)
const { projectPaths, projectsDir } = useProjectsLoader([
projectsLoaderTrigger,
@@ -163,6 +157,31 @@ const ProjectsContextDesktop = ({
}
}
},
+ navigateToFile: ({ context, event }) => {
+ if (event.type !== 'xstate.done.actor.create-file') return
+ // For now, the browser version of create-file doesn't need to navigate
+ // since it just overwrites the current file.
+ if (!isDesktop()) return
+ let projectPath = window.electron.join(
+ context.defaultDirectory,
+ event.output.projectName
+ )
+ let filePath = window.electron.join(
+ projectPath,
+ event.output.fileName
+ )
+ onProjectOpen(
+ {
+ name: event.output.projectName,
+ path: projectPath,
+ },
+ null
+ )
+ const pathToNavigateTo = `${PATHS.FILE}/${encodeURIComponent(
+ filePath
+ )}`
+ navigate(pathToNavigateTo)
+ },
toastSuccess: ({ event }) =>
toast.success(
('data' in event && typeof event.data === 'string' && event.data) ||
@@ -182,7 +201,10 @@ const ProjectsContextDesktop = ({
),
},
actors: {
- readProjects: fromPromise(() => listProjects()),
+ readProjects: fromPromise(async () => {
+ if (!isDesktop()) return [] as Project[]
+ return listProjects()
+ }),
createProject: fromPromise(async ({ input }) => {
let name = (
input && 'name' in input && input.name
@@ -238,6 +260,101 @@ const ProjectsContextDesktop = ({
name: input.name,
}
}),
+ createFile: fromPromise(async ({ input }) => {
+ let projectName =
+ (input.method === 'newProject' ? input.name : input.projectName) ||
+ settings.projects.defaultProjectName.current
+ let fileName =
+ input.method === 'newProject'
+ ? PROJECT_ENTRYPOINT
+ : input.name.endsWith(FILE_EXT)
+ ? input.name
+ : input.name + FILE_EXT
+ let message = 'File created successfully'
+ const unitsConfiguration: DeepPartial = {
+ settings: {
+ project: {
+ directory: settings.app.projectDirectory.current,
+ },
+ modeling: {
+ base_unit: input.units,
+ },
+ },
+ }
+
+ if (isDesktop()) {
+ const needsInterpolated =
+ doesProjectNameNeedInterpolated(projectName)
+ console.log(
+ `The project name "${projectName}" needs interpolated: ${needsInterpolated}`
+ )
+ if (needsInterpolated) {
+ const nextIndex = getNextProjectIndex(projectName, input.projects)
+ projectName = interpolateProjectNameWithIndex(
+ projectName,
+ nextIndex
+ )
+ }
+
+ // Create the project around the file if newProject
+ if (input.method === 'newProject') {
+ await createNewProjectDirectory(
+ projectName,
+ input.code,
+ unitsConfiguration
+ )
+ message = `Project "${projectName}" created successfully with link contents`
+ } else {
+ let projectPath = window.electron.join(
+ settings.app.projectDirectory.current,
+ projectName
+ )
+
+ message = `File "${fileName}" created successfully`
+ const existingConfiguration = await loadAndValidateSettings(
+ projectPath
+ )
+ const settingsToSave = setSettingsAtLevel(
+ existingConfiguration.settings,
+ 'project',
+ projectConfigurationToSettingsPayload(unitsConfiguration)
+ )
+ await saveSettings(settingsToSave, projectPath)
+ }
+
+ // Create the file
+ let baseDir = window.electron.join(
+ settings.app.projectDirectory.current,
+ projectName
+ )
+ const { name, path } = getNextFileName({
+ entryName: fileName,
+ baseDir,
+ })
+ fileName = name
+
+ await window.electron.writeFile(path, input.code || '')
+ } else {
+ // Browser version doesn't navigate, just overwrites the current file
+ clearImportSearchParams()
+ codeManager.updateCodeStateEditor(input.code || '')
+ await codeManager.writeToFile()
+ message = 'File successfully overwritten with link contents'
+ settingsSend({
+ type: 'set.modeling.defaultUnit',
+ data: {
+ level: 'project',
+ value: input.units,
+ },
+ })
+ }
+
+ return {
+ message,
+ fileName,
+ projectName,
+ }
+ }),
},
guards: {
'Has at least 1 project': ({ event }) => {
@@ -267,6 +384,7 @@ const ProjectsContextDesktop = ({
state,
commandBarConfig: projectsCommandBarConfig,
actor,
+ onCancel: clearImportSearchParams,
})
return (
diff --git a/src/hooks/useCreateFileLinkQueryWatcher.ts b/src/hooks/useCreateFileLinkQueryWatcher.ts
index 3e1ceb94a..24915a411 100644
--- a/src/hooks/useCreateFileLinkQueryWatcher.ts
+++ b/src/hooks/useCreateFileLinkQueryWatcher.ts
@@ -1,7 +1,7 @@
import { base64ToString } from 'lib/base64'
import { CREATE_FILE_URL_PARAM, DEFAULT_FILE_NAME } from 'lib/constants'
import { useEffect } from 'react'
-import { useLocation } from 'react-router-dom'
+import { useSearchParams } from 'react-router-dom'
import { useCommandsContext } from './useCommandsContext'
import { useSettingsAuthContext } from './useSettingsAuthContext'
import { isDesktop } from 'lib/isDesktop'
@@ -16,28 +16,28 @@ import { baseUnitsUnion } from 'lib/settings/settingsTypes'
* URL parameters.
*/
export function useCreateFileLinkQuery() {
- const location = useLocation()
+ const [searchParams] = useSearchParams()
const { commandBarSend } = useCommandsContext()
const { settings } = useSettingsAuthContext()
useEffect(() => {
- const urlParams = new URLSearchParams(location.search)
- const createFileParam = urlParams.has(CREATE_FILE_URL_PARAM)
+ const createFileParam = searchParams.has(CREATE_FILE_URL_PARAM)
console.log('checking for createFileParam', {
createFileParam,
- urlParams: [...urlParams.entries()],
- location,
+ searchParams: [...searchParams.entries()],
})
if (createFileParam) {
const params: FileLinkParams = {
- code: base64ToString(decodeURIComponent(urlParams.get('code') ?? '')),
+ code: base64ToString(
+ decodeURIComponent(searchParams.get('code') ?? '')
+ ),
- name: urlParams.get('name') ?? DEFAULT_FILE_NAME,
+ name: searchParams.get('name') ?? DEFAULT_FILE_NAME,
units:
- (baseUnitsUnion.find((unit) => urlParams.get('units') === unit) ||
+ (baseUnitsUnion.find((unit) => searchParams.get('units') === unit) ||
settings.context.modeling.defaultUnit.default) ??
settings.context.modeling.defaultUnit.current,
}
@@ -74,5 +74,5 @@ export function useCreateFileLinkQuery() {
},
})
}
- }, [location.search])
+ }, [searchParams])
}
diff --git a/src/hooks/useProjectsLoader.tsx b/src/hooks/useProjectsLoader.tsx
index 04e55c917..aff8edaa8 100644
--- a/src/hooks/useProjectsLoader.tsx
+++ b/src/hooks/useProjectsLoader.tsx
@@ -14,7 +14,7 @@ export const useProjectsLoader = (deps?: [number]) => {
useEffect(() => {
// Useless on web, until we get fake filesystems over there.
- if (!isDesktop) return
+ if (!isDesktop()) return
if (deps && deps[0] === lastTs) return
diff --git a/src/lib/commandBarConfigs/projectsCommandConfig.ts b/src/lib/commandBarConfigs/projectsCommandConfig.ts
index cc639e003..2af32111b 100644
--- a/src/lib/commandBarConfigs/projectsCommandConfig.ts
+++ b/src/lib/commandBarConfigs/projectsCommandConfig.ts
@@ -1,6 +1,7 @@
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning'
import { StateMachineCommandSetConfig } from 'lib/commandTypes'
+import { isDesktop } from 'lib/isDesktop'
import { baseUnitLabels, baseUnitsUnion } from 'lib/settings/settingsTypes'
import { projectsMachine } from 'machines/projectsMachine'
@@ -112,16 +113,27 @@ export const projectsCommandBarConfig: StateMachineCommandSetConfig<
inputType: 'options',
required: true,
skip: true,
- options: [
- { name: 'New Project', value: 'newProject' },
- { name: 'Existing Project', value: 'existingProject' },
- ],
+ options: isDesktop()
+ ? [
+ { name: 'New project', value: 'newProject' },
+ { name: 'Existing project', value: 'existingProject' },
+ ]
+ : [{ name: 'Overwrite', value: 'existingProject' }],
+ valueSummary(value) {
+ return isDesktop()
+ ? value === 'newProject'
+ ? 'New project'
+ : 'Existing project'
+ : 'Overwrite'
+ },
},
// TODO: We can't get the currently-opened project to auto-populate here because
// it's not available on projectMachine, but lower in fileMachine. Unify these.
projectName: {
inputType: 'options',
- required: (commandsContext) => commandsContext.argumentsToSubmit.method === 'existingProject',
+ required: (commandsContext) =>
+ isDesktop() &&
+ commandsContext.argumentsToSubmit.method === 'existingProject',
skip: true,
options: [],
optionsFromContext: (context) =>
@@ -132,13 +144,16 @@ export const projectsCommandBarConfig: StateMachineCommandSetConfig<
},
name: {
inputType: 'string',
- required: true,
+ required: isDesktop(),
skip: true,
},
code: {
inputType: 'text',
- required: false,
+ required: true,
skip: true,
+ valueSummary(value) {
+ return value?.trim().split('\n').length + ' lines'
+ },
},
units: {
inputType: 'options',
@@ -151,7 +166,17 @@ export const projectsCommandBarConfig: StateMachineCommandSetConfig<
},
},
reviewMessage(commandBarContext) {
- return `Will add the contents from URL to a new ${commandBarContext.argumentsToSubmit.method === 'newProject' ? 'project with file main.kcl' : `file within the project "${commandBarContext.argumentsToSubmit.projectName}"`} named "${commandBarContext.argumentsToSubmit.name}", and set default units to "${commandBarContext.argumentsToSubmit.units}".`
+ return isDesktop()
+ ? `Will add the contents from URL to a new ${
+ commandBarContext.argumentsToSubmit.method === 'newProject'
+ ? 'project with file main.kcl'
+ : `file within the project "${commandBarContext.argumentsToSubmit.projectName}"`
+ } named "${
+ commandBarContext.argumentsToSubmit.name
+ }", and set default units to "${
+ commandBarContext.argumentsToSubmit.units
+ }".`
+ : `Will overwrite the contents of the current file with the contents from the URL.`
},
- }
+ },
}
diff --git a/src/lib/createFileLink.ts b/src/lib/createFileLink.ts
index 85d2adb75..2dc5b545e 100644
--- a/src/lib/createFileLink.ts
+++ b/src/lib/createFileLink.ts
@@ -1,26 +1,23 @@
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
-import { CREATE_FILE_URL_PARAM, PROD_APP_URL } from './constants'
+import { CREATE_FILE_URL_PARAM } from './constants'
import { stringToBase64 } from './base64'
export interface FileLinkParams {
- code: string
- name: string
- units: UnitLength_type
+ code: string
+ name: string
+ units: UnitLength_type
}
/**
* Given a file's code, name, and units, creates shareable link
* TODO: make the app respect this link
*/
-export function createFileLink({
- code,
- name,
- units,
-}: FileLinkParams) {
+export function createFileLink({ code, name, units }: FileLinkParams) {
+ const origin = globalThis.window.location.origin
return new URL(
`/?${CREATE_FILE_URL_PARAM}&name=${encodeURIComponent(
name
)}&units=${units}&code=${encodeURIComponent(stringToBase64(code))}`,
- PROD_APP_URL
+ origin
).href
}
diff --git a/src/lib/routeLoaders.ts b/src/lib/routeLoaders.ts
index 894cd6d71..7bc4a0fde 100644
--- a/src/lib/routeLoaders.ts
+++ b/src/lib/routeLoaders.ts
@@ -104,7 +104,7 @@ export const fileLoader: LoaderFunction = async (
return redirect(
`${PATHS.FILE}/${encodeURIComponent(
isDesktop() ? fallbackFile : params.id + '/' + PROJECT_ENTRYPOINT
- )}`
+ )}${new URL(routerData.request.url).search || ''}`
)
}
diff --git a/src/machines/projectsMachine.ts b/src/machines/projectsMachine.ts
index e2d6827df..126f5244d 100644
--- a/src/machines/projectsMachine.ts
+++ b/src/machines/projectsMachine.ts
@@ -25,7 +25,10 @@ export const projectsMachine = setup({
type: 'Delete project'
data: ProjectsCommandSchema['Delete project']
}
- | { type: 'Import file from URL'; data: ProjectsCommandSchema['Import file from URL'] }
+ | {
+ type: 'Import file from URL'
+ data: ProjectsCommandSchema['Import file from URL']
+ }
| { type: 'navigate'; data: { name: string } }
| {
type: 'xstate.done.actor.read-projects'
@@ -43,6 +46,10 @@ export const projectsMachine = setup({
type: 'xstate.done.actor.rename-project'
output: { message: string; oldName: string; newName: string }
}
+ | {
+ type: 'xstate.done.actor.create-file'
+ output: { message: string; projectName: string; fileName: string }
+ }
| { type: 'assign'; data: { [key: string]: any } },
input: {} as {
projects: Project[]
@@ -61,6 +68,7 @@ export const projectsMachine = setup({
toastError: () => {},
navigateToProject: () => {},
navigateToProjectIfNeeded: () => {},
+ navigateToFile: () => {},
},
actors: {
readProjects: fromPromise(() => Promise.resolve([] as Project[])),
@@ -91,13 +99,20 @@ export const projectsMachine = setup({
name: '',
})
),
+ createFile: fromPromise(
+ (_: {
+ input: ProjectsCommandSchema['Import file from URL'] & {
+ projects: Project[]
+ }
+ }) => Promise.resolve({ message: '', projectName: '', fileName: '' })
+ ),
},
guards: {
'Has at least 1 project': () => false,
'New project method is used': () => false,
},
}).createMachine({
- /** @xstate-layout N4IgpgJg5mDOIC5QAkD2BbMACdBDAxgBYCWAdmAMS6yzFSkDaADALqKgAOqtALsaqXYgAHogAsAJgA0IAJ6IAjBIkA2AHQAOCUw0qNkjQE4xYjQF8zMtJhwES5NcmpZSqLBwBOqAFZh8PWAoAJTBcCHcvX39YZjYkEC5efkF40QQFFQBmAHY1Qwz9QwBWU0NMrRl5BGyxBTUVJlVM01rtBXNLEGtsPCIyMEdnVwifPwCKAGEPUJ5sT1H-WKFE4j4BITSMzMzNIsyJDSZMhSYmFWzKxAkTNSYxIuU9zOKT7IsrDB67fsHYEajxiEwv8xjFWMtuKtkhsrpJboZsioincFMdTIjLggVGJ1GImMVMg0JISjEV3l1PrY+g4nH95gDAiFSLgbPSxkt4is1ilQGlrhJ4YjkbU0RoMXJEIYkWoikV8hJinjdOdyd0qfYBrSQdFJtNcLNtTwOZxIdyYQh+YKkSjReKqqYBQoEQpsgiSi9MqrKb0Nb9DYEACJgAA2YANbMW4M5puhqVhAvxQptCnRKkxCleuw0Gm2+2aRVRXpsPp+Woj4wA8hwwKRDcaEjH1nGLXDE9aRSmxWmJVipWocmdsbL8kxC501SWHFMZmQoIaKBABAMyAA3VAAawG+D1swAtOX61zY7zENlEWoMkoatcdPoipjCWIZRIirozsPiYYi19qQNp-rZ3nMAPC8Dw1A4YN9QAM1QDx0DUbcZjAfdInZKMTSSJsT2qc9Lwka8lTvTFTB2WUMiyQwc3yPRv3VH4mRZQDywXJc1FXDcBmmZlMBQhYjXQhtMJ5ERT1wlQr0kQj7kxKiZTxM5s2zbQEVoycBgY9AmNQ-wKGA0DwMgngYLgtQuJZZCDwEo8sJEnD1Dwgjb2kntig0GUkWVfZDEMfFVO+Bwg1DPhSDnZjFwcdjNzUCAQzDCztP4uIMKhGy0jPezxPwySnPvHsTjlPIhyOHRsnwlM-N-NRArDLS+N0kDYIM6DYPgmKgvivjD0bYS+VbBF21RTs7UUUcimfRpXyYRF8LFCrfSBCBaoZFiItINcor1CBeIZLqhPNdKL0yxzs2cqoNDqdpSo0EoSoLOb6NCRaQv9FblzWjjTMe7bQQYBQksElKesUMRjFubRMllCHsm2a5MVldRDBfFpmj0IpxPuhwFqW0F6v0iDmpMzbvuiXbAfNFNQcaI5IaKaH9jETFsUMNRVDxOU5TxBUMcof8DSg4hQ1Js1mwyfCL2JYlakkA4GZcp16jEaGzylfMsm53UkKwfnBb+iE9pFlQDjUM8HiYV99At2WqhOO5+zPLQqbRyiyXJVwYvgeIJ38sA9bJ5td27KpdzG7zQ70VFslOBVim5v1hnLD3kuF7D2hOW5smRCQM2uhQHkZ6VtkRBFnmu0qFFjssEsTgHk9syR03xAUlWOVQ9gzyjY957H-F92u0hMJ8ryc86iUyYiCvxFNzldVvjFjjTu54XvjzrnIWZUfI0eRM2VCyK3JThe5mhdWnS5dj5i29qrYuC0KEuX1LxDxepnlOfYDghp0G+yOpngzN+Yajnno9Re1drJA3SC6HY4lvL6AVMiJEuUqgbzckfQ4+gcTnUJBYCwQA */
+ /** @xstate-layout N4IgpgJg5mDOIC5QAkD2BbMACdBDAxgBYCWAdmAMS6yzFSkDaADALqKgAOqtALsaqXYgAHogAsAJgA0IAJ6IAjAHYAbADoArBJVMFTCQA4mTAMwmxAXwsy0mHARLkKASXRcATjywAzYgBtsb3cMLABVACUAGWY2JBAuXn5BONEESRl5BAUFFQM1HQUxJSYATmyFAyUTKxsMbDwiMjA1ZGosUlQsDmCAKzB8HlgKcLBcCC7e-sGYoQTiPgEhVIUy9SKSiQ0NEwkclRyMxGKJNRKlMRVDU23zpRqQW3qHJpa2jonUPoGhgGF3UZ42G6nymMzicwWyVAyzKJzECg0OiYGjERhK+kOWUMSlOGiUlTEJlMxRKBnuj3sjXIr1gHy+g2Go3GwPpsDBnG48ySS0QGj0+Q0JTOGkqBg2JhKmNRJy2OyUEn0Kml5LqlMczVatJZUyGI1IuDs2oG7PinMhPIQfKYAqFShF+PFkrkRwkYjU8OUShKKhMKg0pUs1geqoa6ppdJ1FD+AKBk2NrFmZu5KV5-L9tvtYokEsxelRagUCoMiMumyUdpVdlDL01Ee+FAAImAAoC6zwTRDk9DU9b08LRY7MWd1AZcoS-aoLiYFJWnlSNW0jQyAPIcMCkNsdpOLFOWtOC-sO7NOzLbGWFbY6acmFESWdql7R3B8UhQNsUCACZpkABuqAA1s0+D-M+YAALRLluiQ7t2WRMGIbryjeBgGBIEglNsCi5sY6hMOchQqEoCLFCh97VtST4vm+S4UGA7jBO4agcH4z7eKg7joGowExhBcbtgm4LblCIiKPBiHZiKqHoZhuaFhoBbGMYBhiPBCgStUQYUuRzR6gaZDUXxH5fmov4Ac0-z6pgvEgvGsQctBwnLGJahIZJaEYdOmKEfJuQSoRRKSRcZHPNSunoPp750QxTEsTwbEcWoFkGuBkECfZXIwSJcEIS5Ekoe5MnOggXqIaUqFiiUhIInemkhiFzRNi2EU0Z+1KmYBagQM2YCAtZ9JQRljmiTlrn5dJnlFShOLwcpZjKBs+KBrUVb1WojU9c1hlRexMWsexnFdS2KV8QN5q7laNqHlmOaTcpmjoWc8L6HoYrBfOagjGMm02QyrXfqQf4dSBEB9Tqp1dllebichUkeVhRXTiU+QecUKhCps4pvWGn0QN9rJGW1ANmYlTKg98DAKHZpoORanoyqh8oImU3pKJifJ5Ohdr7CYqjIvKWMvDjeORttjHMXtCXA2T0xpdTg20+W9MSIzgorIRXlpr6ORbPs+jwQLFEgVRPj+JQf0mUTHXcaBYG+AE4OZcsxbuts5hbCK+zFmImJgSc5zPV6BQVD6RQG80lERXblCi7tcX7VxRvgVHDtDQgaE4vK2gXKiTA6ItubmJo8IoqOylERUVhBh0XXwHEWn1YmNO7mBKg+2KpzK2IpIBUwBhqUtwYre9tbvEutfpWdsFFnkPpVJnQqz15Ozur36FoypREbGH4Zj438u7tO1qIu5qK5PBGyYjebpEkS44e9oVTbxHr5tnvk9ZeXajTuW+zaHNuzYRUmoeCEp-RKlUHJbeYVhYDDfhDVIxR5IGGnISQk6E+6EkxMUBQX9c66EMMg3Q5Zt7rWNkuOBjsjilDUAqQsZQzzgOkJNUk7o7SmF-tiXOUCmQwMGBQ1OF4TgVGzFUG8yIxQGDZiYPI4oqh+mPsrDSy05xhmfm+KO-CLT+iRucEUuwFQqWzMWXM2YTAuTtL6ZBylkRcMrkAA */
id: 'Home machine',
initial: 'Reading projects',
@@ -114,13 +129,7 @@ export const projectsMachine = setup({
target: '.Reading projects',
},
- "Import file from URL": [{
- target: ".Creating project",
- guard: "New project method is used"
- }, {
- target: ".Reading projects",
- actions: "navigateToProject"
- }]
+ 'Import file from URL': '.Creating file',
},
states: {
'Has no projects': {
@@ -165,7 +174,10 @@ export const projectsMachine = setup({
id: 'create-project',
src: 'createProject',
input: ({ event, context }) => {
- if (event.type !== 'Create project' && event.type !== 'Import file from URL') {
+ if (
+ event.type !== 'Create project' &&
+ event.type !== 'Import file from URL'
+ ) {
return {
name: '',
projects: context.projects,
@@ -280,7 +292,41 @@ export const projectsMachine = setup({
actions: ['toastError'],
},
],
- }
+ },
+ },
+
+ 'Creating file': {
+ invoke: {
+ id: 'create-file',
+ src: 'createFile',
+ input: ({ event, context }) => {
+ if (event.type !== 'Import file from URL') {
+ return {
+ code: '',
+ name: '',
+ units: 'mm',
+ method: 'existingProject',
+ projects: context.projects,
+ }
+ }
+ return {
+ code: event.data.code || '',
+ name: event.data.name,
+ units: event.data.units,
+ method: event.data.method,
+ projectName: event.data.projectName,
+ projects: context.projects,
+ }
+ },
+ onDone: {
+ target: 'Reading projects',
+ actions: ['navigateToFile', 'toastSuccess'],
+ },
+ onError: {
+ target: 'Reading projects',
+ actions: 'toastError',
+ },
+ },
},
},
})
diff --git a/src/main.ts b/src/main.ts
index 7f8c9d614..8a3325cd8 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -53,7 +53,7 @@ if (require('electron-squirrel-startup')) {
const ZOO_STUDIO_PROTOCOL = 'zoo-studio'
-/// Register our application to handle all "electron-fiddle://" protocols.
+/// Register our application to handle all "zoo-studio://" protocols.
if (process.defaultApp) {
if (process.argv.length >= 2) {
app.setAsDefaultProtocolClient(ZOO_STUDIO_PROTOCOL, process.execPath, [
@@ -90,6 +90,7 @@ const createWindow = (filePath?: string): BrowserWindow => {
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
newWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL).catch(reportRejection)
} else {
+ console.log('Loading from file', filePath)
getProjectPathAtStartup(filePath)
.then(async (projectPath) => {
const startIndex = path.join(
@@ -320,6 +321,7 @@ const getProjectPathAtStartup = async (
// macOS: open-url events that were received before the app is ready
const getOpenUrls: string[] = (global as any).getOpenUrls
if (getOpenUrls && getOpenUrls.length > 0) {
+ console.log('getOpenUrls', getOpenUrls)
projectPath = getOpenUrls[0] // We only do one project at a
}
// Reset this so we don't accidentally use it again.
@@ -389,6 +391,8 @@ function registerStartupListeners() {
) {
event.preventDefault()
+ console.log('open-url', url)
+
// If we have a mainWindow, lets open another window.
if (mainWindow) {
createWindow(url)
diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx
index d68dba866..8f51a3875 100644
--- a/src/routes/Home.tsx
+++ b/src/routes/Home.tsx
@@ -28,6 +28,7 @@ import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher'
// This route only opens in the desktop context for now,
// as defined in Router.tsx, so we can use the desktop APIs and types.
const Home = () => {
+ // Keep a lookout for a URL query string that invokes the 'import file from URL' command
useCreateFileLinkQuery()
const { state, send } = useProjectsContext()
const { commandBarSend } = useCommandsContext()
@@ -207,7 +208,7 @@ const Home = () => {
Go to a test create-file link