sitesmith: null-safe esc() across all toHtml + WorkingIndicator

Real-world AI output frequently sends mismatched prop names (e.g.
items vs features, cta object vs buttonText/Href). The toHtml functions
of section/form/sections-folder components each defined a local
esc = (s: string) => s.replace(...) that crashed when called with
undefined, taking the auto-save export with it.

Patched every local esc() to coerce non-strings:
  const esc = (s: any) => String(s ?? "").replace(...)
17 files touched; behavior unchanged for valid string inputs.

Also adds a WorkingIndicator (Claude Code-style spinner + rotating
phrase + elapsed seconds) shown in the modal footer while a generation
is in flight, replacing the disabled "Thinking..." placeholder.
This commit is contained in:
2026-05-24 15:54:48 -07:00
parent ac0347ae5f
commit 069ea1235a
19 changed files with 106 additions and 22 deletions
+10 -5
View File
@@ -8,6 +8,7 @@ import { UpgradeBanner } from './UpgradeBanner';
import { ScopeConfirmDialog } from './ScopeConfirmDialog';
import { MessageList } from './MessageList';
import { ChatInput } from './ChatInput';
import { WorkingIndicator } from './WorkingIndicator';
import { SitesmithResponse } from '../../types/sitesmith';
interface Props { onClose: () => void; }
@@ -76,11 +77,15 @@ export const SitesmithModal: React.FC<Props> = ({ onClose }) => {
: <MessageList messages={messages} />}
</div>
<div style={footer}>
<ChatInput
disabled={!canChat || busy}
placeholder={!canChat ? 'Upgrade your plan to use Sitesmith' : busy ? 'Thinking…' : 'Describe what you want…'}
onSend={handleSend}
/>
{busy ? (
<WorkingIndicator />
) : (
<ChatInput
disabled={!canChat}
placeholder={!canChat ? 'Upgrade your plan to use Sitesmith' : 'Describe what you want…'}
onSend={handleSend}
/>
)}
</div>
<ScopeConfirmDialog
open={!!pendingReplace}