Claude Code engine, image viewer, colour tags, checks per turn and more
- Claude Code as an engine: chats on a "claude-code/<alias>" model run through the claude CLI (stream-json); tool approvals go through our permission system. Sign in from the web UI (URL + pasted code), CLI version and update from Settings; the Docker image contains the CLI. - Small models (8k context) for side tasks: input is cut to the model's context window; one-line chat status, "What does this do?" on approvals, "Plan as automation" (job draft from a chat). - Mobile: top bar no longer widens the page, image viewer (zoom, swipe, mark up, use in message), jump to previous own message, message menu, time under every message. - Approvals: push actions (allow/reject), "always allow in this project" (never for dangerous commands) with revoke. - Per turn: changed files with diffs and rollback; project check command with a green/red badge and "let the agent fix it". - Colour tags for chats in the colours of the active theme. - Project defaults for new chats, project page (timeline), offline queue. Tested in a throwaway container with a mock model (8k small model): 26 API checks passed (permissions, diffs, checks, defaults, job draft, side-task budget, Claude Code status/login URL, unsigned-in chat). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
084452abf6
commit
62fe5bf467
42 files changed
+3734
-55
No files matched your search
@@ -0,0 +1,75 @@
|
||||
import { createContext, useContext } from "react";
|
||||
import { post } from "../api";
|
||||
import { asQuote, compose } from "../compose";
|
||||
import { useT } from "../i18n";
|
||||
import { navigate } from "../router";
|
||||
import { loadSessions } from "../store";
|
||||
import { toast } from "../dialogs";
|
||||
import { Icon } from "./Icon";
|
||||
|
||||
/** What the message menus of one chat need to know. */
|
||||
export const ChatCtx = createContext<{ sessionId: string; canFork: boolean; canReply: boolean }>({ sessionId: "", canFork: false, canReply: false });
|
||||
|
||||
/**
|
||||
* Actions of one message: copy, quote into the reply, fork the chat from here and
|
||||
* (for own messages) edit and resend. Hover on desktop, always faintly visible on touch.
|
||||
*/
|
||||
export function MessageMenu({ messageId, text, forkable, onRewind }: { messageId: string; text: string; forkable: boolean; onRewind?: () => void }) {
|
||||
const t = useT();
|
||||
const { sessionId, canFork, canReply } = useContext(ChatCtx);
|
||||
const close = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
(e.currentTarget.closest("details") as HTMLDetailsElement).open = false;
|
||||
};
|
||||
if (!text.trim() && !onRewind) return null;
|
||||
return (
|
||||
<details className="menu-pop msg-menu">
|
||||
<summary className="small ghost" title={t("msg.actions")} aria-label={t("msg.actions")}>
|
||||
<Icon name="more" />
|
||||
</summary>
|
||||
<div className="menu-pop-body">
|
||||
{text.trim() && (
|
||||
<a
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
close(e);
|
||||
const done = () => toast(t("msg.copied"), "success");
|
||||
if (navigator.clipboard?.writeText) void navigator.clipboard.writeText(text).then(done, () => toast(t("msg.copyFailed")));
|
||||
else toast(t("msg.copyFailed"));
|
||||
}}
|
||||
>
|
||||
<Icon name="copy" /> {t("msg.copy")}
|
||||
</a>
|
||||
)}
|
||||
{text.trim() && canReply && (
|
||||
<a href="#" onClick={(e) => (close(e), compose({ sessionId, text: asQuote(text) }))}>
|
||||
<Icon name="reply" /> {t("msg.quote")}
|
||||
</a>
|
||||
)}
|
||||
{onRewind && (
|
||||
<a href="#" onClick={(e) => (close(e), onRewind())}>
|
||||
<Icon name="edit" /> {t("chat.rewind")}
|
||||
</a>
|
||||
)}
|
||||
{forkable && canFork && (
|
||||
<a
|
||||
href="#"
|
||||
onClick={async (e) => {
|
||||
close(e);
|
||||
try {
|
||||
const fork = await post<{ id: string }>(`/sessions/${sessionId}/fork`, { message_id: messageId });
|
||||
await loadSessions();
|
||||
toast(t("chat.forked"));
|
||||
navigate(`/s/${fork.id}`);
|
||||
} catch (err: any) {
|
||||
toast(err.message);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon name="fork" /> {t("msg.forkHere")}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</details>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user