From 9961d1e08039a07423fe1149b3d6deeb9e891557 Mon Sep 17 00:00:00 2001 From: Pier-Jean Malandrino Date: Sat, 11 Apr 2026 10:17:12 +0200 Subject: [PATCH] feat(#160): promote Ingest to 4th Studio mode after Prepare MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Ingest as a dedicated mode tab in the Studio pipeline (Configure → Verify → Prepare → Ingest). Create IngestPanel component in features/ingestion/ui/ with summary, stepper, and action button. Remove orphan Ingest button and inline stepper from StudioPage. Remove auto-ingest on analysis complete. Closes #160 --- frontend/src/features/ingestion/index.ts | 1 + .../src/features/ingestion/ui/IngestPanel.vue | 346 ++++++++++++++++++ frontend/src/pages/StudioPage.vue | 196 ++-------- frontend/src/shared/i18n.ts | 8 + 4 files changed, 387 insertions(+), 164 deletions(-) create mode 100644 frontend/src/features/ingestion/ui/IngestPanel.vue diff --git a/frontend/src/features/ingestion/index.ts b/frontend/src/features/ingestion/index.ts index 95916a3..fdedec3 100644 --- a/frontend/src/features/ingestion/index.ts +++ b/frontend/src/features/ingestion/index.ts @@ -1 +1,2 @@ export { useIngestionStore } from './store' +export { default as IngestPanel } from './ui/IngestPanel.vue' diff --git a/frontend/src/features/ingestion/ui/IngestPanel.vue b/frontend/src/features/ingestion/ui/IngestPanel.vue new file mode 100644 index 0000000..d2f55ae --- /dev/null +++ b/frontend/src/features/ingestion/ui/IngestPanel.vue @@ -0,0 +1,346 @@ + + + + + diff --git a/frontend/src/pages/StudioPage.vue b/frontend/src/pages/StudioPage.vue index 16d4c8e..3d6f24d 100644 --- a/frontend/src/pages/StudioPage.vue +++ b/frontend/src/pages/StudioPage.vue @@ -66,6 +66,24 @@ {{ t('studio.prepare') }} +
@@ -94,64 +112,6 @@ {{ analysisStore.running ? t('studio.analyzing') : t('studio.run') }} - -
- - - -
-
- - {{ t('ingestion.stepEmbedding') }} -
-
-
- - {{ t('ingestion.stepIndexing') }} -
-
-
- - {{ t('ingestion.stepDone') }}
@@ -507,6 +467,15 @@ @rechunked="onRechunked" />
+ + +
+ +
@@ -522,6 +491,7 @@ import { DocumentUpload, DocumentList } from '../features/document/index' import { ResultTabs } from '../features/analysis/index' import BboxOverlay from '../features/analysis/ui/BboxOverlay.vue' import { ChunkPanel } from '../features/chunking' +import { IngestPanel } from '../features/ingestion' import { useFeatureFlag } from '../features/feature-flags' import { getPreviewUrl } from '../features/document/api' import { useI18n } from '../shared/i18n' @@ -632,11 +602,6 @@ async function runAnalysis() { await analysisStore.run(documentStore.selectedId, { ...pipelineOptions }) } -async function runIngestion() { - if (!analysisStore.currentAnalysis?.id) return - await ingestionStore.ingest(analysisStore.currentAnalysis.id) -} - function addMore() { documentStore.selectedId = null } @@ -658,22 +623,12 @@ watch(currentPage, () => { }) // Auto-switch to verify when analysis completes + refresh document data (pageCount) -// Auto-trigger ingestion if pipeline is available (#81) watch( () => analysisStore.currentAnalysis?.status, - async (status) => { + (status) => { if (status === 'COMPLETED') { mode.value = 'verify' documentStore.load() - - // Auto-ingest if chunks are available and pipeline is configured - if ( - ingestionStore.available && - analysisStore.currentAnalysis?.chunksJson && - analysisStore.currentAnalysis?.id - ) { - await ingestionStore.ingest(analysisStore.currentAnalysis.id) - } } }, ) @@ -895,21 +850,6 @@ onBeforeUnmount(() => { cursor: not-allowed; } -.topbar-btn.ingest { - background: var(--success); - border-color: var(--success); - color: white; -} - -.topbar-btn.ingest:hover:not(:disabled) { - filter: brightness(1.1); -} - -.topbar-btn.ingest:disabled { - opacity: 0.6; - cursor: not-allowed; -} - .topbar-btn .btn-icon { width: 16px; height: 16px; @@ -924,79 +864,6 @@ onBeforeUnmount(() => { animation: spin 0.6s linear infinite; } -/* Ingestion stepper */ -.ingestion-stepper { - display: flex; - align-items: center; - justify-content: center; - gap: 0; - padding: 8px 20px; - background: var(--bg-surface); - border-bottom: 1px solid var(--border); -} - -.step { - display: flex; - align-items: center; - gap: 6px; - padding: 0 8px; -} - -.step-dot { - width: 10px; - height: 10px; - border-radius: 50%; - background: var(--border); - transition: all 0.3s ease; -} - -.step.active .step-dot { - background: var(--accent); - box-shadow: 0 0 6px var(--accent); - animation: pulse-dot 1s ease-in-out infinite; -} - -.step.done .step-dot { - background: var(--success, #22c55e); -} - -.step-label { - font-size: 12px; - color: var(--text-muted); - font-weight: 500; -} - -.step.active .step-label { - color: var(--accent); -} - -.step.done .step-label { - color: var(--success, #22c55e); -} - -.step-line { - width: 40px; - height: 2px; - background: var(--border); - transition: background 0.3s ease; -} - -.step-line.done { - background: var(--success, #22c55e); -} - -@keyframes pulse-dot { - 0%, - 100% { - transform: scale(1); - opacity: 1; - } - 50% { - transform: scale(1.3); - opacity: 0.7; - } -} - /* Doc info bar */ .doc-infobar { display: flex; @@ -1541,9 +1408,10 @@ onBeforeUnmount(() => { padding-top: 16px; } -/* Verify panel */ +/* Verify / Prepare / Ingest panels */ .verify-panel, -.prepare-panel { +.prepare-panel, +.ingest-panel-wrapper { height: 100%; overflow: hidden; display: flex; diff --git a/frontend/src/shared/i18n.ts b/frontend/src/shared/i18n.ts index f0c5c46..2c7ff81 100644 --- a/frontend/src/shared/i18n.ts +++ b/frontend/src/shared/i18n.ts @@ -109,6 +109,7 @@ const messages: Messages = { // Chunking 'studio.prepare': 'Préparer', + 'studio.ingest': 'Ingérer', 'chunking.settings': 'Chunking', 'chunking.chunkerType': 'Type de chunker', 'chunking.maxTokens': 'Tokens max', @@ -137,6 +138,9 @@ const messages: Messages = { // Ingestion / My Documents 'ingestion.ingest': 'Ingérer', + 'ingestion.document': 'Document', + 'ingestion.chunkCount': 'Chunks prêts', + 'ingestion.successMessage': 'Indexation terminée avec succès !', 'ingestion.ingesting': 'Ingestion...', 'ingestion.reindex': 'Ré-indexer', 'ingestion.indexed': 'Indexé', @@ -274,6 +278,7 @@ const messages: Messages = { 'history.open': 'Open', 'studio.prepare': 'Prepare', + 'studio.ingest': 'Ingest', 'chunking.settings': 'Chunking', 'chunking.chunkerType': 'Chunker type', 'chunking.maxTokens': 'Max tokens', @@ -300,6 +305,9 @@ const messages: Messages = { 'search.hint': 'Enter a term to search through indexed chunks.', 'ingestion.ingest': 'Ingest', + 'ingestion.document': 'Document', + 'ingestion.chunkCount': 'Chunks ready', + 'ingestion.successMessage': 'Indexing completed successfully!', 'ingestion.ingesting': 'Ingesting...', 'ingestion.reindex': 'Re-index', 'ingestion.indexed': 'Indexed',