+
+
+
+
+
+
File System
+
+
+
+
+
+
+
+
+
+
{#await getFiles()}
{:then files}
-
- {/await}
-
- {#await getContent(filename)}
-
-
-
- {:then content}
-
{content}
+
{/await}
-
+
+
+
+ {#if filename}
+
+
{filename}
+
+ {#if isEditing}
+
+
+ {:else}
+
+
+ {/if}
+
+
+
+ {#await getContent(filename)}
+
+ {:then _}
+ {#if isEditing}
+
+ {:else}
+
{content}
+ {/if}
+ {/await}
+ {:else}
+
Select a file to view its contents
+ {/if}
+
+
+
diff --git a/app/src/routes/system/filesystem/Folder.svelte b/app/src/routes/system/filesystem/Folder.svelte
index 42fc4cf..084f296 100644
--- a/app/src/routes/system/filesystem/Folder.svelte
+++ b/app/src/routes/system/filesystem/Folder.svelte
@@ -5,37 +5,40 @@
interface Props {
expanded?: boolean
- name: any
+ name: string
files: any
- selected: any
+ selected: (name: string) => void
+ onDelete: (name: string) => void
}
- let { expanded = $bindable(false), name, files, selected }: Props = $props()
+ let { expanded = $bindable(false), name, files, selected, onDelete }: Props = $props()
function toggle() {
expanded = !expanded
}
-
+
+
-{#if expanded}
-
- {#each Object.entries(files) as [name, content]}
- -
- {#if typeof content == 'object'}
-
- {:else}
-
- {/if}
-
- {/each}
-
-{/if}
+ {#if expanded}
+
+ {#each Object.entries(files) as [itemName, content]}
+ -
+ {#if typeof content === 'object'}
+
+ {:else}
+
+ {/if}
+
+ {/each}
+
+ {/if}
+
diff --git a/app/src/routes/system/filesystem/NewFileDialog.svelte b/app/src/routes/system/filesystem/NewFileDialog.svelte
new file mode 100644
index 0000000..4695a2b
--- /dev/null
+++ b/app/src/routes/system/filesystem/NewFileDialog.svelte
@@ -0,0 +1,44 @@
+
+
+{#if isOpen}
+
+
+
Create New File
+
+
+
+
+
+
+
+
+
+{/if}
diff --git a/app/src/routes/system/filesystem/NewFolderDialog.svelte b/app/src/routes/system/filesystem/NewFolderDialog.svelte
new file mode 100644
index 0000000..92d0a75
--- /dev/null
+++ b/app/src/routes/system/filesystem/NewFolderDialog.svelte
@@ -0,0 +1,44 @@
+
+
+{#if isOpen}
+
+
+
Create New Folder
+
+
+
+
+
+
+
+
+
+{/if}
diff --git a/esp32/lib/ESP32-sveltekit/filesystem.cpp b/esp32/lib/ESP32-sveltekit/filesystem.cpp
index 9e426ef..cf52878 100644
--- a/esp32/lib/ESP32-sveltekit/filesystem.cpp
+++ b/esp32/lib/ESP32-sveltekit/filesystem.cpp
@@ -44,23 +44,28 @@ bool deleteFile(const char *filename) { return ESPFS.remove(filename); }
String listFiles(const String &directory, bool isRoot) {
File root = ESPFS.open(directory.startsWith("/") ? directory : "/" + directory);
- if (!root.isDirectory()) return "";
+ if (!root.isDirectory()) return "{}";
File file = root.openNextFile();
+ if (!file) {
+ return isRoot ? "{ \"root\": {} }" : "{}";
+ }
+
String output = isRoot ? "{ \"root\": {" : "{";
while (file) {
+ String name = String(file.name());
if (file.isDirectory()) {
- output += "\"" + String(file.name()) + "\": " + listFiles(file.name(), false) + ", ";
+ output += "\"" + name + "\": " + listFiles(name, false);
} else {
- output += "\"" + String(file.name()) + "\": " + String(file.size()) + ", ";
+ output += "\"" + name + "\": " + String(file.size());
}
- file = root.openNextFile();
+
+ File next = root.openNextFile();
+ if (next) output += ", ";
+ file = next;
}
- if (output.endsWith(", ")) {
- output.remove(output.length() - 2);
- }
output += "}";
if (isRoot) output += "}";
@@ -98,4 +103,10 @@ bool editFile(const char *filename, const char *content) {
return true;
}
+esp_err_t mkdir(PsychicRequest *request, JsonVariant &json) {
+ const char *path = json["path"].as
();
+ ESP_LOGI(TAG, "Creating directory: %s", path);
+ return ESPFS.mkdir(path) ? request->reply(200) : request->reply(500);
+}
+
} // namespace FileSystem
\ No newline at end of file
diff --git a/esp32/lib/ESP32-sveltekit/filesystem.h b/esp32/lib/ESP32-sveltekit/filesystem.h
index 3fdbb4f..f13cedf 100644
--- a/esp32/lib/ESP32-sveltekit/filesystem.h
+++ b/esp32/lib/ESP32-sveltekit/filesystem.h
@@ -26,4 +26,6 @@ esp_err_t uploadFile(PsychicRequest *request, const String &filename, uint64_t i
esp_err_t getFiles(PsychicRequest *request);
esp_err_t handleDelete(PsychicRequest *request, JsonVariant &json);
esp_err_t handleEdit(PsychicRequest *request, JsonVariant &json);
+
+esp_err_t mkdir(PsychicRequest *request, JsonVariant &json);
} // namespace FileSystem
\ No newline at end of file
diff --git a/esp32/src/spot.cpp b/esp32/src/spot.cpp
index ce13f06..bb21bd3 100644
--- a/esp32/src/spot.cpp
+++ b/esp32/src/spot.cpp
@@ -79,6 +79,7 @@ void Spot::setupServer() {
_server.on("/api/files/delete", HTTP_POST, FileSystem::handleDelete);
_server.on("/api/files/upload/*", HTTP_POST, FileSystem::uploadHandler);
_server.on("/api/files/edit", HTTP_POST, FileSystem::handleEdit);
+ _server.on("/api/files/mkdir", HTTP_POST, FileSystem::mkdir);
// SERVO
_server.on("/api/servo/config", HTTP_GET,