feat(web): full UI redesign with light/dark theme, TanStack data tables, media rendering, and gateway-routed agent execution
Overhaul the Dench web app with a comprehensive visual redesign and several
major feature additions across the chat interface, workspace, and agent
runtime layer.
Theme & Design System
- Replace the dark-only palette with a full light/dark theme system that
respects system preference via localStorage + inline script (no FOUC).
- Introduce new design tokens: glassmorphism surfaces, semantic colors
(success/warning/error/info), object-type chip palettes, and a tiered
shadow scale (sm/md/lg/xl).
- Add Instrument Serif + Inter via Google Fonts for a refined typographic
hierarchy; headings use the serif face, body uses Inter.
- Rebrand UI from "Ironclaw" to "Dench" across the landing page and
metadata.
Chat & Chain-of-Thought
- Rewrite the chain-of-thought component with inline media detection and
rendering — images, video, audio, and PDFs referenced in agent output
are now displayed directly in the conversation thread.
- Add status indicator parts (e.g. "Preparing response...",
"Optimizing session context...") that render as subtle activity badges
instead of verbose reasoning blocks.
- Integrate react-markdown with remark-gfm for proper markdown rendering
in assistant messages (tables, strikethrough, autolinks, etc.).
- Improve report-block splitting and lazy-loaded ReportCard rendering.
Workspace
- Introduce @tanstack/react-table for the object table, replacing the
hand-rolled table with full column sorting, fuzzy filtering via
match-sorter-utils, row selection, and bulk actions.
- Add a new media viewer component for in-workspace image/video/PDF
preview.
- New API routes: bulk-delete entries, field management (CRUD + reorder),
raw-file serving endpoint for media assets.
- Redesign workspace sidebar, empty state, and entry detail modal with
the new theme tokens and improved layout.
Agent Runtime
- Switch web agent execution from --local to gateway-routed mode so
concurrent chat threads share the gateway's lane-based concurrency
system, eliminating cross-process file-lock contention.
- Advertise "tool-events" capability during WebSocket handshake so the
gateway streams tool start/update/result events to the UI.
- Add new agent callback hooks: onLifecycleStart, onCompactionStart/End,
and onToolUpdate for richer real-time feedback.
- Forward media URLs emitted by agent events into the chat stream.
Dependencies
- Add @tanstack/match-sorter-utils and @tanstack/react-table to the web
app.
Published as ironclaw@2026.2.10-1.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 11:17:23 -08:00
|
|
|
import { existsSync, readFileSync } from "node:fs";
|
|
|
|
|
import { resolve } from "node:path";
|
2026-02-15 18:18:15 -08:00
|
|
|
import { safeResolvePath, resolveWorkspaceRoot } from "@/lib/workspace";
|
feat(web): full UI redesign with light/dark theme, TanStack data tables, media rendering, and gateway-routed agent execution
Overhaul the Dench web app with a comprehensive visual redesign and several
major feature additions across the chat interface, workspace, and agent
runtime layer.
Theme & Design System
- Replace the dark-only palette with a full light/dark theme system that
respects system preference via localStorage + inline script (no FOUC).
- Introduce new design tokens: glassmorphism surfaces, semantic colors
(success/warning/error/info), object-type chip palettes, and a tiered
shadow scale (sm/md/lg/xl).
- Add Instrument Serif + Inter via Google Fonts for a refined typographic
hierarchy; headings use the serif face, body uses Inter.
- Rebrand UI from "Ironclaw" to "Dench" across the landing page and
metadata.
Chat & Chain-of-Thought
- Rewrite the chain-of-thought component with inline media detection and
rendering — images, video, audio, and PDFs referenced in agent output
are now displayed directly in the conversation thread.
- Add status indicator parts (e.g. "Preparing response...",
"Optimizing session context...") that render as subtle activity badges
instead of verbose reasoning blocks.
- Integrate react-markdown with remark-gfm for proper markdown rendering
in assistant messages (tables, strikethrough, autolinks, etc.).
- Improve report-block splitting and lazy-loaded ReportCard rendering.
Workspace
- Introduce @tanstack/react-table for the object table, replacing the
hand-rolled table with full column sorting, fuzzy filtering via
match-sorter-utils, row selection, and bulk actions.
- Add a new media viewer component for in-workspace image/video/PDF
preview.
- New API routes: bulk-delete entries, field management (CRUD + reorder),
raw-file serving endpoint for media assets.
- Redesign workspace sidebar, empty state, and entry detail modal with
the new theme tokens and improved layout.
Agent Runtime
- Switch web agent execution from --local to gateway-routed mode so
concurrent chat threads share the gateway's lane-based concurrency
system, eliminating cross-process file-lock contention.
- Advertise "tool-events" capability during WebSocket handshake so the
gateway streams tool start/update/result events to the UI.
- Add new agent callback hooks: onLifecycleStart, onCompactionStart/End,
and onToolUpdate for richer real-time feedback.
- Forward media URLs emitted by agent events into the chat stream.
Dependencies
- Add @tanstack/match-sorter-utils and @tanstack/react-table to the web
app.
Published as ironclaw@2026.2.10-1.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 11:17:23 -08:00
|
|
|
|
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
|
export const runtime = "nodejs";
|
|
|
|
|
|
|
|
|
|
const MIME_MAP: Record<string, string> = {
|
|
|
|
|
// Images
|
|
|
|
|
jpg: "image/jpeg",
|
|
|
|
|
jpeg: "image/jpeg",
|
|
|
|
|
png: "image/png",
|
|
|
|
|
gif: "image/gif",
|
|
|
|
|
webp: "image/webp",
|
|
|
|
|
svg: "image/svg+xml",
|
|
|
|
|
ico: "image/x-icon",
|
|
|
|
|
bmp: "image/bmp",
|
|
|
|
|
tiff: "image/tiff",
|
|
|
|
|
tif: "image/tiff",
|
|
|
|
|
avif: "image/avif",
|
|
|
|
|
heic: "image/heic",
|
|
|
|
|
heif: "image/heif",
|
|
|
|
|
// Video
|
|
|
|
|
mp4: "video/mp4",
|
|
|
|
|
webm: "video/webm",
|
|
|
|
|
mov: "video/quicktime",
|
|
|
|
|
avi: "video/x-msvideo",
|
|
|
|
|
mkv: "video/x-matroska",
|
|
|
|
|
// Audio
|
|
|
|
|
mp3: "audio/mpeg",
|
|
|
|
|
wav: "audio/wav",
|
|
|
|
|
ogg: "audio/ogg",
|
|
|
|
|
m4a: "audio/mp4",
|
|
|
|
|
// Documents
|
|
|
|
|
pdf: "application/pdf",
|
2026-02-21 14:39:55 -08:00
|
|
|
html: "text/html",
|
|
|
|
|
htm: "text/html",
|
feat(web): full UI redesign with light/dark theme, TanStack data tables, media rendering, and gateway-routed agent execution
Overhaul the Dench web app with a comprehensive visual redesign and several
major feature additions across the chat interface, workspace, and agent
runtime layer.
Theme & Design System
- Replace the dark-only palette with a full light/dark theme system that
respects system preference via localStorage + inline script (no FOUC).
- Introduce new design tokens: glassmorphism surfaces, semantic colors
(success/warning/error/info), object-type chip palettes, and a tiered
shadow scale (sm/md/lg/xl).
- Add Instrument Serif + Inter via Google Fonts for a refined typographic
hierarchy; headings use the serif face, body uses Inter.
- Rebrand UI from "Ironclaw" to "Dench" across the landing page and
metadata.
Chat & Chain-of-Thought
- Rewrite the chain-of-thought component with inline media detection and
rendering — images, video, audio, and PDFs referenced in agent output
are now displayed directly in the conversation thread.
- Add status indicator parts (e.g. "Preparing response...",
"Optimizing session context...") that render as subtle activity badges
instead of verbose reasoning blocks.
- Integrate react-markdown with remark-gfm for proper markdown rendering
in assistant messages (tables, strikethrough, autolinks, etc.).
- Improve report-block splitting and lazy-loaded ReportCard rendering.
Workspace
- Introduce @tanstack/react-table for the object table, replacing the
hand-rolled table with full column sorting, fuzzy filtering via
match-sorter-utils, row selection, and bulk actions.
- Add a new media viewer component for in-workspace image/video/PDF
preview.
- New API routes: bulk-delete entries, field management (CRUD + reorder),
raw-file serving endpoint for media assets.
- Redesign workspace sidebar, empty state, and entry detail modal with
the new theme tokens and improved layout.
Agent Runtime
- Switch web agent execution from --local to gateway-routed mode so
concurrent chat threads share the gateway's lane-based concurrency
system, eliminating cross-process file-lock contention.
- Advertise "tool-events" capability during WebSocket handshake so the
gateway streams tool start/update/result events to the UI.
- Add new agent callback hooks: onLifecycleStart, onCompactionStart/End,
and onToolUpdate for richer real-time feedback.
- Forward media URLs emitted by agent events into the chat stream.
Dependencies
- Add @tanstack/match-sorter-utils and @tanstack/react-table to the web
app.
Published as ironclaw@2026.2.10-1.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 11:17:23 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resolve a file path, trying multiple strategies:
|
|
|
|
|
* 1. Absolute path — the agent may read files from anywhere on the local machine
|
|
|
|
|
* (Photos library, Downloads, etc.), so we serve any readable absolute path.
|
|
|
|
|
* 2. Workspace-relative via safeResolvePath
|
|
|
|
|
* 3. Bare filename — search common workspace subdirectories
|
|
|
|
|
*
|
|
|
|
|
* Security note: this is a local-only dev server; it never runs in production.
|
|
|
|
|
*/
|
|
|
|
|
function resolveFile(path: string): string | null {
|
|
|
|
|
// 1. Absolute path — serve directly if it exists on disk
|
|
|
|
|
if (path.startsWith("/")) {
|
|
|
|
|
const abs = resolve(path);
|
|
|
|
|
if (existsSync(abs)) {return abs;}
|
|
|
|
|
// Fall through to workspace-relative in case the leading / is accidental
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Standard workspace-relative resolution
|
|
|
|
|
const resolved = safeResolvePath(path);
|
|
|
|
|
if (resolved) {return resolved;}
|
|
|
|
|
|
|
|
|
|
// 3. Try common subdirectories in case the path is a bare filename
|
2026-02-15 18:18:15 -08:00
|
|
|
const root = resolveWorkspaceRoot();
|
feat(web): full UI redesign with light/dark theme, TanStack data tables, media rendering, and gateway-routed agent execution
Overhaul the Dench web app with a comprehensive visual redesign and several
major feature additions across the chat interface, workspace, and agent
runtime layer.
Theme & Design System
- Replace the dark-only palette with a full light/dark theme system that
respects system preference via localStorage + inline script (no FOUC).
- Introduce new design tokens: glassmorphism surfaces, semantic colors
(success/warning/error/info), object-type chip palettes, and a tiered
shadow scale (sm/md/lg/xl).
- Add Instrument Serif + Inter via Google Fonts for a refined typographic
hierarchy; headings use the serif face, body uses Inter.
- Rebrand UI from "Ironclaw" to "Dench" across the landing page and
metadata.
Chat & Chain-of-Thought
- Rewrite the chain-of-thought component with inline media detection and
rendering — images, video, audio, and PDFs referenced in agent output
are now displayed directly in the conversation thread.
- Add status indicator parts (e.g. "Preparing response...",
"Optimizing session context...") that render as subtle activity badges
instead of verbose reasoning blocks.
- Integrate react-markdown with remark-gfm for proper markdown rendering
in assistant messages (tables, strikethrough, autolinks, etc.).
- Improve report-block splitting and lazy-loaded ReportCard rendering.
Workspace
- Introduce @tanstack/react-table for the object table, replacing the
hand-rolled table with full column sorting, fuzzy filtering via
match-sorter-utils, row selection, and bulk actions.
- Add a new media viewer component for in-workspace image/video/PDF
preview.
- New API routes: bulk-delete entries, field management (CRUD + reorder),
raw-file serving endpoint for media assets.
- Redesign workspace sidebar, empty state, and entry detail modal with
the new theme tokens and improved layout.
Agent Runtime
- Switch web agent execution from --local to gateway-routed mode so
concurrent chat threads share the gateway's lane-based concurrency
system, eliminating cross-process file-lock contention.
- Advertise "tool-events" capability during WebSocket handshake so the
gateway streams tool start/update/result events to the UI.
- Add new agent callback hooks: onLifecycleStart, onCompactionStart/End,
and onToolUpdate for richer real-time feedback.
- Forward media URLs emitted by agent events into the chat stream.
Dependencies
- Add @tanstack/match-sorter-utils and @tanstack/react-table to the web
app.
Published as ironclaw@2026.2.10-1.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 11:17:23 -08:00
|
|
|
if (!root) {return null;}
|
|
|
|
|
const rootAbs = resolve(root);
|
|
|
|
|
const basename = path.split("/").pop() ?? path;
|
|
|
|
|
if (basename === path) {
|
|
|
|
|
const subdirs = [
|
|
|
|
|
"assets",
|
|
|
|
|
"knowledge",
|
|
|
|
|
"manufacturing",
|
|
|
|
|
"uploads",
|
|
|
|
|
"files",
|
|
|
|
|
"images",
|
|
|
|
|
"media",
|
|
|
|
|
"reports",
|
|
|
|
|
"exports",
|
|
|
|
|
];
|
|
|
|
|
for (const sub of subdirs) {
|
|
|
|
|
const candidate = resolve(root, sub, basename);
|
|
|
|
|
if (
|
|
|
|
|
candidate.startsWith(rootAbs) &&
|
|
|
|
|
existsSync(candidate)
|
|
|
|
|
) {
|
|
|
|
|
return candidate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* GET /api/workspace/raw-file?path=...
|
|
|
|
|
* Serves a workspace file with the correct Content-Type for inline display.
|
|
|
|
|
* Used by the chain-of-thought component to render images, videos, and PDFs.
|
|
|
|
|
*/
|
|
|
|
|
export async function GET(req: Request) {
|
|
|
|
|
const url = new URL(req.url);
|
|
|
|
|
const path = url.searchParams.get("path");
|
|
|
|
|
|
|
|
|
|
if (!path) {
|
|
|
|
|
return new Response("Missing path", { status: 400 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const absolute = resolveFile(path);
|
|
|
|
|
if (!absolute) {
|
|
|
|
|
return new Response("Not found", { status: 404 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
|
|
|
const contentType = MIME_MAP[ext] ?? "application/octet-stream";
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const buffer = readFileSync(absolute);
|
|
|
|
|
return new Response(buffer, {
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": contentType,
|
|
|
|
|
"Cache-Control": "public, max-age=3600",
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
} catch {
|
|
|
|
|
return new Response("Read error", { status: 500 });
|
|
|
|
|
}
|
|
|
|
|
}
|