Merge remote-tracking branch 'origin/master' into dshw/pr-2423

# Conflicts:
#	packages/client/ui-sidebar/src/client/SidebarRoot.tsx
This commit is contained in:
_Kerman
2026-08-13 04:32:57 +08:00
3517 changed files with 51843 additions and 30957 deletions

View File

@@ -3,6 +3,7 @@
exports[`sidebar shell snapshots > renders the collapsed rail after the crossfade settles, in place 1`] = `
<div
data-slot="sidebar"
style="display: contents;"
>
<div
class="root collapsed railIn quietBars"
@@ -52,10 +53,32 @@ exports[`sidebar shell snapshots > renders the collapsed rail after the crossfad
</button>
<div
class="regionArea"
/>
>
<div
data-slot="sidebar.workspaces"
style="display: contents;"
/>
</div>
<div
class="footArea"
/>
>
<div
class="footerActions"
>
<div
data-slot="sidebar.footer.action"
style="display: contents;"
/>
</div>
<div
class="settingsArea"
>
<div
data-slot="sidebar.settings"
style="display: contents;"
/>
</div>
</div>
</div>
</div>
`;
@@ -63,6 +86,7 @@ exports[`sidebar shell snapshots > renders the collapsed rail after the crossfad
exports[`sidebar shell snapshots > renders the expanded column (wordmark, capsule, empty holes) 1`] = `
<div
data-slot="sidebar"
style="display: contents;"
>
<div
class="root quietBars"
@@ -122,10 +146,32 @@ exports[`sidebar shell snapshots > renders the expanded column (wordmark, capsul
</button>
<div
class="regionArea"
/>
>
<div
data-slot="sidebar.workspaces"
style="display: contents;"
/>
</div>
<div
class="footArea"
/>
>
<div
class="footerActions"
>
<div
data-slot="sidebar.footer.action"
style="display: contents;"
/>
</div>
<div
class="settingsArea"
>
<div
data-slot="sidebar.settings"
style="display: contents;"
/>
</div>
</div>
</div>
</div>
`;
@@ -133,6 +179,7 @@ exports[`sidebar shell snapshots > renders the expanded column (wordmark, capsul
exports[`sidebar shell snapshots > renders the expanded column in the default locale (zh, no setLocale) 1`] = `
<div
data-slot="sidebar"
style="display: contents;"
>
<div
class="root quietBars"
@@ -192,10 +239,32 @@ exports[`sidebar shell snapshots > renders the expanded column in the default lo
</button>
<div
class="regionArea"
/>
>
<div
data-slot="sidebar.workspaces"
style="display: contents;"
/>
</div>
<div
class="footArea"
/>
>
<div
class="footerActions"
>
<div
data-slot="sidebar.footer.action"
style="display: contents;"
/>
</div>
<div
class="settingsArea"
>
<div
data-slot="sidebar.settings"
style="display: contents;"
/>
</div>
</div>
</div>
</div>
`;

View File

@@ -1,22 +1,22 @@
/** Sidebar shell slot registration and its plain runtime/layout callbacks. */
import { Context } from '@deepseek-ai/cordis'
import { describe, expect, it, vi } from 'vitest'
import { SlotsService } from '@deepseek-ai/dsh-client-runtime/client'
import { LocaleService } from '@deepseek-ai/dsh-client-locale/client'
import { SlotRegistry } from '@deepseek-ai/dsh-client-runtime/client'
import { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'
import { apply, inject } from '@deepseek-ai/dsh-client-ui-sidebar/client'
import type { SidebarRootInjected } from '@deepseek-ai/dsh-client-ui-sidebar/client'
async function bench(declare = true) {
const ctx = new Context()
await ctx.plugin(SlotsService).await()
await ctx.plugin(SlotRegistry).await()
const layout = { toggleSidebar: vi.fn() }
const workspaces = { startSession: vi.fn() }
const sessions = { open: vi.fn(), clear: vi.fn() }
ctx.provide('layout', layout)
ctx.provide('sessions', sessions as never)
ctx.provide('workspaces', workspaces as never)
ctx.provide('locale', new LocaleService(ctx))
const slots = ctx.get('slots') as SlotsService
ctx.provide('locale', new LocaleRuntime(ctx))
const slots = ctx.get('slots') as SlotRegistry
if (declare) {
slots.register(
{ name: 'root', children: { 'sidebar': { kind: 'single', scope: 'root' } } } as never,
@@ -31,11 +31,13 @@ describe('ui-sidebar apply', () => {
expect(inject).toEqual(['slots', 'layout', 'sessions', 'workspaces', 'locale'])
})
it('registers the shell and declares the browsing-region hole', async () => {
it('registers the shell and declares its child seats', async () => {
const b = await bench()
await b.ctx.plugin({ inject: [...inject], apply }).await()
expect(b.slots.entries('sidebar')).toHaveLength(1)
expect(b.slots.spec('sidebar.workspaces')).toEqual({ kind: 'single', scope: 'root' })
expect(b.slots.spec('sidebar.settings')).toEqual({ kind: 'single', scope: 'root' })
expect(b.slots.spec('sidebar.footer.action')).toEqual({ kind: 'list', scope: 'root' })
// Copy rides the standard locale seat, not the inject face.
expect(b.slots.entries('sidebar')[0]!.locale).toBe('sidebar')
const injected = (b.slots.entries('sidebar')[0]!.inject as () => SidebarRootInjected)()
@@ -61,5 +63,6 @@ describe('ui-sidebar apply', () => {
await fiber.dispose()
expect(b.slots.entries('sidebar')).toHaveLength(0)
expect(b.slots.spec('sidebar.workspaces')).toBeUndefined()
expect(b.slots.spec('sidebar.footer.action')).toBeUndefined()
})
})

View File

@@ -1,12 +1,12 @@
import { describe, expect, it } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import * as SidebarInvariant from '@deepseek-ai/dsh-client-ui-sidebar/invariant'
import InvariantService from '@deepseek-ai/dsh-invariants'
import InvariantRegistry from '@deepseek-ai/dsh-invariants'
describe('invariant companion', () => {
it('registers under the package name with an empty installer', async () => {
const ctx = new Context()
await ctx.plugin(InvariantService, { enabled: true })
await ctx.plugin(InvariantRegistry, { enabled: true })
await expect(ctx.plugin(SidebarInvariant).await()).resolves.toBeDefined()
})

View File

@@ -1,7 +1,10 @@
// @vitest-environment jsdom
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
import type { SidebarRootComponentProps, SidebarSectionOwnerProps, SidebarSettingsOwnerProps } from '../src/client/contract/slots.ts'
import type {
SidebarFooterActionOwnerProps, SidebarRootComponentProps, SidebarSectionOwnerProps,
SidebarSettingsOwnerProps,
} from '../src/client/contract/slots.ts'
import { SidebarRoot } from '../src/client/SidebarRoot.tsx'
import { en } from '../src/client/locales.ts'
@@ -23,17 +26,25 @@ function mountShell({ collapsed = false, width = 300 }: { collapsed?: boolean; w
const toggleSidebar = vi.fn()
let regionOwner: SidebarSectionOwnerProps | undefined
let settingsOwner: SidebarSettingsOwnerProps | undefined
let footerActionOwner: SidebarFooterActionOwnerProps | undefined
let current = { collapsed, width }
const root = () => (
<SidebarRoot
collapsed={current.collapsed} width={current.width}
useSessions={neverHook} useWorkspaces={neverHook}
startSession={startSession} toggleSidebar={toggleSidebar} t={t}
renderSlot={((key: string, owner: SidebarSectionOwnerProps | SidebarSettingsOwnerProps) => {
renderSlot={((
key: string,
owner: SidebarFooterActionOwnerProps | SidebarSectionOwnerProps | SidebarSettingsOwnerProps,
) => {
if (key === 'sidebar.settings') {
settingsOwner = owner
return <div data-testid="settings-seat" data-wide={owner.wide} />
}
if (key === 'sidebar.footer.action') {
footerActionOwner = owner
return <div data-testid="footer-action-seat" data-wide={owner.wide} />
}
regionOwner = owner as SidebarSectionOwnerProps
return <div data-testid="region" data-wide={owner.wide} />
}) as SidebarRootComponentProps['renderSlot']}
@@ -51,6 +62,10 @@ function mountShell({ collapsed = false, width = 300 }: { collapsed?: boolean; w
if (settingsOwner === undefined) throw new Error('settings owner not rendered')
return settingsOwner
},
footerActionOwner: () => {
if (footerActionOwner === undefined) throw new Error('footer action owner not rendered')
return footerActionOwner
},
rerender(next: Partial<typeof current>) {
current = { ...current, ...next }
view.rerender(root())
@@ -75,6 +90,7 @@ describe('SidebarRoot shell', () => {
expect(b.regionOwner().wide).toBe(true)
// The settings seat rides the same wide flag (ui-settings renders the row).
expect(b.settingsOwner().wide).toBe(true)
expect(b.footerActionOwner().wide).toBe(true)
// Expanded: the request is a no-op (no accidental collapse).
b.regionOwner().expandSidebar()
expect(b.toggleSidebar).not.toHaveBeenCalled()
@@ -89,6 +105,7 @@ describe('SidebarRoot shell', () => {
vi.advanceTimersByTime(200)
b.rerender({})
expect(b.regionOwner().wide).toBe(false)
expect(b.footerActionOwner().wide).toBe(false)
expect(screen.getByTestId('region')).toBeTruthy()
b.regionOwner().expandSidebar()
expect(b.toggleSidebar).toHaveBeenCalledOnce()

View File

@@ -11,7 +11,7 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { act, cleanup, waitFor } from '@testing-library/react'
import { SlotTestRuntime, usePinnedBrowserLanguages } from '@deepseek-ai/dsh-client-test-runtime'
import { LocaleService } from '@deepseek-ai/dsh-client-locale/client'
import { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'
import { apply, inject } from '@deepseek-ai/dsh-client-ui-sidebar/client'
// The service reads its initial locale from the browser; these specs assert
@@ -29,7 +29,7 @@ afterEach(cleanup)
async function bench(options: { locale?: 'en' } = {}) {
const runtime = await SlotTestRuntime.create()
runtime.provide('layout', { toggleSidebar: vi.fn() })
const locale = new LocaleService(runtime.ctx)
const locale = new LocaleRuntime(runtime.ctx)
if (options.locale === 'en') locale.setLocale('en')
runtime.provide('locale', locale)
runtime.slots.installLocale(locale)