Skip to content
Open
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
34 changes: 30 additions & 4 deletions src/kiro_crew/docs/ui-index.generated.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion website/src/pages/knowledge/SourcesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { Upload, FolderSync, FolderOpen, X, RefreshCw, AlertCircle, CheckCircle, ChevronDown, ChevronRight, Pause, Play, Pencil, Check, Coins } from 'lucide-react'
import { Badge, EmptyState, ContentSkeleton } from '../../components/ui'
import Clickable from '../../components/Clickable'
import { uiLocation } from '../../uiLocations/uiLocation'
import { knowledgeApi } from './api'
import { formatRelativeDate, FALLBACK_SUPPORTED_FORMATS } from './helpers'
import { parseSourceProps, shouldShowWordCount, syncStatusLabel } from './knowledgeUtils'
Expand Down Expand Up @@ -443,7 +444,7 @@ export default function SourcesList({ onIngest, uploadNamespace, setUploadNamesp
<span>{i18nT('pages.knowledge.sourcesList.indexing_uses_kiro_requests_each_source_costs_mo')}</span>
</div>
<div className="flex justify-end">
<button onClick={() => setShowAdd(true)} className="px-3 py-1.5 text-[13px] bg-accent text-accent-fg rounded-md hover:bg-accent/80">{i18nT('pages.knowledge.sourcesList.add_source')}</button>
<button onClick={() => setShowAdd(true)} className="px-3 py-1.5 text-[13px] bg-accent text-accent-fg rounded-md hover:bg-accent/80" {...uiLocation('knowledge.add-source')}>{i18nT('pages.knowledge.sourcesList.add_source')}</button>
</div>

{showAdd && (
Expand Down
29 changes: 23 additions & 6 deletions website/src/pages/knowledge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const STATUS_LABEL_KEY = {
} as const

import { i18nT } from '../../i18n/t'
import { uiLocation } from '../../uiLocations/uiLocation'
import { GuideRevealScope } from '../../guide/GuideRevealScope'
import { useImeGuard } from '../../hooks/useImeGuard'
import { editableEventTarget } from '../../utils/editableTarget'
const KnowledgeGraph = lazy(() => import('./KnowledgeGraph'))
Expand Down Expand Up @@ -565,12 +567,22 @@ export default function KnowledgePage({ embedded = false }: { embedded?: boolean
{/* Tabs — horizontally scrollable on narrow viewports so the active
underline never spills past the container. */}
<div className="flex gap-1 px-4 md:px-6 border-b border-border overflow-x-auto">
{TABS.map(t => (
<button key={t} onClick={() => { setTab(t); setSelectedId(null); setSelectedItems(new Set()) }}
className={`flex items-center gap-1.5 px-3 py-2 text-[13px] font-medium border-b-2 transition-all bg-transparent cursor-pointer shrink-0 whitespace-nowrap ${tab === t ? 'border-accent text-text font-semibold' : 'border-transparent text-muted hover:text-text'}`}>
{TAB_ICON[t]} {i18nT(TAB_LABEL_KEY[t])}
</button>
))}
{TABS.map(t => {
const onClick = () => { setTab(t); setSelectedId(null); setSelectedItems(new Set()) }
const className = `flex items-center gap-1.5 px-3 py-2 text-[13px] font-medium border-b-2 transition-all bg-transparent cursor-pointer shrink-0 whitespace-nowrap ${tab === t ? 'border-accent text-text font-semibold' : 'border-transparent text-muted hover:text-text'}`
// Sources is registered with find_ui (uiLocations/areas/capabilities.ts):
// a registered row needs its own branch, with its label key written
// out, so the index can read the label from this render site.
return t === 'sources' ? (
<button key={t} onClick={onClick} className={className} {...uiLocation('knowledge.sources-tab')}>
<FolderSync size={14} /> {i18nT('pages.knowledge.index.sources_tab')}
</button>
) : (
<button key={t} onClick={onClick} className={className}>
{TAB_ICON[t]} {i18nT(TAB_LABEL_KEY[t])}
</button>
)
})}
{embedded && (
<span className="ml-auto self-center shrink-0 pb-0.5">
<Btn onClick={() => setShowHelp(true)}><HelpCircle size={14} /> {i18nT('pages.knowledge.index.help')}</Btn>
Expand All @@ -580,6 +592,10 @@ export default function KnowledgePage({ embedded = false }: { embedded?: boolean

<div className={`flex-1 px-4 md:px-6 py-4 min-h-0 ${tab === 'graph' ? 'flex flex-col' : 'overflow-y-auto'}`} ref={listContainerRef}>
<EmbeddingStatus />
{/* Owns the find_ui reveal scope for the Sources view, outside the
conditional so it can report "closed" while SourcesList is
unmounted (same pattern as ConnectionsPage's MCP Servers tab). */}
<GuideRevealScope id="open:knowledge.sources-tab" open={tab === 'sources'}>
{isEmpty && tab === 'list' ? (
<>
{listFilterBar}
Expand Down Expand Up @@ -689,6 +705,7 @@ export default function KnowledgePage({ embedded = false }: { embedded?: boolean
) : (
<SourcesList onIngest={handleFiles} uploadNamespace={uploadNamespace} setUploadNamespace={setUploadNamespace} namespaces={namespaces} ingestionJobs={ingestionJobs} uploadAccept={uploadAccept} supportedFormatsDisplay={supportedFormatsDisplay} acceptsNoExtension={acceptsNoExtension} />
)}
</GuideRevealScope>
</div>

{/* Stats bar */}
Expand Down
17 changes: 16 additions & 1 deletion website/src/test/KnowledgeEmbedded.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
* strip. Standalone mode keeps the original chrome byte-for-byte.
*/
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen } from '@testing-library/react'
import { render, screen, fireEvent } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { MemoryRouter } from 'react-router-dom'
import { scopeOpen } from '../guide/liveRegistry'

const mockKnowledgeApi = vi.fn()
vi.mock('../pages/knowledge/api', () => ({
Expand Down Expand Up @@ -56,3 +57,17 @@ describe('KnowledgePage — embedded mode', () => {
expect(screen.getByRole('button', { name: /help/i })).toBeInTheDocument()
})
})

describe('KnowledgePage — find_ui reveal scope for the Sources view', () => {
// The find_ui guide to Add Source has a step whose scope is
// `open:knowledge.sources-tab`. The page owns that scope, so the step can
// tell "Sources is open" from "Add Source has not loaded yet".
it('reports closed on the list view, open on Sources, and drops the report on unmount', () => {
const { unmount } = mount(<KnowledgePage embedded />)
expect(scopeOpen('open:knowledge.sources-tab')).toBe(false)
fireEvent.click(screen.getByRole('button', { name: /^sources$/i }))
expect(scopeOpen('open:knowledge.sources-tab')).toBe(true)
unmount()
expect(scopeOpen('open:knowledge.sources-tab')).toBeNull()
})
})
22 changes: 22 additions & 0 deletions website/src/uiLocations/areas/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,28 @@ export const LOCATIONS = {
},
placements: [{ surface: 'capabilities', parent: 'tab.capabilities.skills', entry: 'toolbar' }],
},
// Customize > Knowledge opens on its List view (local state, not the URL), so
// Add Source hangs under the Sources view tab: the path names the click that
// reveals it.
'knowledge.sources-tab': {
kind: 'tab',
terms: {
en: ['knowledge sources', 'my knowledge folders', 'indexed folders', 'watched folders'],
'zh-CN': ['知识来源', '知识库文件夹', '已索引的文件夹'],
},
placements: [{ surface: 'capabilities', parent: 'tab.capabilities.knowledge', entry: 'tab' }],
},
'knowledge.add-source': {
kind: 'button',
terms: {
en: [
'add a folder to the knowledge base', 'add files to knowledge', 'upload a document to knowledge',
'index a folder', 'watch a folder', 'add knowledge', 'add to knowledge base',
],
'zh-CN': ['添加文件夹到知识库', '上传文档到知识库', '索引文件夹', '添加知识'],
},
placements: [{ surface: 'capabilities', parent: 'knowledge.sources-tab', entry: 'toolbar' }],
},
'skills.refresh': {
kind: 'button',
label: { from: 'attr', attr: 'aria-label' },
Expand Down
48 changes: 47 additions & 1 deletion website/src/uiLocations/guidePlans.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type { UiGuidePlan } from './types'

/** The `build_digest` of the packaged index this bundle was built with (see `guideBuildDigest`). */
export const GUIDE_BUILD_DIGEST = "sha256:380172f6b61dbdcd6235a0cc02058ca796dfd7a9b7e6b18f0eba078db304d001"
export const GUIDE_BUILD_DIGEST = "sha256:3df4fd5d7d70839df25d85a10d8f523597899acf91c0129ba04578415f575f4e"

/** Curated location ids: the only ids a live observation may name. */
export const GUIDE_OBSERVABLE_IDS: readonly string[] = [
Expand Down Expand Up @@ -69,6 +69,8 @@ export const GUIDE_OBSERVABLE_IDS: readonly string[] = [
"composer.stop",
"composer.voice",
"connections.mcp-servers-tab",
"knowledge.add-source",
"knowledge.sources-tab",
"mcp.add-custom",
"mcp.add-server",
"mcp.probe",
Expand Down Expand Up @@ -265,6 +267,10 @@ export const GUIDE_REVEAL_SCOPES = {
"kind": "open",
"revealer": "connections.mcp-servers-tab"
},
"open:knowledge.sources-tab": {
"kind": "open",
"revealer": "knowledge.sources-tab"
},
"shell.nav-rail": {
"kind": "state",
"revealer": "shell.nav-toggle",
Expand Down Expand Up @@ -1699,6 +1705,46 @@ export const GUIDE_PLANS: Readonly<Record<string, UiGuidePlan>> = {
}
]
},
"knowledge.add-source": {
"version": 2,
"label_key": "pages.knowledge.sourcesList.add_source",
"placements": [
{
"id": "any",
"route": "/capabilities?tab=knowledge",
"steps": [
{
"id": "any:knowledge.sources-tab",
"location": "knowledge.sources-tab",
"label_key": "pages.knowledge.index.sources_tab",
"scope": "open:knowledge.sources-tab"
},
{
"id": "any:knowledge.add-source",
"location": "knowledge.add-source",
"label_key": "pages.knowledge.sourcesList.add_source"
}
]
}
]
},
"knowledge.sources-tab": {
"version": 2,
"label_key": "pages.knowledge.index.sources_tab",
"placements": [
{
"id": "any",
"route": "/capabilities?tab=knowledge",
"steps": [
{
"id": "any:knowledge.sources-tab",
"location": "knowledge.sources-tab",
"label_key": "pages.knowledge.index.sources_tab"
}
]
}
]
},
"mcp.add-server": {
"version": 2,
"label_key": "pages.overview.mcpTab.add_server",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/**
* Goldens for the wave-3 additions to the `schedule`, `artifacts` and
* `capabilities` areas (Schedule's job controls, the Artifacts toolbar extras,
* Customize > Skills and the MCP probe): the exact parent chain, entry, route,
* Customize > Skills, the MCP probe and Customize > Knowledge's Sources view
* and Add Source): the exact parent chain, entry, route,
* label key and requirements each location carries in the generated index, and
* the one render site its marker sits on, in the committed index.
*/
Expand All @@ -22,6 +23,8 @@ const SCHEDULE_SRC = src('../pages/SchedulePage.tsx')
const ARTIFACTS_SRC = readPageFamily('ArtifactsPage')
const SKILLS_SRC = src('../pages/overview/SkillsTab.tsx')
const MCP_SRC = src('../pages/overview/McpTab.tsx')
const KNOWLEDGE_SRC = src('../pages/knowledge/index.tsx')
const SOURCES_SRC = src('../pages/knowledge/SourcesList.tsx')

const cond = (...ids: string[]): Req[] => ids.map(id => ({ kind: 'condition', id }))

Expand Down Expand Up @@ -85,6 +88,22 @@ const EXPECTED: Record<string, Expect> = {
'skills.create': { kind: 'button', labelKey: 'pages.overview.skillsTab.create_new_skill', source: SKILLS_SRC, placement: skills },
'skills.add': { kind: 'button', labelKey: 'pages.overview.skillsTab.add_skill', source: SKILLS_SRC, placement: skills },
'skills.refresh': { kind: 'button', labelKey: 'pages.overview.skillsTab.refresh_skills', source: SKILLS_SRC, placement: skills },
// Customize > Knowledge opens on its List view, so Add Source hangs under
// the Sources view tab that reveals it.
'knowledge.sources-tab': {
kind: 'tab', labelKey: 'pages.knowledge.index.sources_tab', source: KNOWLEDGE_SRC,
placement: {
surface: 'capabilities', route: '/capabilities?tab=knowledge',
parents: ['page.capabilities', 'tab.capabilities.knowledge'], entry: 'tab', requires: [],
},
},
'knowledge.add-source': {
kind: 'button', labelKey: 'pages.knowledge.sourcesList.add_source', source: SOURCES_SRC,
placement: {
surface: 'capabilities', route: '/capabilities?tab=knowledge',
parents: ['page.capabilities', 'tab.capabilities.knowledge', 'knowledge.sources-tab'], entry: 'toolbar', requires: [],
},
},
'mcp.probe': {
kind: 'button', labelKey: 'pages.overview.mcpTab.probe_mcp_servers', source: MCP_SRC,
placement: {
Expand Down
Loading