Compare commits

...
Author SHA1 Message Date
Richard Zink 90d65ec654 Release v202611-00014 2026-11-10 10:35:48 +01:00
Richard ZinkandClaude Opus 5.5 7298102e93 Folder picker: create a new folder (new automation, new chat)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 10:35:35 +01:00
6 changed files with 34 additions and 1 deletions

No files matched your search

+7
View File
@@ -3,6 +3,13 @@
All notable changes to CodeCraft. Versions follow [Semantic Versioning](https://semver.org/); All notable changes to CodeCraft. Versions follow [Semantic Versioning](https://semver.org/);
until 1.0, minor versions may contain breaking changes. until 1.0, minor versions may contain breaking changes.
## [v202611-00014] – 2026-11-10
### Added
- **New folder in the folder picker:** creating an automation or a chat, *New folder* creates a
subfolder of the shown folder and selects it.
## [v202611-00013] – 2026-11-10 ## [v202611-00013] – 2026-11-10
### Added ### Added
+1 -1
View File
@@ -11,7 +11,7 @@
</p> </p>
<p align="center"> <p align="center">
<img alt="Version v202611-00013" src="https://img.shields.io/badge/version-v202611--00013-d69e2e?style=flat-square"> <img alt="Version v202611-00014" src="https://img.shields.io/badge/version-v202611--00014-d69e2e?style=flat-square">
<img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-689d6a?style=flat-square"> <img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-689d6a?style=flat-square">
<img alt="Rust backend" src="https://img.shields.io/badge/backend-Rust-b7410e?style=flat-square&logo=rust"> <img alt="Rust backend" src="https://img.shields.io/badge/backend-Rust-b7410e?style=flat-square&logo=rust">
<img alt="React PWA" src="https://img.shields.io/badge/frontend-React%20PWA-2d81ff?style=flat-square&logo=react"> <img alt="React PWA" src="https://img.shields.io/badge/frontend-React%20PWA-2d81ff?style=flat-square&logo=react">
+20
View File
@@ -1,6 +1,7 @@
import { Icon } from "./Icon"; import { Icon } from "./Icon";
import { useEffect, useMemo, useRef, useState, type ReactNode } from "react"; import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
import { post, uploadFile } from "../api"; import { post, uploadFile } from "../api";
import { promptDialog, toast } from "../dialogs";
import { useAgents, useFetch } from "../hooks"; import { useAgents, useFetch } from "../hooks";
import { useT, type Key } from "../i18n"; import { useT, type Key } from "../i18n";
import type { ImageData } from "../types"; import type { ImageData } from "../types";
@@ -602,6 +603,20 @@ export function FolderPicker({ path, onPick }: { path: string; onPick: (p: strin
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [listing?.path]); }, [listing?.path]);
const rel = listing ? listing.path.slice(listing.workspace.length) || "/" : ""; const rel = listing ? listing.path.slice(listing.workspace.length) || "/" : "";
/** Create a subfolder of the shown folder and select it. */
const newFolder = async () => {
if (!listing) return;
const name = (await promptDialog({ title: t("files.newFolder"), message: t("files.newFolderName") }))?.trim();
if (!name) return;
if (name.includes("/") || name === "." || name === "..") return toast(t("new.invalidFolderName"));
const target = `${listing.path.replace(/\/$/, "")}/${name}`;
try {
await post("/files/create", { path: target, dir: true });
setBrowse(target);
} catch (e: any) {
toast(e.message);
}
};
return ( return (
<div className="wiz-picker"> <div className="wiz-picker">
{roots.length > 1 && ( {roots.length > 1 && (
@@ -622,6 +637,11 @@ export function FolderPicker({ path, onPick }: { path: string; onPick: (p: strin
<Icon name="arrow-up" /> {t("new.up")} <Icon name="arrow-up" /> {t("new.up")}
</button> </button>
)} )}
{listing && (
<button className="small" onClick={() => void newFolder()}>
<Icon name="plus" /> {t("files.newFolder")}
</button>
)}
</div> </div>
<div className="dir-list"> <div className="dir-list">
{listing?.dirs.length === 0 && <div className="empty" style={{ padding: 16 }}>{t("new.noSubdirs")}</div>} {listing?.dirs.length === 0 && <div className="empty" style={{ padding: 16 }}>{t("new.noSubdirs")}</div>}
+1
View File
@@ -554,6 +554,7 @@ export const de: Messages = {
"jobs.tplMode": "Modus", "jobs.tplMode": "Modus",
"new.title": "Neue Session", "new.title": "Neue Session",
"new.directory": "Projektverzeichnis", "new.directory": "Projektverzeichnis",
"new.invalidFolderName": "Ordnernamen dürfen kein „/“ enthalten.",
"new.up": "Hoch", "new.up": "Hoch",
"new.noSubdirs": "Keine Unterverzeichnisse", "new.noSubdirs": "Keine Unterverzeichnisse",
"new.model": "Modell", "new.model": "Modell",
+1
View File
@@ -553,6 +553,7 @@ export const en = {
"jobs.tplMode": "Mode", "jobs.tplMode": "Mode",
"new.title": "New session", "new.title": "New session",
"new.directory": "Project directory", "new.directory": "Project directory",
"new.invalidFolderName": "Folder names cannot contain “/”.",
"new.up": "Up", "new.up": "Up",
"new.noSubdirs": "No subdirectories", "new.noSubdirs": "No subdirectories",
"new.model": "Model", "new.model": "Model",
+4
View File
@@ -3,6 +3,10 @@
* under "unreleased"; scripts/release.sh renames the key to the release version. * under "unreleased"; scripts/release.sh renames the key to the release version.
*/ */
export const WHATS_NEW: Record<string, { en: string[]; de: string[] }> = { export const WHATS_NEW: Record<string, { en: string[]; de: string[] }> = {
"202611-00014": {
en: ["Folder picker (new automation, new chat): create a new folder right there"],
de: ["Ordnerauswahl (neue Automatisierung, neuer Chat): direkt einen neuen Ordner anlegen"],
},
"202611-00013": { "202611-00013": {
en: [ en: [
"New automation editor: instructions, a graphical schedule and an assistant chat that writes the instructions with you", "New automation editor: instructions, a graphical schedule and an assistant chat that writes the instructions with you",