Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2d58123
feat(artifacts): add create/finalize/fail helpers + error field (#2778)
oxoxDev May 29, 2026
82e8fa9
feat(runtime_python): add run + venv helpers for Python-backed tools …
oxoxDev May 29, 2026
026fe48
feat(tools): add generate_presentation tool backed by python-pptx (#2…
oxoxDev May 29, 2026
63310f5
test(presentation): end-to-end integration + CI python-pptx provision…
oxoxDev May 29, 2026
aa1469c
feat(agent/orchestrator): add generate_presentation to named tool lis…
oxoxDev May 29, 2026
5c059f4
feat(artifacts): publish ArtifactReady/Failed + bridge to web channel…
oxoxDev May 30, 2026
12509a3
feat(tauri): add download_artifact_to_downloads command (#2779)
oxoxDev May 30, 2026
d8f92ae
feat(chat): ArtifactCard + download service + slice + i18n (#2779)
oxoxDev May 30, 2026
998c107
chore(fmt): apply pre-push auto-fixes (rustfmt + Prettier) (#2779)
oxoxDev May 30, 2026
6e09a01
test(playwright): fix insights-dashboard spec + adopt joyride scrub (…
oxoxDev May 30, 2026
3e5a943
fix(i18n): platform-neutral reveal copy in all 14 locales (#2779)
oxoxDev May 30, 2026
25eac0a
fix(chat): validate kind + cap artifact error in logs (#2779)
oxoxDev May 30, 2026
a641263
fix(artifacts): redact failure reason payloads from structured logs (…
oxoxDev May 30, 2026
8417d59
fix(chat/artifact): cap failure reason at 280 chars + Show more (#2779)
oxoxDev May 30, 2026
0e0bc99
fix(runtime_python): reject pre-release Python versions in selector (…
oxoxDev May 30, 2026
7994bed
chore(fmt): apply rustfmt to downloader_tests (#2779)
oxoxDev May 30, 2026
c93e475
fix(tauri): allow-list download_artifact_to_downloads command (#2779)
oxoxDev May 30, 2026
84e9001
fix(chat/artifact): reveal-in-folder via plugin's typed binding (#2779)
oxoxDev May 30, 2026
0ae9328
feat(chat/runtime): add removeArtifactForThread reducer (#3024)
oxoxDev May 30, 2026
3eba390
feat(store): persist artifactsByThread (ready-only) (#3024)
oxoxDev May 30, 2026
54e4a07
feat(services/artifact): add deleteArtifact RPC wrapper (#3024)
oxoxDev May 30, 2026
17dea97
feat(chat/files): ChatFilesChip + ChatFilesPanel components (#3024)
oxoxDev May 30, 2026
139f85e
feat(i18n): chat.files.* keys for files panel × 14 locales (#3024)
oxoxDev May 30, 2026
e22ae0a
feat(chat/page): mount ChatFilesChip in header + delegate ready cards…
oxoxDev May 30, 2026
5c5454d
fix(chat/files): pluralize chip aria-label by count (#3024)
oxoxDev May 30, 2026
ebf4c8d
fix(chat/files): typed error codes + double-extension guard (#3024)
oxoxDev May 30, 2026
53ed97a
test(chat/files): cover service + persist filter + slice gaps (#3024)
oxoxDev May 30, 2026
77782dd
test(chat/files): disambiguate raw vs localized error in NOT_DESKTOP …
oxoxDev May 30, 2026
999b398
fix(artifact_commands): cross-platform-safe filenames + atomic collis…
oxoxDev Jun 1, 2026
304d78e
fix(chatService): narrow artifact-event payloads by typeof, not truth…
oxoxDev Jun 1, 2026
a95ea60
Merge remote-tracking branch 'upstream/main' into feat/3024-files-in-…
oxoxDev Jun 1, 2026
602d3eb
i18n(pl): translate chat.artifact + chat.files keys for #3024 parity
oxoxDev Jun 1, 2026
5aff4f8
Merge remote-tracking branch 'upstream/main' into feat/3024-files-in-…
oxoxDev Jun 1, 2026
de6302d
Merge remote-tracking branch 'upstream/main' into pr/3026
senamakel Jun 1, 2026
8725db4
fix(tools/ops): use root_config.workspace_dir post-#3074 refactor
oxoxDev Jun 2, 2026
d519c9a
Merge remote-tracking branch 'upstream/main' into feat/3024-files-in-…
oxoxDev Jun 2, 2026
7e4b32c
feat(artifacts): bind workspace_dir to ArtifactReady/Failed events (#…
oxoxDev Jun 2, 2026
990406a
test(chat): cover ArtifactCard render + download/retry flows (#3024)
oxoxDev Jun 2, 2026
eb73ef3
test(chat): fire ChatRuntimeProvider artifact handlers end-to-end (#3…
oxoxDev Jun 2, 2026
8ff838b
test(chat): cover artifact_failed happy path + envelope skip (#3024)
oxoxDev Jun 2, 2026
f2b1244
Merge remote-tracking branch 'upstream/main' into feat/3024-files-in-…
oxoxDev Jun 2, 2026
6fc024e
Merge remote-tracking branch 'upstream/main' into feat/3024-files-in-…
oxoxDev Jun 2, 2026
dfabc36
test(chatRuntime): pin clear-semantics contract — artifacts preserved…
oxoxDev Jun 2, 2026
f716e24
test(chatService.artifactEvents): add workspace_dir to artifact event…
oxoxDev Jun 2, 2026
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
76 changes: 76 additions & 0 deletions app/src/components/chat/ChatFilesChip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useMemo, useState } from 'react';

import { useT } from '../../lib/i18n/I18nContext';
import { useAppSelector } from '../../store/hooks';
import ChatFilesPanel from './ChatFilesPanel';

/**
* Header chip surfacing the count of ready artifacts for a thread (#3024).
*
* - Hidden when the count is zero (no chrome cost on chats that never
* generated a file).
* - Click opens the {@link ChatFilesPanel} popover; clicking again or
* pressing Esc closes it (panel owns Esc + click-outside).
*
* Reads from the persisted `chatRuntime.artifactsByThread` slice — entries
* survive app restarts via the `artifactsReadyOnlyTransform` configured in
* `store/index.ts`.
*/
export interface ChatFilesChipProps {
threadId: string;
}

export default function ChatFilesChip({ threadId }: ChatFilesChipProps) {
const { t } = useT();
const [open, setOpen] = useState(false);
const artifactsByThread = useAppSelector(state => state.chatRuntime.artifactsByThread);
const allForThread = artifactsByThread[threadId] ?? [];
// Only ready artifacts are listable — in_progress / failed live above the
// composer until they resolve.
const readyArtifacts = useMemo(
() => allForThread.filter(a => a.status === 'ready'),
[allForThread]
);
const count = readyArtifacts.length;

if (count === 0) return null;

return (
<div className="relative inline-block">
<button
type="button"
onClick={() => setOpen(prev => !prev)}
aria-haspopup="dialog"
aria-expanded={open}
aria-label={t(
count === 1 ? 'chat.files.chip.aria.one' : 'chat.files.chip.aria.other'
).replace('{count}', String(count))}
data-testid="chat-files-chip"
className="h-7 inline-flex items-center gap-1.5 rounded-lg border border-stone-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 hover:bg-stone-50 dark:hover:bg-neutral-800/60 text-xs font-medium text-stone-600 dark:text-neutral-300 transition-colors px-2">
<svg
aria-hidden="true"
className="w-3.5 h-3.5"
fill="none"
stroke="currentColor"
strokeWidth={1.8}
viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.49"
/>
</svg>
<span data-testid="chat-files-chip-count" className="font-mono leading-none">
{count}
</span>
</button>
{open && (
<ChatFilesPanel
threadId={threadId}
artifacts={readyArtifacts}
onClose={() => setOpen(false)}
/>
)}
</div>
);
}
Loading
Loading