diff --git a/e2e/playwright/flow-tests.spec.ts b/e2e/playwright/flow-tests.spec.ts index 7eeb16b72..76c7d34e2 100644 --- a/e2e/playwright/flow-tests.spec.ts +++ b/e2e/playwright/flow-tests.spec.ts @@ -3576,6 +3576,10 @@ test('Basic default modeling and sketch hotkeys work', async ({ page }) => { * TODO: There is a bug somewhere that causes this test to fail * if you toggle the codePane closed before your trigger the * start of the sketch. + * and a separate Safari-only bug that causes the test to fail + * if the pane is open the entire test. The maintainer of CodeMirror + * has pinpointed this to the unusual browser behavior: + * https://discuss.codemirror.net/t/how-to-force-unfocus-of-the-codemirror-element-in-safari/8095/3 */ await codePaneButton.click() diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 550eac533..ae240a9c5 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -344,7 +344,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -379,7 +379,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -425,7 +425,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -574,7 +574,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "syn_derive", ] @@ -883,7 +883,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1085,7 +1085,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1095,7 +1095,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1119,7 +1119,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1130,7 +1130,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1177,7 +1177,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "synstructure", ] @@ -1214,7 +1214,7 @@ dependencies = [ "regex", "serde", "serde_tokenstream", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1225,7 +1225,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1287,7 +1287,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1319,7 +1319,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1417,7 +1417,7 @@ checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1568,7 +1568,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1684,7 +1684,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1960,7 +1960,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1988,7 +1988,7 @@ dependencies = [ "inflections", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -2063,7 +2063,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -2461,15 +2461,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -2568,7 +2559,7 @@ dependencies = [ [[package]] name = "kcl-lib" -version = "0.1.57" +version = "0.1.58" dependencies = [ "anyhow", "approx", @@ -2591,7 +2582,7 @@ dependencies = [ "kittycad-execution-plan-traits", "lazy_static", "mime_guess", - "parse-display 0.9.0", + "parse-display", "reqwest 0.11.27", "ropey", "schemars", @@ -2627,13 +2618,13 @@ dependencies = [ [[package]] name = "kittycad" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6e12eb45fd9a28c8e99dbdef54556246b39acee14e4aa6f0fc43636caa62d9" +checksum = "b0cbef813153197e60c0e96f59eea0b75f8418380f414b20250ee81b60e522c3" dependencies = [ "anyhow", "async-trait", - "base64 0.21.7", + "base64 0.22.1", "bigdecimal", "bytes", "chrono", @@ -2642,10 +2633,10 @@ dependencies = [ "format_serde_error", "futures", "http 0.2.12", - "itertools 0.10.5", + "itertools", "log", "mime_guess", - "parse-display 0.8.2", + "parse-display", "phonenumber", "rand 0.8.5", "reqwest 0.11.27", @@ -2672,7 +2663,7 @@ checksum = "0611fc9b9786175da21d895ffa0f65039e19c9111e94a41b7af999e3b95f045f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3360,43 +3351,17 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "parse-display" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6509d08722b53e8dafe97f2027b22ccbe3a5db83cb352931e9716b0aa44bc5c" -dependencies = [ - "once_cell", - "parse-display-derive 0.8.2", - "regex", -] - [[package]] name = "parse-display" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06af5f9333eb47bd9ba8462d612e37a8328a5cb80b13f0af4de4c3b89f52dee5" dependencies = [ - "parse-display-derive 0.9.0", + "parse-display-derive", "regex", "regex-syntax 0.8.3", ] -[[package]] -name = "parse-display-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68517892c8daf78da08c0db777fcc17e07f2f63ef70041718f8a7630ad84f341" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "regex", - "regex-syntax 0.7.5", - "structmeta 0.2.0", - "syn 2.0.65", -] - [[package]] name = "parse-display-derive" version = "0.9.0" @@ -3407,8 +3372,8 @@ dependencies = [ "quote", "regex", "regex-syntax 0.8.3", - "structmeta 0.3.0", - "syn 2.0.65", + "structmeta", + "syn 2.0.66", ] [[package]] @@ -3550,7 +3515,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3582,14 +3547,14 @@ dependencies = [ [[package]] name = "phonenumber" -version = "0.3.4+8.13.34" +version = "0.3.5+8.13.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d888d375f2963bf06c5079665fbe53db69860879ff5a78524fe3c93c54fb7b8" +checksum = "f174c8db59b620032bd52b655fc97000458850fec0db35fcd4e802b668517ec0" dependencies = [ "bincode", "either", "fnv", - "itertools 0.12.1", + "itertools", "lazy_static", "nom", "quick-xml", @@ -3618,7 +3583,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3770,9 +3735,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -4002,12 +3967,6 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - [[package]] name = "regex-syntax" version = "0.8.3" @@ -4456,7 +4415,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4565,7 +4524,7 @@ checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4576,7 +4535,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4609,7 +4568,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4630,7 +4589,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4672,7 +4631,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4931,18 +4890,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "structmeta" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ad9e09554f0456d67a69c1584c9798ba733a5b50349a6c0d0948710523922d" -dependencies = [ - "proc-macro2", - "quote", - "structmeta-derive 0.2.0", - "syn 2.0.65", -] - [[package]] name = "structmeta" version = "0.3.0" @@ -4951,19 +4898,8 @@ checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329" dependencies = [ "proc-macro2", "quote", - "structmeta-derive 0.3.0", - "syn 2.0.65", -] - -[[package]] -name = "structmeta-derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.65", + "structmeta-derive", + "syn 2.0.66", ] [[package]] @@ -4974,7 +4910,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4996,7 +4932,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -5029,9 +4965,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.65" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -5047,7 +4983,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -5064,7 +5000,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -5281,7 +5217,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.65", + "syn 2.0.66", "tauri-utils", "thiserror", "time", @@ -5299,7 +5235,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "tauri-codegen", "tauri-utils", ] @@ -5665,7 +5601,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -5754,7 +5690,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -5943,7 +5879,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -5972,7 +5908,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -6093,7 +6029,7 @@ source = "git+https://github.com/Aleph-Alpha/ts-rs#f898578d80d3e2a54080c1c046c45 dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "termcolor", ] @@ -6306,7 +6242,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -6405,7 +6341,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -6439,7 +6375,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6580,7 +6516,7 @@ checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -6686,7 +6622,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -6697,7 +6633,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -7139,7 +7075,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] diff --git a/src/components/FileMachineProvider.tsx b/src/components/FileMachineProvider.tsx index 2665927f0..a84dda72b 100644 --- a/src/components/FileMachineProvider.tsx +++ b/src/components/FileMachineProvider.tsx @@ -11,6 +11,7 @@ import { InterpreterFrom, Prop, StateFrom, + assign, } from 'xstate' import { useCommandsContext } from 'hooks/useCommandsContext' import { fileMachine } from 'machines/fileMachine' @@ -37,7 +38,7 @@ export const FileMachineProvider = ({ }) => { const navigate = useNavigate() const { commandBarSend } = useCommandsContext() - const { project } = useRouteLoaderData(paths.FILE) as IndexLoaderData + const { project, file } = useRouteLoaderData(paths.FILE) as IndexLoaderData const [state, send] = useMachine(fileMachine, { context: { @@ -53,8 +54,32 @@ export const FileMachineProvider = ({ context.selectedDirectory + sep() + event.data.name )}` ) + } else if ( + event.data && + 'path' in event.data && + event.data.path.endsWith(FILE_EXT) + ) { + // Don't navigate to newly created directories + navigate(`${paths.FILE}/${encodeURIComponent(event.data.path)}`) } }, + addFileToRenamingQueue: assign({ + itemsBeingRenamed: (context, event) => [ + ...context.itemsBeingRenamed, + event.data.path, + ], + }), + removeFileFromRenamingQueue: assign({ + itemsBeingRenamed: ( + context, + event: EventFrom + ) => + context.itemsBeingRenamed.filter( + (path) => path !== event.data.oldPath + ), + }), + renameToastSuccess: (_, event) => toast.success(event.data.message), + createToastSuccess: (_, event) => toast.success(event.data.message), toastSuccess: (_, event) => event.data && toast.success((event.data || '') + ''), toastError: (_, event) => toast.error((event.data || '') + ''), @@ -70,37 +95,56 @@ export const FileMachineProvider = ({ } }, createFile: async (context, event) => { - let name = event.data.name.trim() || DEFAULT_FILE_NAME + let createdName = event.data.name.trim() || DEFAULT_FILE_NAME + let createdPath: string if (event.data.makeDir) { - await mkdir(await join(context.selectedDirectory.path, name)) + createdPath = await join(context.selectedDirectory.path, createdName) + await mkdir(createdPath) } else { - await create( + createdPath = context.selectedDirectory.path + - sep() + - name + - (name.endsWith(FILE_EXT) ? '' : FILE_EXT) - ) + sep() + + createdName + + (createdName.endsWith(FILE_EXT) ? '' : FILE_EXT) + await create(createdPath) } - return `Successfully created "${name}"` + return { + message: `Successfully created "${createdName}"`, + path: createdPath, + } }, renameFile: async ( context: ContextFrom, event: EventFrom ) => { const { oldName, newName, isDir } = event.data - let name = newName ? newName : DEFAULT_FILE_NAME + const name = newName ? newName : DEFAULT_FILE_NAME + const oldPath = await join(context.selectedDirectory.path, oldName) + const newDirPath = await join(context.selectedDirectory.path, name) + const newPath = + newDirPath + (name.endsWith(FILE_EXT) || isDir ? '' : FILE_EXT) - await rename( - await join(context.selectedDirectory.path, oldName), - (await join(context.selectedDirectory.path, name)) + - (name.endsWith(FILE_EXT) || isDir ? '' : FILE_EXT), - {} - ) - return ( - oldName !== name && `Successfully renamed "${oldName}" to "${name}"` - ) + await rename(oldPath, newPath, {}) + + if (oldPath === file?.path && project?.path) { + // If we just renamed the current file, navigate to the new path + navigate(paths.FILE + '/' + encodeURIComponent(newPath)) + } else if (file?.path.includes(oldPath)) { + // If we just renamed a directory that the current file is in, navigate to the new path + navigate( + paths.FILE + + '/' + + encodeURIComponent(file.path.replace(oldPath, newDirPath)) + ) + } + + return { + message: `Successfully renamed "${oldName}" to "${name}"`, + newPath, + oldPath, + } }, deleteFile: async ( context: ContextFrom, @@ -117,6 +161,17 @@ export const FileMachineProvider = ({ console.error('Error deleting file', e) ) } + + // If we just deleted the current file or one of its parent directories, + // navigate to the project root + if ( + (event.data.path === file?.path || + file?.path.includes(event.data.path)) && + project?.path + ) { + navigate(paths.FILE + '/' + encodeURIComponent(project.path)) + } + return `Successfully deleted ${isDir ? 'folder' : 'file'} "${ event.data.name }"` diff --git a/src/components/FileTree.tsx b/src/components/FileTree.tsx index 69799e8f2..436e27815 100644 --- a/src/components/FileTree.tsx +++ b/src/components/FileTree.tsx @@ -2,11 +2,11 @@ import type { FileEntry, IndexLoaderData } from 'lib/types' import { paths } from 'lib/paths' import { ActionButton } from './ActionButton' import Tooltip from './Tooltip' -import { Dispatch, useEffect, useRef, useState } from 'react' +import { Dispatch, useCallback, useEffect, useRef, useState } from 'react' import { useNavigate, useRouteLoaderData } from 'react-router-dom' -import { Dialog, Disclosure } from '@headlessui/react' +import { Disclosure } from '@headlessui/react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faChevronRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons' +import { faChevronRight } from '@fortawesome/free-solid-svg-icons' import { useFileContext } from 'hooks/useFileContext' import styles from './FileTree.module.css' import { sortProject } from 'lib/tauriFS' @@ -16,6 +16,8 @@ import { codeManager, kclManager } from 'lib/singletons' import { useDocumentHasFocus } from 'hooks/useDocumentHasFocus' import { useLspContext } from './LspProvider' import useHotkeyWrapper from 'lib/hotkeyWrapper' +import { useModelingContext } from 'hooks/useModelingContext' +import { DeleteConfirmationDialog } from './ProjectCard/DeleteProjectDialog' function getIndentationCSS(level: number) { return `calc(1rem * ${level + 1})` @@ -23,11 +25,11 @@ function getIndentationCSS(level: number) { function RenameForm({ fileOrDir, - setIsRenaming, + onSubmit, level = 0, }: { fileOrDir: FileEntry - setIsRenaming: Dispatch> + onSubmit: () => void level?: number }) { const { send } = useFileContext() @@ -35,7 +37,6 @@ function RenameForm({ function handleRenameSubmit(e: React.FormEvent) { e.preventDefault() - setIsRenaming(false) send({ type: 'Rename file', data: { @@ -49,7 +50,7 @@ function RenameForm({ function handleKeyDown(e: React.KeyboardEvent) { if (e.key === 'Escape') { e.stopPropagation() - setIsRenaming(false) + onSubmit() } } @@ -61,10 +62,12 @@ function RenameForm({ ref={inputRef} type="text" autoFocus + autoCapitalize="off" + autoCorrect="off" placeholder={fileOrDir.name} className="w-full py-1 bg-transparent text-chalkboard-100 placeholder:text-chalkboard-70 dark:text-chalkboard-10 dark:placeholder:text-chalkboard-50 focus:outline-none focus:ring-0" onKeyDown={handleKeyDown} - onBlur={() => setIsRenaming(false)} + onBlur={onSubmit} style={{ paddingInlineStart: getIndentationCSS(level) }} /> @@ -75,7 +78,7 @@ function RenameForm({ ) } -function DeleteConfirmationDialog({ +function DeleteFileTreeItemDialog({ fileOrDir, setIsOpen, }: { @@ -84,48 +87,23 @@ function DeleteConfirmationDialog({ }) { const { send } = useFileContext() return ( - setIsOpen(false)} - className="relative z-50" + setIsOpen(false)} + onConfirm={() => { + send({ type: 'Delete file', data: fileOrDir }) + setIsOpen(false) + }} > -
- - - Delete {fileOrDir.children !== undefined ? 'Folder' : 'File'} - - - This will permanently delete "{fileOrDir.name || 'this file'}" - {fileOrDir.children !== undefined - ? ' and all of its contents. ' - : '. '} - This action cannot be undone. - - -
- { - send({ type: 'Delete file', data: fileOrDir }) - setIsOpen(false) - }} - iconStart={{ - icon: faTrashAlt, - bgClassName: 'bg-destroy-80', - iconClassName: - 'text-destroy-20 group-hover:text-destroy-10 hover:text-destroy-10 dark:text-destroy-20 dark:group-hover:text-destroy-10 dark:hover:text-destroy-10', - }} - className="hover:border-destroy-40 dark:hover:border-destroy-40" - > - Delete - - setIsOpen(false)}> - Cancel - -
-
-
-
+

