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,85 @@
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { ApiError, post } from "./api";
|
||||
|
||||
/** Messages written without a connection: kept on this device and sent when it is back. */
|
||||
export interface OutboxItem {
|
||||
id: string;
|
||||
sessionId: string;
|
||||
text: string;
|
||||
effort?: string;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
const KEY = "codecraft.outbox";
|
||||
const listeners = new Set<() => void>();
|
||||
let cache: OutboxItem[] | undefined;
|
||||
|
||||
function load(): OutboxItem[] {
|
||||
if (cache) return cache;
|
||||
try {
|
||||
const v = JSON.parse(localStorage.getItem(KEY) ?? "[]");
|
||||
cache = Array.isArray(v) ? v : [];
|
||||
} catch {
|
||||
cache = [];
|
||||
}
|
||||
return cache!;
|
||||
}
|
||||
|
||||
function save(items: OutboxItem[]) {
|
||||
cache = items;
|
||||
try {
|
||||
localStorage.setItem(KEY, JSON.stringify(items));
|
||||
} catch {
|
||||
// Storage blocked: the list still works until the page is closed.
|
||||
}
|
||||
listeners.forEach((l) => l());
|
||||
}
|
||||
|
||||
export function addToOutbox(sessionId: string, text: string, effort?: string) {
|
||||
save([...load(), { id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, sessionId, text, effort, createdAt: Date.now() }]);
|
||||
}
|
||||
|
||||
export function removeFromOutbox(id: string) {
|
||||
save(load().filter((i) => i.id !== id));
|
||||
}
|
||||
|
||||
export function useOutbox(sessionId: string): OutboxItem[] {
|
||||
const all = useSyncExternalStore(
|
||||
(l) => {
|
||||
listeners.add(l);
|
||||
return () => listeners.delete(l);
|
||||
},
|
||||
() => load(),
|
||||
);
|
||||
return all.filter((i) => i.sessionId === sessionId);
|
||||
}
|
||||
|
||||
/** True for failures of the connection itself (not answers of the server). */
|
||||
export function isOffline(e: unknown) {
|
||||
return !(e instanceof ApiError) && (!navigator.onLine || e instanceof TypeError);
|
||||
}
|
||||
|
||||
let flushing = false;
|
||||
|
||||
/** Send the waiting messages of a chat in order; stops at the first connection failure. Returns how many were sent. */
|
||||
export async function flushOutbox(sessionId: string): Promise<number> {
|
||||
if (flushing) return 0;
|
||||
flushing = true;
|
||||
let sent = 0;
|
||||
try {
|
||||
for (const item of load().filter((i) => i.sessionId === sessionId)) {
|
||||
try {
|
||||
await post(`/sessions/${item.sessionId}/prompt`, { text: item.text, effort: item.effort });
|
||||
removeFromOutbox(item.id);
|
||||
sent++;
|
||||
} catch (e) {
|
||||
if (isOffline(e)) break;
|
||||
// The server refused it (deleted chat, read-only …): it would never go through.
|
||||
removeFromOutbox(item.id);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
flushing = false;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
Reference in new issue
Block a user