misc: UI improvements and docs updates#345
Conversation
- Add General section to agent config page with display_name and role fields - Show agent display name alongside ID in the page header - Add changelog section to settings with per-release card layout - Add rehype-raw for HTML-in-markdown rendering - Add changelog API endpoint serving embedded CHANGELOG.md - Document environment variables and dev setup in docs - Add projects design document
WalkthroughThe pull request adds changelog functionality across frontend and backend, introduces a comprehensive Projects feature design document, and updates user-facing documentation with environment variables and development setup guidance. Changes span documentation, API endpoints, React components, and utility functions without modifying core runtime behavior. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| <div className={className ? `markdown ${className}` : "markdown"}> | ||
| <ReactMarkdown | ||
| remarkPlugins={[remarkGfm]} | ||
| rehypePlugins={[rehypeRaw]} |
There was a problem hiding this comment.
Enabling rehypeRaw here makes all Markdown render raw HTML. This component is used for chat/task content too, so if any of that is user-controlled this becomes an XSS footgun.
Consider gating raw HTML to trusted sources only (e.g. changelog/docs), or pairing rehype-raw with a sanitizer/allowlist.
| useEffect(() => { | ||
| onDirtyChange(localDirty); | ||
| }, [localDirty, onDirtyChange]); | ||
|
|
||
| const handleSave = useCallback(() => { | ||
| onSave({ display_name: localDisplayName, role: localRole }); | ||
| setLocalDirty(false); | ||
| }, [onSave, localDisplayName, localRole]); |
There was a problem hiding this comment.
handleSave clears localDirty, which immediately triggers the sync useEffect above and can overwrite the inputs with the previous displayName/role before the mutation refetch completes (and it also drops the edits on save failure).
One approach is to keep localDirty true until the props catch up to the saved values:
| useEffect(() => { | |
| onDirtyChange(localDirty); | |
| }, [localDirty, onDirtyChange]); | |
| const handleSave = useCallback(() => { | |
| onSave({ display_name: localDisplayName, role: localRole }); | |
| setLocalDirty(false); | |
| }, [onSave, localDisplayName, localRole]); | |
| useEffect(() => { | |
| onDirtyChange(localDirty); | |
| }, [localDirty, onDirtyChange]); | |
| useEffect(() => { | |
| if (localDirty && localDisplayName === displayName && localRole === role) { | |
| setLocalDirty(false); | |
| } | |
| }, [localDirty, localDisplayName, localRole, displayName, role]); | |
| const handleSave = useCallback(() => { | |
| onSave({ display_name: localDisplayName, role: localRole }); | |
| }, [onSave, localDisplayName, localRole]); |
| } | ||
|
|
||
| function ChangelogSection() { | ||
| const { data: changelog, isLoading } = useQuery<string>({ |
There was a problem hiding this comment.
Minor UX: if api.changelog fails, this currently falls through to the same "No changelog available" empty state. Might be worth surfacing an explicit error state so users can distinguish "failed to load" vs "no releases parsed".
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
docs/design-docs/projects.md (2)
13-21: Add language specifier to fenced code block.The directory structure code block should have a language specifier for proper rendering.
📝 Proposed fix
-``` +```text ~/Projects/spacebot/ ← project root ├── spacebot/ ← repo (core agent)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/design-docs/projects.md` around lines 13 - 21, The fenced code block showing the directory tree lacks a language specifier which can break syntax highlighting; update the fenced block in docs/design-docs/projects.md by adding a language tag (e.g., "text") immediately after the opening triple backticks for the directory structure block so it reads ```text and leave the content unchanged.
309-321: Add language specifier to tool definition code block.Consider using
yamlortextfor the tool schema definition.📝 Proposed fix
-``` +```yaml project_manage: action: "create" | "scan" | "add_repo" | "create_worktree" | "remove_worktree" | "list" | "disk_usage"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/design-docs/projects.md` around lines 309 - 321, The YAML-like tool schema block for "project_manage" lacks a language specifier; update the fenced code block that defines project_manage (the block starting with project_manage: and listing action, project_id, name, etc.) to add a language tag (e.g., ```yaml) so syntax highlighting and tooling recognize it as YAML; ensure only the opening fence is changed to include the language and leave the schema contents unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@interface/src/components/Markdown.tsx`:
- Line 3: The Markdown component currently imports and uses rehype-raw which
allows rendering unsanitized HTML; update the component by importing
rehype-sanitize (e.g. rehypeSanitize) and include it in the rehype pipeline
immediately after rehypeRaw so raw HTML is passed through a sanitizer; modify
the sanitization options if needed to explicitly allow safe tags/attributes for
your use cases, ensuring the component (Markdown) uses [rehypeRaw,
rehypeSanitize] order when building remark/rehype processors.
---
Nitpick comments:
In `@docs/design-docs/projects.md`:
- Around line 13-21: The fenced code block showing the directory tree lacks a
language specifier which can break syntax highlighting; update the fenced block
in docs/design-docs/projects.md by adding a language tag (e.g., "text")
immediately after the opening triple backticks for the directory structure block
so it reads ```text and leave the content unchanged.
- Around line 309-321: The YAML-like tool schema block for "project_manage"
lacks a language specifier; update the fenced code block that defines
project_manage (the block starting with project_manage: and listing action,
project_id, name, etc.) to add a language tag (e.g., ```yaml) so syntax
highlighting and tooling recognize it as YAML; ensure only the opening fence is
changed to include the language and leave the schema contents unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0b2e4d0a-4c3b-4f5f-8ad4-ddd1c261d56b
⛔ Files ignored due to path filters (2)
interface/bun.lockis excluded by!**/*.lock,!**/*.lockinterface/package.jsonis excluded by!**/*.json
📒 Files selected for processing (11)
docs/content/docs/(configuration)/config.mdxdocs/content/docs/(getting-started)/quickstart.mdxdocs/design-docs/projects.mdinterface/src/api/client.tsinterface/src/components/Markdown.tsxinterface/src/router.tsxinterface/src/routes/AgentConfig.tsxinterface/src/routes/Settings.tsxsrc/api/server.rssrc/api/settings.rssrc/self_awareness.rs
| @@ -1,5 +1,6 @@ | |||
| import ReactMarkdown from "react-markdown"; | |||
| import remarkGfm from "remark-gfm"; | |||
| import rehypeRaw from "rehype-raw"; | |||
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
rehype-sanitize npm package usage with rehype-raw
💡 Result:
To safely use rehype-raw (which turns embedded/raw HTML into real nodes), run rehype-sanitize after rehype-raw to drop anything not explicitly allowed by the sanitizer schema. This is the recommended pattern because enabling raw HTML is “dangerous” unless you sanitize. [1] [2]
Unified / remark pipeline (Markdown → HAST → HTML)
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeRaw from 'rehype-raw'
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'
const file = await unified()
.use(remarkParse)
// keep raw HTML from markdown as "raw" nodes (dangerous unless you sanitize later)
.use(remarkRehype, { allowDangerousHtml: true })
// parse those raw nodes into real HTML AST nodes
.use(rehypeRaw)
// sanitize the resulting tree (defaults are fairly strict)
.use(rehypeSanitize, defaultSchema)
.use(rehypeStringify)
.process(markdown)Order and the allowDangerousHtml + rehype-raw + rehype-sanitize combo are shown in the remark-rehype docs, and rehype-raw explicitly warns to sanitize if you combine it with user content. [1] [2]
Allowing extra attributes/classes (common need)
If you need to allow specific things (e.g., className for syntax highlighting), extend defaultSchema and pass it to rehype-sanitize. [3]
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'
const schema = {
...defaultSchema,
attributes: {
...defaultSchema.attributes,
code: [...(defaultSchema.attributes.code || []), ['className']],
span: [...(defaultSchema.attributes.span || []), ['className']],
},
}Key rule: anything that creates/rehydrates HTML nodes (rehype-raw) should happen before sanitization; anything that adds risky nodes/attrs should be assumed unsafe unless your schema allows it. [1] [2] [3]
Sources:
[1] remark-rehype example showing allowDangerousHtml: true → rehype-raw → rehype-sanitize [1]
[2] rehype-raw docs warning to sanitize when used with user content [2]
[3] rehype-sanitize docs (schema-based allowlist; defaultSchema) [3]
🏁 Script executed:
# First, locate and read Markdown.tsx
find . -name "Markdown.tsx" -type fRepository: spacedriveapp/spacebot
Length of output: 105
🏁 Script executed:
# Search for rehypeSanitize usage in the codebase
rg "rehypeSanitize" --type ts --type tsxRepository: spacedriveapp/spacebot
Length of output: 93
🏁 Script executed:
# Check the current state of Markdown.tsx including the imports and rehypePlugins usage
find . -name "Markdown.tsx" -type f -exec wc -l {} \;Repository: spacedriveapp/spacebot
Length of output: 108
🏁 Script executed:
# Search for rehypeSanitize usage - try without file type flag
rg "rehypeSanitize"Repository: spacedriveapp/spacebot
Length of output: 48
🏁 Script executed:
# Read the Markdown.tsx file to see current implementation
cat -n ./interface/src/components/Markdown.tsxRepository: spacedriveapp/spacebot
Length of output: 882
🏁 Script executed:
# Check usage of Markdown component in the codebase
rg "Markdown" ./interface/src/components/ -A 2 -B 2 | head -100Repository: spacedriveapp/spacebot
Length of output: 3293
🏁 Script executed:
# Search for specific components that use Markdown
rg "CortexChatPanel|WebChatPanel|ChannelDetail|AgentWorkers" ./interface/src/ -lRepository: spacedriveapp/spacebot
Length of output: 381
🏁 Script executed:
# Check ChannelDetail.tsx for Markdown usage
rg "Markdown" ./interface/src/routes/ChannelDetail.tsx -B 3 -A 3Repository: spacedriveapp/spacebot
Length of output: 1343
🏁 Script executed:
# Check AgentWorkers.tsx for Markdown usage
rg "Markdown" ./interface/src/routes/AgentWorkers.tsx -B 3 -A 3Repository: spacedriveapp/spacebot
Length of output: 748
🏁 Script executed:
# Check what content sources are being passed to Markdown in WebChatPanel
sed -n '1,150p' ./interface/src/components/WebChatPanel.tsx | cat -nRepository: spacedriveapp/spacebot
Length of output: 6912
XSS vulnerability: rehype-raw renders unsanitized HTML from untrusted sources.
The Markdown component is used to render content from multiple sources including external chat messages (Discord/Slack/Telegram), worker execution results, and assistant responses. Enabling rehype-raw without sanitization allows malicious HTML like <script>, <img onerror="...">, or event handlers to execute in the browser.
Add rehype-sanitize after rehype-raw to allowlist safe HTML tags:
🛡️ Proposed fix: Add rehype-sanitize
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
+import rehypeSanitize from "rehype-sanitize";
export function Markdown({
children,
className,
}: {
children: string;
className?: string;
}) {
return (
<div className={className ? `markdown ${className}` : "markdown"}>
<ReactMarkdown
remarkPlugins={[remarkGfm]}
- rehypePlugins={[rehypeRaw]}
+ rehypePlugins={[rehypeRaw, rehypeSanitize]}
components={{🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@interface/src/components/Markdown.tsx` at line 3, The Markdown component
currently imports and uses rehype-raw which allows rendering unsanitized HTML;
update the component by importing rehype-sanitize (e.g. rehypeSanitize) and
include it in the rehype pipeline immediately after rehypeRaw so raw HTML is
passed through a sanitizer; modify the sanitization options if needed to
explicitly allow safe tags/attributes for your use cases, ensuring the component
(Markdown) uses [rehypeRaw, rehypeSanitize] order when building remark/rehype
processors.
misc: UI improvements and docs updates
Summary
/changelogAPI endpoint with embedded contentrehype-rawplugin so HTML tags in markdown content render correctlyNote
Changes Overview
This PR brings UI improvements and comprehensive documentation updates. The frontend now exposes agent configuration (display name and role) that was previously only editable through the topology graph. The header consistently shows the agent's display name alongside its ID across all routes. A new changelog view in settings displays release history parsed from CHANGELOG.md.
Documentation expanded with environment variable reference (SPACEBOT_DIR, SPACEBOT_DEPLOYMENT, etc.), a dev setup guide for running isolated instances side-by-side, and a detailed design document for the upcoming Projects feature. The markdown renderer now supports raw HTML via rehype-raw, enabling richer content rendering.
Key files: agent config UI (GeneralEditor), header rendering, changelog parsing, markdown components, API changelog endpoint, and 600+ lines of Projects design documentation.
Written by Tembo for commit 4488ae5. This will update automatically on new commits.