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,55 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { api, get } from "../api";
|
||||
import { useT } from "../i18n";
|
||||
import { toast } from "../dialogs";
|
||||
import { Icon } from "./Icon";
|
||||
|
||||
/** "bash:git status" → "git status"; tool names stay. */
|
||||
const label = (key: string) => (key.startsWith("bash:") ? `$ ${key.slice(5)}` : key);
|
||||
|
||||
/** What the user allowed "for this project" (all chats of the folder) – with a way to revoke it. */
|
||||
export function ProjectPermissions({ sessionId, cwd, onClose }: { sessionId: string; cwd: string; onClose: () => void }) {
|
||||
const t = useT();
|
||||
const [keys, setKeys] = useState<string[]>();
|
||||
useEffect(() => {
|
||||
get<string[]>(`/sessions/${sessionId}/approvals`).then(setKeys, (e) => (toast(e.message), setKeys([])));
|
||||
}, [sessionId]);
|
||||
const revoke = async (key: string) => {
|
||||
try {
|
||||
await api(`/sessions/${sessionId}/approvals`, { method: "DELETE", json: { key } });
|
||||
setKeys((k) => k?.filter((x) => x !== key));
|
||||
} catch (e: any) {
|
||||
toast(e.message);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="modal-backdrop" onClick={onClose}>
|
||||
<div className="modal" role="dialog" aria-label={t("permProject.title")} onClick={(e) => e.stopPropagation()}>
|
||||
<h3>
|
||||
<Icon name="lock" /> {t("permProject.title")}
|
||||
</h3>
|
||||
<p style={{ fontSize: 13, color: "var(--fg3)", marginBottom: 4 }}>{t("permProject.intro")}</p>
|
||||
<div className="path mono" style={{ marginBottom: 8 }}>{cwd}</div>
|
||||
{keys === undefined ? (
|
||||
<div className="empty">…</div>
|
||||
) : keys.length === 0 ? (
|
||||
<p style={{ fontSize: 13 }}>{t("permProject.empty")}</p>
|
||||
) : (
|
||||
<ul className="rewind-files">
|
||||
{keys.map((k) => (
|
||||
<li key={k} className="row" style={{ justifyContent: "space-between" }}>
|
||||
<code>{label(k)}</code>
|
||||
<button className="small danger" onClick={() => void revoke(k)}>
|
||||
{t("permProject.revoke")}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
<div className="row" style={{ justifyContent: "flex-end", marginTop: 10 }}>
|
||||
<button onClick={onClose}>{t("common.close")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user