+ This will permanently delete "{fileOrDir.name || 'this file'}" + {fileOrDir.children !== undefined ? ' and all of its contents. ' : '. '} +

+

+ Are you sure you want to delete "{fileOrDir.name || 'this file'} + "? This action cannot be undone. +

+ ) } @@ -133,35 +111,57 @@ const FileTreeItem = ({ project, currentFile, fileOrDir, - onDoubleClick, + onNavigateToFile, level = 0, }: { project?: IndexLoaderData['project'] currentFile?: IndexLoaderData['file'] fileOrDir: FileEntry - onDoubleClick?: () => void + onNavigateToFile?: () => void level?: number }) => { - const { send, context } = useFileContext() + const { send: fileSend, context: fileContext } = useFileContext() const { onFileOpen, onFileClose } = useLspContext() const navigate = useNavigate() - const [isRenaming, setIsRenaming] = useState(false) const [isConfirmingDelete, setIsConfirmingDelete] = useState(false) const isCurrentFile = fileOrDir.path === currentFile?.path + const isRenaming = fileContext.itemsBeingRenamed.includes(fileOrDir.path) + const removeCurrentItemFromRenaming = useCallback( + () => + fileSend({ + type: 'assign', + data: { + itemsBeingRenamed: fileContext.itemsBeingRenamed.filter( + (path) => path !== fileOrDir.path + ), + }, + }), + [fileContext.itemsBeingRenamed, fileOrDir.path, fileSend] + ) + + const addCurrentItemToRenaming = useCallback(() => { + fileSend({ + type: 'assign', + data: { + itemsBeingRenamed: [...fileContext.itemsBeingRenamed, fileOrDir.path], + }, + }) + }, [fileContext.itemsBeingRenamed, fileOrDir.path, fileSend]) + function handleKeyUp(e: React.KeyboardEvent) { if (e.metaKey && e.key === 'Backspace') { // Open confirmation dialog setIsConfirmingDelete(true) } else if (e.key === 'Enter') { // Show the renaming form - setIsRenaming(true) + addCurrentItemToRenaming() } else if (e.code === 'Space') { - handleDoubleClick() + handleClick() } } - function handleDoubleClick() { + function handleClick() { if (fileOrDir.children !== undefined) return // Don't open directories if (fileOrDir.name?.endsWith(FILE_EXT) === false && project?.path) { @@ -181,7 +181,7 @@ const FileTreeItem = ({ // Open kcl files navigate(`${paths.FILE}/${encodeURIComponent(fileOrDir.path)}`) } - onDoubleClick?.() + onNavigateToFile?.() } return ( @@ -199,8 +199,10 @@ const FileTreeItem = ({