ConusAI vendor 81941888f

This commit is contained in:
ConusAI vendor 81941888f
2026-10-07 08:47:51 +00:00
commit c3c6526952
135 changed files with 13010 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
export { ModulePanel } from './ui/ModulePanel'
export { moduleView, type ModuleName } from './model/view'
@@ -0,0 +1,58 @@
import { isValidElement, type ReactElement } from 'react'
import { describe, expect, it, vi } from 'vitest'
import { ExportNotesButton } from '@/features/export-notes'
import { UploadsLeft } from '@/features/rate-limit'
import { SearchNotes } from '@/features/search-notes'
import { panelSlots } from '@/widgets/module-panel/model/slots'
import type { ModuleName } from '@/widgets/module-panel/model/view'
// The features' actions reach the shared API barrel; the slot mapping calls none of it.
vi.mock('@/shared/api', () => ({}))
const none: Record<ModuleName, boolean> = { kv: false, nats: false, qdrant: false, git: false, s3: false }
const enable = (service: string) => <i data-enable={service} />
function slotFor(name: ModuleName, linked: Record<ModuleName, boolean>) {
const slot = panelSlots({ linked, query: 'budget', gitHost: 'git.example.test', enable }).find((s) => s.name === name)
if (!slot) throw new Error(`no slot for ${name}`)
return slot.content
}
describe('panelSlots', () => {
it.each([
['kv', 'redis'],
['nats', 'nats'],
['qdrant', 'qdrant'],
['git', 'git'],
['s3', 's3'],
] as const)('renders the enable card for %s, naming %s, when its variables are absent', (name, service) => {
const content = slotFor(name, none) as ReactElement<{ 'data-enable': string }>
expect(isValidElement(content)).toBe(true)
expect(content.props['data-enable']).toBe(service)
})
it('renders the uploads counter for kv when linked', () => {
expect((slotFor('kv', { ...none, kv: true }) as ReactElement).type).toBe(UploadsLeft)
})
it('renders the similarity search for qdrant when linked, with the query', () => {
const content = slotFor('qdrant', { ...none, qdrant: true }) as ReactElement<{ query: string }>
expect(content.type).toBe(SearchNotes)
expect(content.props.query).toBe('budget')
})
it('renders the export button for s3 when linked', () => {
expect((slotFor('s3', { ...none, s3: true }) as ReactElement).type).toBe(ExportNotesButton)
})
it('names the git host the packages install from when git is linked', () => {
const content = slotFor('git', { ...none, git: true }) as ReactElement<{ children: unknown[] }>
expect(content.type).toBe('p')
expect(content.props.children).toContain('git.example.test')
})
it('renders no feature for a module whose variables are absent', () => {
const content = slotFor('kv', none) as ReactElement
expect(content.type).not.toBe(UploadsLeft)
})
})
+40
View File
@@ -0,0 +1,40 @@
import type { ReactNode } from 'react'
import { ExportNotesButton } from '@/features/export-notes'
import { UploadsLeft } from '@/features/rate-limit'
import { SearchNotes } from '@/features/search-notes'
import { moduleView, type ModuleName } from '@/widgets/module-panel/model/view'
/** The service an optional module is linked through, as the platform's catalog names it. */
export type ModuleService = 'redis' | 'nats' | 'qdrant' | 'git' | 's3'
export type ModuleSlot = { name: ModuleName; service: ModuleService; content: ReactNode }
/**
* The panel's slots, one per optional module (D13): the module's feature when its
* variables are present, the enable card the view supplies when they are not. A pure
* function, so a test can assert what each slot renders without a server.
*/
export function panelSlots({
linked,
query,
gitHost,
enable,
}: {
linked: Record<ModuleName, boolean>
query: string | undefined
gitHost: string
enable: (service: ModuleService) => ReactNode
}): ModuleSlot[] {
const features: Array<{ name: ModuleName; service: ModuleService; feature: ReactNode }> = [
{ name: 'kv', service: 'redis', feature: <UploadsLeft /> },
{ name: 'nats', service: 'nats', feature: <p>New notes are announced live on NATS.</p> },
{ name: 'qdrant', service: 'qdrant', feature: <SearchNotes query={query} /> },
{ name: 's3', service: 's3', feature: <ExportNotesButton /> },
{ name: 'git', service: 'git', feature: <p>Packages install from {gitHost}.</p> },
]
return features.map((slot) => ({
name: slot.name,
service: slot.service,
content: moduleView(slot.name, linked) === 'feature' ? slot.feature : enable(slot.service),
}))
}
@@ -0,0 +1,15 @@
import { describe, expect, it } from 'vitest'
import { moduleView, type ModuleName } from '@/widgets/module-panel/model/view'
const names: ModuleName[] = ['kv', 'nats', 'qdrant', 'git', 's3']
const none = { kv: false, nats: false, qdrant: false, git: false, s3: false }
describe('moduleView', () => {
it.each(names)('shows the enable card for %s when its variables are absent', (name) => {
expect(moduleView(name, none)).toBe('enable')
})
it.each(names)('shows the feature for %s when its variables are present', (name) => {
expect(moduleView(name, { ...none, [name]: true })).toBe('feature')
})
})
+9
View File
@@ -0,0 +1,9 @@
/** The optional modules the panel shows (plan D13), by their env-derived names. */
export type ModuleName = 'kv' | 'nats' | 'qdrant' | 'git' | 's3'
/** What a module's slot shows: its feature when linked, its enable card when not. */
export type ModuleView = 'feature' | 'enable'
export function moduleView(name: ModuleName, linked: Record<ModuleName, boolean>): ModuleView {
return linked[name] ? 'feature' : 'enable'
}
@@ -0,0 +1,31 @@
import type { ReactNode } from 'react'
import { getEnv, modules } from '@/shared/config'
import { panelSlots, type ModuleService } from '@/widgets/module-panel/model/slots'
/**
* One slot per optional module (D13): the module's feature when its variables are
* present, and the platform-described enable card when they are not.
*/
export function ModulePanel({
query,
enable,
}: {
query: string | undefined
/** The enable card for a service, supplied by the view that composes this panel. */
enable: (service: ModuleService) => ReactNode
}) {
const env = getEnv()
const slots = panelSlots({
linked: modules(env),
query,
gitHost: env.GIT_URL ? new URL(env.GIT_URL).host : '',
enable,
})
return (
<section>
{slots.map((slot) => (
<div key={slot.name}>{slot.content}</div>
))}
</section>
)
}