Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions kits/slides/app/src/components/ChatPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ function turnsToMessages(turns) {
return m;
}

export function ChatColumn({ deckId, seed, title, copilotBuilding, collapsed, onToggle, onDeckMaybeChanged,
onSessionStarted, width }) {
export function ChatColumn({ deckId, seed, template, title, copilotBuilding, collapsed, onToggle,
onDeckMaybeChanged, onSessionStarted, width }) {
const [messages, setMessages] = useState([]);
const [draft, setDraft] = useState('');
const [attaching, setAttaching] = useState(false);
Expand Down
34 changes: 19 additions & 15 deletions kits/slides/app/src/pages/DeckPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,13 @@ export function DeckPage({ id: routeId, seed, template }) {
exporting={exporting} peers={collab.peers} live={collab.live}
saveState={saveState} copilotBusy={copilotBusy} />
<div className="sl-body">
{/* No deck yet: the skeleton IS the body. Rendering it inside the canvas left
the real rail sitting empty beside it — two rails and a white gutter. */}
{!deck && (!noDeck || noDeck.working) ? (
<SkeletonDeck note={noDeck?.working
? (tplName ? `Designing your deck in ${tplName}…` : 'Designing your deck…')
: 'Loading deck…'} />
) : (<>
<aside className="sl-rail scroll" ref={railRef}>
{slides.map((s, i) => (
<button key={s.id} className={'sl-rail-item' + (i === sel ? ' active' : '')}
Expand All @@ -296,32 +303,29 @@ export function DeckPage({ id: routeId, seed, template }) {
onDragState={(elId, frame) => collab.setDrag(elId, frame, slide.id)}
peers={collab.peers}
/>
) : deck ? (
<div className="empty-note" style={{ paddingTop: 80 }}>This deck has no slides.</div>
) : (
deck ? (
<div className="empty-note" style={{ paddingTop: 80 }}>This deck has no slides.</div>
) : noDeck && !noDeck.working ? (
<div className="empty-note" style={{ paddingTop: 80 }}>
<div>{noDeck.text}</div>
{noDeck.detail && <pre className="deck-empty-detail">{noDeck.detail}</pre>}
<div style={{ marginTop: 14 }}>
Ask the copilot again on the right — the conversation is still here.
</div>
// The only case left here: a turn finished without writing a deck. Its own last
// words are the only honest explanation we have.
<div className="empty-note" style={{ paddingTop: 80 }}>
<div>{noDeck?.text}</div>
{noDeck?.detail && <pre className="deck-empty-detail">{noDeck.detail}</pre>}
<div style={{ marginTop: 14 }}>
Ask the copilot again on the right — the conversation is still here.
</div>
) : (
// Still coming: show the shape of a deck rather than a sentence in an empty pane.
<SkeletonDeck note={noDeck?.working
? (tplName ? `Designing your deck in ${tplName}…` : 'Designing your deck…')
: 'Loading deck…'} />
)
</div>
)}
</div>
</>)}
</div>
</div>

<PaneResizer pane={chatPane} />
<ChatColumn
deckId={id}
seed={seed}
template={template}
title={deck?.meta?.title || 'Copilot'}
copilotBuilding={copilotBusy}
collapsed={false}
Expand Down
11 changes: 2 additions & 9 deletions kits/slides/app/src/pages/Landing.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,6 @@ function ViewToggle({ value, onChange }) {
const AV_COLORS = ['#4F46E5', '#7C3AED', '#DB2777', '#D97706', '#059669', '#2563EB'];
function colorFor(id) { let h = 0; for (const c of String(id)) h = (h * 31 + c.charCodeAt(0)) >>> 0; return AV_COLORS[h % AV_COLORS.length]; }
function initialOf(n) { return String(n || '?').trim().charAt(0).toUpperCase() || '?'; }
function AccessStack({ me }) {
if (!me?.id) return <span className="gt-last">—</span>;
const name = me.display_name || me.name || me.email || 'You';
return <span className="acc-stack"><span className="acc-av" style={{ background: colorFor(me.id) }} title={`${name} (you)`}>{initialOf(name)}</span></span>;
}

const VIEW_KEY = 'slides.decks.view';

export function LandingPage() {
Expand Down Expand Up @@ -298,7 +292,7 @@ export function LandingPage() {
)}
</div>
{decks === null ? (
<div className="gtable-wrap"><table className="gtable"><thead><tr><th>Name</th><th className="col-last">Last viewed</th><th className="col-access">Access</th><th className="gt-actions" /></tr></thead><tbody><SkeletonTableRows count={4} /></tbody></table></div>
<div className="gtable-wrap"><table className="gtable"><thead><tr><th>Name</th><th className="col-last">Last viewed</th><th className="gt-actions" /></tr></thead><tbody><SkeletonTableRows count={4} /></tbody></table></div>
) : decks.length === 0 ? (
<div className="card empty-note">No slides yet. Create a deck from the prompt above or pick a template.</div>
) : deckMatches.length === 0 ? (
Expand All @@ -318,13 +312,12 @@ export function LandingPage() {
</div>
) : (
<div className="gtable-wrap"><table className="gtable">
<thead><tr><th>Name</th><th className="col-last">Last viewed</th><th className="col-access">Access</th><th className="gt-actions" /></tr></thead>
<thead><tr><th>Name</th><th className="col-last">Last viewed</th><th className="gt-actions" /></tr></thead>
<tbody>
{deckMatches.map((d) => (
<tr key={d.id} tabIndex={0} onClick={() => { if (editingId !== d.id) openDeck(d.id); }} onKeyDown={(e) => { if (e.key === 'Enter' && editingId !== d.id) openDeck(d.id); }}>
<td><span className="gt-name"><span className="gt-thumb"><DeckThumb id={d.id} /></span>{editingId === d.id ? renameField(d) : <span className="gcard-name" title={d.name}>{d.name}</span>}</span></td>
<td className="gt-last col-last">{relativeTime(viewed[d.id]) || '—'}</td>
<td className="col-access"><AccessStack me={me} /></td>
<td className="gt-actions">{rowActions(d)}</td>
</tr>
))}
Expand Down
Loading