Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77f9d4b0e0 | ||
|
|
103132a359 |
No files matched your search
@@ -3,6 +3,13 @@
|
||||
All notable changes to CodeCraft. Versions follow [Semantic Versioning](https://semver.org/);
|
||||
until 1.0, minor versions may contain breaking changes.
|
||||
|
||||
## [v202611-00038] – 2026-11-15
|
||||
|
||||
### Changed
|
||||
|
||||
- The chat menu in the sidebar has a **Delete** entry (with the question about the chat's own folder);
|
||||
colour and icon selection moved out of it – they remain in the ⋯ menu inside the chat.
|
||||
|
||||
## [v202611-00037] – 2026-11-15
|
||||
|
||||
### Added
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img alt="Version v202611-00037" src="https://img.shields.io/badge/version-v202611--00037-d69e2e?style=flat-square">
|
||||
<img alt="Version v202611-00038" src="https://img.shields.io/badge/version-v202611--00038-d69e2e?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="React PWA" src="https://img.shields.io/badge/frontend-React%20PWA-2d81ff?style=flat-square&logo=react">
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { del, get } from "./api";
|
||||
import { choiceDialog, confirmDialog, toast } from "./dialogs";
|
||||
import { t } from "./i18n";
|
||||
import { formatSize } from "./components/FileFigure";
|
||||
|
||||
interface FolderInfo {
|
||||
own: boolean;
|
||||
path?: string;
|
||||
files?: number;
|
||||
bytes?: number;
|
||||
git?: boolean;
|
||||
truncated?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a chat after asking. A chat with a folder of its own (not shared with other chats, not a
|
||||
* project) gets the question whether the folder goes with it. Resolves to true when it was deleted.
|
||||
*/
|
||||
export async function deleteChat(session: { id: string; parent_id?: string | null }): Promise<boolean> {
|
||||
const folder = session.parent_id ? undefined : await get<FolderInfo>(`/sessions/${session.id}/folder`).catch(() => undefined);
|
||||
let withFolder = false;
|
||||
if (folder?.own) {
|
||||
const size = `${folder.truncated ? "> " : ""}${t("chat.folderFiles", { n: folder.files ?? 0 })}, ${formatSize(folder.bytes ?? 0)}`;
|
||||
const choice = await choiceDialog({
|
||||
title: t("chat.deleteTitle"),
|
||||
message: `${t("chat.deleteWithFolder", { path: folder.path ?? "", size })}${folder.git ? `\n\n${t("chat.folderHasGit")}` : ""}`,
|
||||
choices: [
|
||||
{ value: "chat", label: t("chat.deleteOnlyChat") },
|
||||
{ value: "both", label: t("chat.deleteChatAndFolder"), danger: true },
|
||||
],
|
||||
});
|
||||
if (!choice) return false;
|
||||
withFolder = choice === "both";
|
||||
} else if (!(await confirmDialog({ message: t("chat.deleteConfirm"), danger: true, confirmLabel: t("common.delete") }))) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const r = await del<{ folder_deleted?: boolean; folder_error?: string }>(`/sessions/${session.id}${withFolder ? "?delete_folder=true" : ""}`);
|
||||
if (r?.folder_error) toast(r.folder_error);
|
||||
else if (r?.folder_deleted) toast(t("chat.folderDeleted"), "success");
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
toast(e.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { takePrefill } from "../share";
|
||||
import { Icon } from "./Icon";
|
||||
import { FileChip, FileFigure, fileType, imageSize, imageSrc, formatSize } from "./FileFigure";
|
||||
import { FileChip, FileFigure, fileType, imageSize, imageSrc } from "./FileFigure";
|
||||
import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode, type RefObject } from "react";
|
||||
import { del, get, patch, post, put } from "../api";
|
||||
import { useFetch, useModels } from "../hooks";
|
||||
@@ -24,7 +24,8 @@ import { UsageOverlay } from "./UsageOverlay";
|
||||
import { RewindDialog } from "./RewindDialog";
|
||||
import { ShellsChip } from "./ShellsPanel";
|
||||
import { TodoList, ToolCard } from "./ToolCard";
|
||||
import { choiceDialog, confirmDialog, promptDialog, toast } from "../dialogs";
|
||||
import { promptDialog, toast } from "../dialogs";
|
||||
import { deleteChat } from "../chatActions";
|
||||
import { ChatTabBar, ChatTabPanes, useChatTabs } from "./ChatTabs";
|
||||
import { ColorSwatches, IconPicker } from "./ColorSwatches";
|
||||
import { ProjectPermissions } from "./ProjectPermissions";
|
||||
@@ -757,30 +758,7 @@ export function ChatView({ sessionId, onMenu, onClosePane }: { sessionId: string
|
||||
};
|
||||
|
||||
const remove = async () => {
|
||||
// Does the chat have a folder of its own? Then ask whether it goes too.
|
||||
const folder = session.parent_id ? undefined : await get<{ own: boolean; path?: string; files?: number; bytes?: number; git?: boolean; truncated?: boolean }>(`/sessions/${sessionId}/folder`).catch(() => undefined);
|
||||
let withFolder = false;
|
||||
if (folder?.own) {
|
||||
const size = `${folder.truncated ? "> " : ""}${t("chat.folderFiles", { n: folder.files ?? 0 })}, ${formatSize(folder.bytes ?? 0)}`;
|
||||
const choice = await choiceDialog({
|
||||
title: t("chat.deleteTitle"),
|
||||
message: `${t("chat.deleteWithFolder", { path: folder.path ?? "", size })}${folder.git ? `\n\n${t("chat.folderHasGit")}` : ""}`,
|
||||
choices: [
|
||||
{ value: "chat", label: t("chat.deleteOnlyChat") },
|
||||
{ value: "both", label: t("chat.deleteChatAndFolder"), danger: true },
|
||||
],
|
||||
});
|
||||
if (!choice) return;
|
||||
withFolder = choice === "both";
|
||||
} else if (!(await confirmDialog({ message: t("chat.deleteConfirm"), danger: true, confirmLabel: t("common.delete") }))) return;
|
||||
try {
|
||||
const r = await del<{ folder_deleted?: boolean; folder_error?: string }>(`/sessions/${sessionId}${withFolder ? "?delete_folder=true" : ""}`);
|
||||
if (r?.folder_error) toast(r.folder_error);
|
||||
else if (r?.folder_deleted) toast(t("chat.folderDeleted"), "success");
|
||||
} catch (e: any) {
|
||||
return void toast(e.message);
|
||||
}
|
||||
navigate(session.parent_id ? `/s/${session.parent_id}` : "/new");
|
||||
if (await deleteChat(session)) navigate(session.parent_id ? `/s/${session.parent_id}` : "/new");
|
||||
};
|
||||
|
||||
// Context usage of the last request relative to the model's window.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ColorSwatches, IconPicker } from "./ColorSwatches";
|
||||
import { deleteChat } from "../chatActions";
|
||||
import { Icon } from "./Icon";
|
||||
import { StatusLeds } from "./StatusLeds";
|
||||
import { cycleThemeMode, getThemeMode, onThemeChange } from "../theme";
|
||||
@@ -98,11 +98,21 @@ function SessionRow({ s, active, attention, busy, folders }: { s: Session; activ
|
||||
<Icon name="folder-up" /> {t("sidebar.removeFromFolder")}
|
||||
</button>
|
||||
)}
|
||||
<ColorSwatches value={s.color} onChange={(color) => (setMenu(false), organise(s.id, { color }))} />
|
||||
<IconPicker value={s.icon} color={s.color} onChange={(icon) => (setMenu(false), organise(s.id, { icon }))} />
|
||||
<button role="menuitem" onClick={() => (setMenu(false), organise(s.id, { archived: !s.archived }))}>
|
||||
<Icon name="archive" /> {t(s.archived ? "sidebar.unarchive" : "sidebar.archiveOne")}
|
||||
</button>
|
||||
{!s.shared && (
|
||||
<button
|
||||
role="menuitem"
|
||||
className="danger"
|
||||
onClick={async () => {
|
||||
setMenu(false);
|
||||
if ((await deleteChat(s)) && active) navigate("/new");
|
||||
}}
|
||||
>
|
||||
<Icon name="trash" /> {t("chat.delete")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
* under "unreleased"; scripts/release.sh renames the key to the release version.
|
||||
*/
|
||||
export const WHATS_NEW: Record<string, { en: string[]; de: string[] }> = {
|
||||
"202611-00038": {
|
||||
en: ["Sidebar chat menu: Delete added; colour and icon are now chosen in the chat's own menu"],
|
||||
de: ["Chat-Menü in der Sidebar: Löschen hinzugefügt; Farbe und Icon wählst du jetzt im Menü des Chats"],
|
||||
},
|
||||
"202611-00037": {
|
||||
en: ["Deleting a chat now asks whether to delete its own folder too"],
|
||||
de: ["Beim Löschen eines Chats wirst du gefragt, ob sein eigener Ordner mitgelöscht werden soll"],
|
||||
|
||||
Reference in new issue
Block a user